| Start/ | End/ | |||
| True | False | - | Line | Source |
| 1 | /* | |||
| 2 | * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles | |||
| 3 | * | |||
| 4 | * Main part | |||
| 5 | * | |||
| 6 | * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria | |||
| 7 | * | |||
| 8 | * If distributed as part of the Linux kernel, this code is licensed under the | |||
| 9 | * terms of the GPL v2. | |||
| 10 | * | |||
| 11 | * Otherwise, the following license terms apply: | |||
| 12 | * | |||
| 13 | * * Redistribution and use in source and binary forms, with or without | |||
| 14 | * * modification, are permitted provided that the following conditions | |||
| 15 | * * are met: | |||
| 16 | * * 1) Redistributions of source code must retain the above copyright | |||
| 17 | * * notice, this list of conditions and the following disclaimer. | |||
| 18 | * * 2) Redistributions in binary form must reproduce the above copyright | |||
| 19 | * * notice, this list of conditions and the following disclaimer in the | |||
| 20 | * * documentation and/or other materials provided with the distribution. | |||
| 21 | * * 3) The name of the author may not be used to endorse or promote products | |||
| 22 | * * derived from this software without specific psisusbr written permission. | |||
| 23 | * * | |||
| 24 | * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR | |||
| 25 | * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES | |||
| 26 | * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. | |||
| 27 | * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, | |||
| 28 | * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT | |||
| 29 | * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||
| 30 | * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||
| 31 | * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||
| 32 | * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF | |||
| 33 | * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||
| 34 | * | |||
| 35 | * Author: Thomas Winischhofer <thomas@winischhofer.net> | |||
| 36 | * | |||
| 37 | */ | |||
| 38 | ||||
| 39 | #include <linux/config.h> | |||
| 40 | #include <linux/module.h> | |||
| 41 | #include <linux/kernel.h> | |||
| 42 | #include <linux/signal.h> | |||
| 43 | #include <linux/sched.h> | |||
| 44 | #include <linux/errno.h> | |||
| 45 | #include <linux/poll.h> | |||
| 46 | #include <linux/init.h> | |||
| 47 | #include <linux/slab.h> | |||
| 48 | #include <linux/spinlock.h> | |||
| 49 | #include <linux/kref.h> | |||
| 50 | #include <linux/usb.h> | |||
| 51 | #include <linux/smp_lock.h> | |||
| 52 | #include <linux/vmalloc.h> | |||
| 53 | ||||
| 54 | #include "sisusb.h" | |||
| 55 | ||||
| 56 | #ifdef INCL_SISUSB_CON | |||
| 57 | #include <linux/font.h> | |||
| 58 | #endif | |||
| 59 | ||||
| 60 | #define SISUSB_DONTSYNC | |||
| 61 | ||||
| 62 | /* Forward declarations / clean-up routines */ | |||
| 63 | ||||
| 64 | #ifdef INCL_SISUSB_CON | |||
| 65 | int sisusb_setreg(struct sisusb_usb_data *sisusb, int port, u8 data); | |||
| 66 | int sisusb_getreg(struct sisusb_usb_data *sisusb, int port, u8 *data); | |||
| 67 | int sisusb_setidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 data); | |||
| 68 | int sisusb_getidxreg(struct sisusb_usb_data *sisusb, int port, u8 index, u8 *data); | |||
| 69 | int sisusb_setidxregandor(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand, u8 myor); | |||
| 70 | int sisusb_setidxregor(struct sisusb_usb_data *sisusb, int port, u8 index, u8 myor); | |||
| 71 | int sisusb_setidxregand(struct sisusb_usb_data *sisusb, int port, u8 idx, u8 myand); | |||
| 72 | ||||
| 73 | int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data); | |||
| 74 | int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data); | |||
| 75 | int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data); | |||
| 76 | int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data); | |||
| 77 | int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src, | |||
| 78 | u32 dest, int length, size_t *bytes_written); | |||
| 79 | ||||
| 80 | int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init); | |||
| 81 | ||||
| 82 | extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo); | |||
| 83 | extern int SiSUSBSetVESAMode(struct SiS_Private *SiS_Pr, unsigned short VModeNo); | |||
| 84 | ||||
| 85 | extern void sisusb_init_concode(void); | |||
| 86 | extern int sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last); | |||
| 87 | extern void sisusb_console_exit(struct sisusb_usb_data *sisusb); | |||
| 88 | ||||
| 89 | extern void sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location); | |||
| 90 | ||||
| 91 | extern int sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, | |||
| 92 | u8 *arg, int cmapsz, int ch512, int dorecalc, | |||
| 93 | struct vc_data *c, int fh, int uplock); | |||
| 94 | ||||
| 95 | static int sisusb_first_vc = 0; | |||
| 96 | static int sisusb_last_vc = 0; | |||
| 97 | module_param_named(first, sisusb_first_vc, int, 0); | |||
| 98 | module_param_named(last, sisusb_last_vc, int, 0); | |||
| 99 | MODULE_PARM_DESC(first, "Number of first console to take over (1 - MAX_NR_CONSOLES)"); | |||
| 100 | MODULE_PARM_DESC(last, "Number of last console to take over (1 - MAX_NR_CONSOLES)"); | |||
| 101 | #endif | |||
| 102 | ||||
| 103 | static struct usb_driver sisusb_driver; | |||
| 104 | ||||
| 105 | DECLARE_MUTEX(disconnect_sem); | |||
| 106 | ||||
| 107 | static void | |||
| 0 | 0 | - | 108 | sisusb_free_buffers(struct sisusb_usb_data *sisusb) |
| 109 | { | |||
| 110 | int i; | |||
| 111 | ||||
| 0 | 0 | - | 112 | for (i = 0; i < NUMOBUFS; i++) { |
| 0 | 0 | - | 113 | if (sisusb->obuf[i]) { |
| 114 | usb_buffer_free(sisusb->sisusb_dev, sisusb->obufsize, | |||
| 115 | sisusb->obuf[i], sisusb->transfer_dma_out[i]); | |||
| 116 | sisusb->obuf[i] = NULL; | |||
| 117 | } | |||
| 118 | } | |||
| 0 | 0 | - | 119 | if (sisusb->ibuf) { |
| 120 | usb_buffer_free(sisusb->sisusb_dev, sisusb->ibufsize, | |||
| 121 | sisusb->ibuf, sisusb->transfer_dma_in); | |||
| 122 | sisusb->ibuf = NULL; | |||
| 123 | } | |||
| 124 | } | |||
| 125 | ||||
| 126 | static void | |||
| 0 | 0 | - | 127 | sisusb_free_urbs(struct sisusb_usb_data *sisusb) |
| 128 | { | |||
| 129 | int i; | |||
| 130 | ||||
| 0 | 0 | - | 131 | for (i = 0; i < NUMOBUFS; i++) { |
| 132 | usb_free_urb(sisusb->sisurbout[i]); | |||
| 133 | sisusb->sisurbout[i] = NULL; | |||
| 134 | } | |||
| 135 | usb_free_urb(sisusb->sisurbin); | |||
| 136 | sisusb->sisurbin = NULL; | |||
| 137 | } | |||
| 138 | ||||
| 139 | /* Level 0: USB transport layer */ | |||
| 140 | ||||
| 141 | /* 1. out-bulks */ | |||
| 142 | ||||
| 143 | /* out-urb management */ | |||
| 144 | ||||
| 145 | /* Return 1 if all free, 0 otherwise */ | |||
| 146 | static int | |||
| 0 | 0 | - | 147 | sisusb_all_free(struct sisusb_usb_data *sisusb) |
| 148 | { | |||
| 149 | int i; | |||
| 150 | ||||
| 0 | 0 | - | 151 | for (i = 0; i < sisusb->numobufs; i++) { |
| 152 | ||||
| 0 | 0 | - | 153 | if (sisusb->urbstatus[i] & SU_URB_BUSY) |
| 0 | - | 154 | return 0; | |
| 155 | ||||
| 156 | } | |||
| 157 | ||||
| 0 | - | 158 | return 1; | |
| 159 | } | |||
| 160 | ||||
| 161 | /* Kill all busy URBs */ | |||
| 162 | static void | |||
| 0 | 0 | - | 163 | sisusb_kill_all_busy(struct sisusb_usb_data *sisusb) |
| 164 | { | |||
| 165 | int i; | |||
| 166 | ||||
| 0 | 0 | - | 167 | if (sisusb_all_free(sisusb)) |
| 0 | - | 168 | return; | |
| 169 | ||||
| 0 | 0 | - | 170 | for (i = 0; i < sisusb->numobufs; i++) { |
| 171 | ||||
| 0 | 0 | - | 172 | if (sisusb->urbstatus[i] & SU_URB_BUSY) |
| 173 | usb_kill_urb(sisusb->sisurbout[i]); | |||
| 174 | ||||
| 175 | } | |||
| 176 | } | |||
| 177 | ||||
| 178 | /* Return 1 if ok, 0 if error (not all complete within timeout) */ | |||
| 179 | static int | |||
| 0 | 0 | - | 180 | sisusb_wait_all_out_complete(struct sisusb_usb_data *sisusb) |
| 181 | { | |||
| 182 | int timeout = 5 * HZ, i = 1; | |||
| 183 | ||||
| 184 | wait_event_timeout(sisusb->wait_q, | |||
| 185 | (i = sisusb_all_free(sisusb)), | |||
| 186 | timeout); | |||
| 187 | ||||
| 0 | - | 188 | return i; | |
| 189 | } | |||
| 190 | ||||
| 191 | static int | |||
| 0 | 0 | - | 192 | sisusb_outurb_available(struct sisusb_usb_data *sisusb) |
| 193 | { | |||
| 194 | int i; | |||
| 195 | ||||
| 0 | 0 | - | 196 | for (i = 0; i < sisusb->numobufs; i++) { |
| 197 | ||||
| 0 | 0 | - | 198 | if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0) |
| 0 | - | 199 | return i; | |
| 200 | ||||
| 201 | } | |||
| 202 | ||||
| 0 | - | 203 | return -1; | |
| 204 | } | |||
| 205 | ||||
| 206 | static int | |||
| 0 | 0 | - | 207 | sisusb_get_free_outbuf(struct sisusb_usb_data *sisusb) |
| 208 | { | |||
| 209 | int i, timeout = 5 * HZ; | |||
| 210 | ||||
| 211 | wait_event_timeout(sisusb->wait_q, | |||
| 212 | ((i = sisusb_outurb_available(sisusb)) >= 0), | |||
| 213 | timeout); | |||
| 214 | ||||
| 0 | - | 215 | return i; | |
| 216 | } | |||
| 217 | ||||
| 218 | static int | |||
| 0 | 0 | - | 219 | sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb) |
| 220 | { | |||
| 221 | int i; | |||
| 222 | ||||
| 223 | i = sisusb_outurb_available(sisusb); | |||
| 224 | ||||
| 0 | 0 | - | 225 | if (i >= 0) |
| 226 | sisusb->urbstatus[i] |= SU_URB_ALLOC; | |||
| 227 | ||||
| 0 | - | 228 | return i; | |
| 229 | } | |||
| 230 | ||||
| 231 | static void | |||
| 0 | 0 | - | 232 | sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index) |
| 233 | { | |||
| 0 | 0 | - | 234 | if ((index >= 0) && (index < sisusb->numobufs)) |
| 0 | - | 234 | (T) && (T) | |
| 0 | - | 234 | (T) && (F) | |
| 0 | - | 234 | (F) && (_) | |
| 235 | sisusb->urbstatus[index] &= ~SU_URB_ALLOC; | |||
| 236 | } | |||
| 237 | ||||
| 238 | /* completion callback */ | |||
| 239 | ||||
| 240 | static void | |||
| 0 | 0 | - | 241 | sisusb_bulk_completeout(struct urb *urb, struct pt_regs *regs) |
| 242 | { | |||
| 243 | struct sisusb_urb_context *context = urb->context; | |||
| 244 | struct sisusb_usb_data *sisusb; | |||
| 245 | ||||
| 0 | 0 | - | 246 | if (!context) |
| 0 | - | 247 | return; | |
| 248 | ||||
| 249 | sisusb = context->sisusb; | |||
| 250 | ||||
| 0 | 0 | - | 251 | if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) |
| 0 | - | 251 | T || _ || _ | |
| 0 | - | 251 | F || T || _ | |
| 0 | - | 251 | F || F || T | |
| 0 | - | 251 | F || F || F | |
| 0 | - | 252 | return; | |
| 253 | ||||
| 254 | #ifndef SISUSB_DONTSYNC | |||
| 255 | if (context->actual_length) | |||
| 256 | *(context->actual_length) += urb->actual_length; | |||
| 257 | #endif | |||
| 258 | ||||
| 259 | sisusb->urbstatus[context->urbindex] &= ~SU_URB_BUSY; | |||
| 260 | wake_up(&sisusb->wait_q); | |||
| 261 | } | |||
| 262 | ||||
| 263 | static int | |||
| 0 | 0 | - | 264 | sisusb_bulkout_msg(struct sisusb_usb_data *sisusb, int index, unsigned int pipe, void *data, |
| 265 | int len, int *actual_length, int timeout, unsigned int tflags, | |||
| 266 | dma_addr_t transfer_dma) | |||
| 267 | { | |||
| 268 | struct urb *urb = sisusb->sisurbout[index]; | |||
| 269 | int retval, byteswritten = 0; | |||
| 270 | ||||
| 271 | /* Set up URB */ | |||
| 272 | urb->transfer_flags = 0; | |||
| 273 | ||||
| 274 | usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, | |||
| 275 | sisusb_bulk_completeout, &sisusb->urbout_context[index]); | |||
| 276 | ||||
| 277 | urb->transfer_flags |= tflags; | |||
| 278 | urb->actual_length = 0; | |||
| 279 | ||||
| 0 | 0 | - | 280 | if ((urb->transfer_dma = transfer_dma)) |
| 281 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |||
| 282 | ||||
| 283 | /* Set up context */ | |||
| 284 | sisusb->urbout_context[index].actual_length = (timeout) ? | |||
| 0 | 0 | - | 284 | ternary-?: ( timeout ) |
| 285 | NULL : actual_length; | |||
| 286 | ||||
| 287 | /* Declare this urb/buffer in use */ | |||
| 288 | sisusb->urbstatus[index] |= SU_URB_BUSY; | |||
| 289 | ||||
| 290 | /* Submit URB */ | |||
| 291 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |||
| 292 | ||||
| 293 | /* If OK, and if timeout > 0, wait for completion */ | |||
| 0 | 0 | - | 294 | if ((retval == 0) && timeout) { |
| 0 | - | 294 | (T) && T | |
| 0 | - | 294 | (T) && F | |
| 0 | - | 294 | (F) && _ | |
| 295 | wait_event_timeout(sisusb->wait_q, | |||
| 296 | (!(sisusb->urbstatus[index] & SU_URB_BUSY)), | |||
| 297 | timeout); | |||
| 0 | 0 | - | 298 | if (sisusb->urbstatus[index] & SU_URB_BUSY) { |
| 299 | /* URB timed out... kill it and report error */ | |||
| 300 | usb_kill_urb(urb); | |||
| 301 | retval = -ETIMEDOUT; | |||
| 302 | } else { | |||
| 303 | /* Otherwise, report urb status */ | |||
| 304 | retval = urb->status; | |||
| 305 | byteswritten = urb->actual_length; | |||
| 306 | } | |||
| 307 | } | |||
| 308 | ||||
| 0 | 0 | - | 309 | if (actual_length) |
| 310 | *actual_length = byteswritten; | |||
| 311 | ||||
| 0 | - | 312 | return retval; | |
| 313 | } | |||
| 314 | ||||
| 315 | /* 2. in-bulks */ | |||
| 316 | ||||
| 317 | /* completion callback */ | |||
| 318 | ||||
| 319 | static void | |||
| 0 | 0 | - | 320 | sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs) |
| 321 | { | |||
| 322 | struct sisusb_usb_data *sisusb = urb->context; | |||
| 323 | ||||
| 0 | 0 | - | 324 | if (!sisusb || !sisusb->sisusb_dev || !sisusb->present) |
| 0 | - | 324 | T || _ || _ | |
| 0 | - | 324 | F || T || _ | |
| 0 | - | 324 | F || F || T | |
| 0 | - | 324 | F || F || F | |
| 0 | - | 325 | return; | |
| 326 | ||||
| 327 | sisusb->completein = 1; | |||
| 328 | wake_up(&sisusb->wait_q); | |||
| 329 | } | |||
| 330 | ||||
| 331 | static int | |||
| 0 | 0 | - | 332 | sisusb_bulkin_msg(struct sisusb_usb_data *sisusb, unsigned int pipe, void *data, int len, |
| 333 | int *actual_length, int timeout, unsigned int tflags, dma_addr_t transfer_dma) | |||
| 334 | { | |||
| 335 | struct urb *urb = sisusb->sisurbin; | |||
| 336 | int retval, readbytes = 0; | |||
| 337 | ||||
| 338 | urb->transfer_flags = 0; | |||
| 339 | ||||
| 340 | usb_fill_bulk_urb(urb, sisusb->sisusb_dev, pipe, data, len, | |||
| 341 | sisusb_bulk_completein, sisusb); | |||
| 342 | ||||
| 343 | urb->transfer_flags |= tflags; | |||
| 344 | urb->actual_length = 0; | |||
| 345 | ||||
| 0 | 0 | - | 346 | if ((urb->transfer_dma = transfer_dma)) |
| 347 | urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP; | |||
| 348 | ||||
| 349 | sisusb->completein = 0; | |||
| 350 | retval = usb_submit_urb(urb, GFP_ATOMIC); | |||
| 0 | 0 | - | 351 | if (retval == 0) { |
| 352 | wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout); | |||
| 0 | 0 | - | 353 | if (!sisusb->completein) { |
| 354 | /* URB timed out... kill it and report error */ | |||
| 355 | usb_kill_urb(urb); | |||
| 356 | retval = -ETIMEDOUT; | |||
| 357 | } else { | |||
| 358 | /* URB completed within timout */ | |||
| 359 | retval = urb->status; | |||
| 360 | readbytes = urb->actual_length; | |||
| 361 | } | |||
| 362 | } | |||
| 363 | ||||
| 0 | 0 | - | 364 | if (actual_length) |
| 365 | *actual_length = readbytes; | |||
| 366 | ||||
| 0 | - | 367 | return retval; | |
| 368 | } | |||
| 369 | ||||
| 370 | ||||
| 371 | /* Level 1: */ | |||
| 372 | ||||
| 373 | /* Send a bulk message of variable size | |||
| 374 | * | |||
| 375 | * To copy the data from userspace, give pointer to "userbuffer", | |||
| 376 | * to copy from (non-DMA) kernel memory, give "kernbuffer". If | |||
| 377 | * both of these are NULL, it is assumed, that the transfer | |||
| 378 | * buffer "sisusb->obuf[index]" is set up with the data to send. | |||
| 379 | * Index is ignored if either kernbuffer or userbuffer is set. | |||
| 380 | * If async is nonzero, URBs will be sent without waiting for | |||
| 381 | * completion of the previous URB. | |||
| 382 | * | |||
| 383 | * (return 0 on success) | |||
| 384 | */ | |||
| 385 | ||||
| 0 | 0 | - | 386 | static int sisusb_send_bulk_msg(struct sisusb_usb_data *sisusb, int ep, int len, |
| 387 | char *kernbuffer, const char __user *userbuffer, int index, | |||
| 388 | ssize_t *bytes_written, unsigned int tflags, int async) | |||
| 389 | { | |||
| 390 | int result = 0, retry, count = len; | |||
| 391 | int passsize, thispass, transferred_len = 0; | |||
| 392 | int fromuser = (userbuffer != NULL) ? 1 : 0; | |||
| 0 | 0 | - | 392 | ternary-?: ( userbuffer != ( ( void * ) 0 ) ) |
| 393 | int fromkern = (kernbuffer != NULL) ? 1 : 0; | |||
| 0 | 0 | - | 393 | ternary-?: ( kernbuffer != ( ( void * ) 0 ) ) |
| 394 | unsigned int pipe; | |||
| 395 | char *buffer; | |||
| 396 | ||||
| 397 | (*bytes_written) = 0; | |||
| 398 | ||||
| 399 | /* Sanity check */ | |||
| 0 | 0 | - | 400 | if (!sisusb || !sisusb->present || !sisusb->sisusb_dev) |
| 0 | - | 400 | T || _ || _ | |
| 0 | - | 400 | F || T || _ | |
| 0 | - | 400 | F || F || T | |
| 0 | - | 400 | F || F || F | |
| 0 | - | 401 | return -ENODEV; | |
| 402 | ||||
| 403 | /* If we copy data from kernel or userspace, force the | |||
| 404 | * allocation of a buffer/urb. If we have the data in | |||
| 405 | * the transfer buffer[index] already, reuse the buffer/URB | |||
| 406 | * if the length is > buffer size. (So, transmitting | |||
| 407 | * large data amounts directly from the transfer buffer | |||
| 408 | * treats the buffer as a ring buffer. However, we need | |||
| 409 | * to sync in this case.) | |||
| 410 | */ | |||
| 0 | 0 | - | 411 | if (fromuser || fromkern) |
| 0 | - | 411 | T || _ | |
| 0 | - | 411 | F || T | |
| 0 | - | 411 | F || F | |
| 412 | index = -1; | |||
| 0 | 0 | - | 413 | else if (len > sisusb->obufsize) |
| 414 | async = 0; | |||
| 415 | ||||
| 416 | pipe = usb_sndbulkpipe(sisusb->sisusb_dev, ep); | |||
| 417 | ||||
| 418 | do { | |||
| 419 | passsize = thispass = (sisusb->obufsize < count) ? | |||
| 0 | 0 | - | 419 | ternary-?: ( sisusb -> obufsize < count ) |
| 420 | sisusb->obufsize : count; | |||
| 421 | ||||
| 0 | 0 | - | 422 | if (index < 0) |
| 423 | index = sisusb_get_free_outbuf(sisusb); | |||
| 424 | ||||
| 0 | 0 | - | 425 | if (index < 0) |
| 0 | - | 426 | return -EIO; | |
| 427 | ||||
| 428 | buffer = sisusb->obuf[index]; | |||
| 429 | ||||
| 0 | 0 | - | 430 | if (fromuser) { |
| 431 | ||||
| 0 | 0 | - | 432 | if (copy_from_user(buffer, userbuffer, passsize)) |
| 0 | - | 433 | return -EFAULT; | |
| 434 | ||||
| 435 | userbuffer += passsize; | |||
| 436 | ||||
| 0 | 0 | - | 437 | } else if (fromkern) { |
| 438 | ||||
| 439 | memcpy(buffer, kernbuffer, passsize); | |||
| 440 | kernbuffer += passsize; | |||
| 441 | ||||
| 442 | } | |||
| 443 | ||||
| 444 | retry = 5; | |||
| 0 | 0 | - | 445 | while (thispass) { |
| 446 | ||||
| 0 | 0 | - | 447 | if (!sisusb->sisusb_dev) |
| 0 | - | 448 | return -ENODEV; | |
| 449 | ||||
| 450 | result = sisusb_bulkout_msg(sisusb, | |||
| 451 | index, | |||
| 452 | pipe, | |||
| 453 | buffer, | |||
| 454 | thispass, | |||
| 455 | &transferred_len, | |||
| 456 | async ? 0 : 5 * HZ, | |||
| 0 | 0 | - | 456 | ternary-?: async |
| 457 | tflags, | |||
| 458 | sisusb->transfer_dma_out[index]); | |||
| 459 | ||||
| 0 | 0 | - | 460 | if (result == -ETIMEDOUT) { |
| 461 | ||||
| 462 | /* Will not happen if async */ | |||
| 0 | 0 | - | 463 | if (!retry--) |
| 0 | - | 464 | return -ETIME; | |
| 465 | ||||
| 0 | - | 466 | continue; | |
| 467 | ||||
| 0 | 0 | - | 468 | } else if ((result == 0) && !async && transferred_len) { |
| 0 | - | 468 | (T) && T && T | |
| 0 | - | 468 | (T) && T && F | |
| 0 | - | 468 | (T) && F && _ | |
| 0 | - | 468 | (F) && _ && _ | |
| 469 | ||||
| 470 | thispass -= transferred_len; | |||
| 0 | 0 | - | 471 | if (thispass) { |
| 0 | 0 | - | 472 | if (sisusb->transfer_dma_out) { |
| 473 | /* If DMA, copy remaining | |||
| 474 | * to beginning of buffer | |||
| 475 | */ | |||
| 476 | memcpy(buffer, | |||
| 477 | buffer + transferred_len, | |||
| 478 | thispass); | |||
| 479 | } else { | |||
| 480 | /* If not DMA, simply increase | |||
| 481 | * the pointer | |||
| 482 | */ | |||
| 483 | buffer += transferred_len; | |||
| 484 | } | |||
| 485 | } | |||
| 486 | ||||
| 487 | } else | |||
| 0 | - | 488 | break; | |
| 489 | }; | |||
| 490 | ||||
| 0 | 0 | - | 491 | if (result) |
| 0 | - | 492 | return result; | |
| 493 | ||||
| 494 | (*bytes_written) += passsize; | |||
| 495 | count -= passsize; | |||
| 496 | ||||
| 497 | /* Force new allocation in next iteration */ | |||
| 0 | 0 | - | 498 | if (fromuser || fromkern) |
| 0 | - | 498 | T || _ | |
| 0 | - | 498 | F || T | |
| 0 | - | 498 | F || F | |
| 499 | index = -1; | |||
| 500 | ||||
| 0 | 0 | - | 501 | } while (count > 0); |
| 502 | ||||
| 0 | 0 | - | 503 | if (async) { |
| 504 | #ifdef SISUSB_DONTSYNC | |||
| 505 | (*bytes_written) = len; | |||
| 506 | /* Some URBs/buffers might be busy */ | |||
| 507 | #else | |||
| 508 | sisusb_wait_all_out_complete(sisusb); | |||
| 509 | (*bytes_written) = transferred_len; | |||
| 510 | & | |||