CTC++ Coverage Report - Execution Profile    #714/1532

Files Summary | Functions Summary | Execution Profile | Index | No Index
First | Previous | Next | Last


File: drivers/usb/misc/sisusbvga/sisusb.c
Instrumentation mode: function-decision-multicondition
TER: 0 % ( 0/953)

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
 
- 108 sisusb_free_buffers(struct sisusb_usb_data *sisusb)
  109 {
  110    int i;
  111 
- 112    for (i = 0; i < NUMOBUFS; i++) {
- 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    }
- 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
 
- 127 sisusb_free_urbs(struct sisusb_usb_data *sisusb)
  128 {
  129    int i;
  130 
- 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
 
- 147 sisusb_all_free(struct sisusb_usb_data *sisusb)
  148 {
  149    int i;
  150 
- 151    for (i = 0; i < sisusb->numobufs; i++) {
  152 
- 153       if (sisusb->urbstatus[i] & SU_URB_BUSY)
 - 154          return 0;
  155 
  156    }
  157 
 - 158    return 1;
  159 }
  160 
  161 /* Kill all busy URBs */
  162 static void
 
- 163 sisusb_kill_all_busy(struct sisusb_usb_data *sisusb)
  164 {
  165    int i;
  166 
- 167    if (sisusb_all_free(sisusb))
 - 168       return;
  169 
- 170    for (i = 0; i < sisusb->numobufs; i++) {
  171 
- 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
 
- 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 
 - 188    return i;
  189 }
  190 
  191 static int
 
- 192 sisusb_outurb_available(struct sisusb_usb_data *sisusb)
  193 {
  194    int i;
  195 
- 196    for (i = 0; i < sisusb->numobufs; i++) {
  197 
- 198       if ((sisusb->urbstatus[i] & (SU_URB_BUSY|SU_URB_ALLOC)) == 0)
 - 199          return i;
  200 
  201    }
  202 
 - 203    return -1;
  204 }
  205 
  206 static int
 
- 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 
 - 215    return i;
  216 }
  217 
  218 static int
 
- 219 sisusb_alloc_outbuf(struct sisusb_usb_data *sisusb)
  220 {
  221    int i;
  222 
  223    i = sisusb_outurb_available(sisusb);
  224 
- 225    if (i >= 0)
  226       sisusb->urbstatus[i] |= SU_URB_ALLOC;
  227 
 - 228    return i;
  229 }
  230 
  231 static void
 
- 232 sisusb_free_outbuf(struct sisusb_usb_data *sisusb, int index)
  233 {
- 234    if ((index >= 0) && (index < sisusb->numobufs))
 - 234   (T) && (T)
 - 234   (T) && (F)
 - 234   (F) && (_)
  235       sisusb->urbstatus[index] &= ~SU_URB_ALLOC;
  236 }
  237 
  238 /* completion callback */
  239 
  240 static void
 
- 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 
- 246    if (!context)
 - 247       return;
  248 
  249    sisusb = context->sisusb;
  250 
- 251    if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
 - 251   T || _ || _
 - 251   F || T || _
 - 251   F || F || T
 - 251   F || F || F
 - 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
 
- 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 
- 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) ?
- 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 */
- 294    if ((retval == 0) && timeout) {
 - 294   (T) && T
 - 294   (T) && F
 - 294   (F) && _
  295       wait_event_timeout(sisusb->wait_q,
  296                (!(sisusb->urbstatus[index] & SU_URB_BUSY)),
  297                timeout);
- 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 
- 309    if (actual_length)
  310       *actual_length = byteswritten;
  311 
 - 312    return retval;
  313 }
  314 
  315 /* 2. in-bulks */
  316 
  317 /* completion callback */
  318 
  319 static void
 
- 320 sisusb_bulk_completein(struct urb *urb, struct pt_regs *regs)
  321 {
  322    struct sisusb_usb_data *sisusb = urb->context;
  323 
- 324    if (!sisusb || !sisusb->sisusb_dev || !sisusb->present)
 - 324   T || _ || _
 - 324   F || T || _
 - 324   F || F || T
 - 324   F || F || F
 - 325       return;
  326 
  327    sisusb->completein = 1;
  328    wake_up(&sisusb->wait_q);
  329 }
  330 
  331 static int
 
- 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 
- 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);
- 351    if (retval == 0) {
  352       wait_event_timeout(sisusb->wait_q, sisusb->completein, timeout);
- 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 
- 364    if (actual_length)
  365       *actual_length = readbytes;
  366 
 - 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 
 
- 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;
- 392 ternary-?: ( userbuffer != ( ( void * ) 0 ) )
    393    int fromkern = (kernbuffer != NULL) ? 1 : 0;
- 393 ternary-?: ( kernbuffer != ( ( void * ) 0 ) )
  394    unsigned int pipe;
  395    char *buffer;
  396 
  397    (*bytes_written) = 0;
  398 
  399    /* Sanity check */
- 400    if (!sisusb || !sisusb->present || !sisusb->sisusb_dev)
 - 400   T || _ || _
 - 400   F || T || _
 - 400   F || F || T
 - 400   F || F || F
 - 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     */
- 411    if (fromuser || fromkern)
 - 411   T || _
 - 411   F || T
 - 411   F || F
  412       index = -1;
- 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) ?
- 419   ternary-?: ( sisusb -> obufsize < count )
  420                   sisusb->obufsize : count;
  421 
- 422       if (index < 0)
  423          index = sisusb_get_free_outbuf(sisusb);
  424 
- 425       if (index < 0)
 - 426          return -EIO;
  427 
  428       buffer = sisusb->obuf[index];
  429 
- 430       if (fromuser) {
  431 
- 432          if (copy_from_user(buffer, userbuffer, passsize))
 - 433             return -EFAULT;
  434 
  435          userbuffer += passsize;
  436 
- 437       } else if (fromkern) {
  438 
  439          memcpy(buffer, kernbuffer, passsize);
  440          kernbuffer += passsize;
  441 
  442       }
  443 
  444       retry = 5;
- 445       while (thispass) {
  446 
- 447          if (!sisusb->sisusb_dev)
 - 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,
- 456     ternary-?: async
  457                   tflags,
  458                   sisusb->transfer_dma_out[index]);
  459 
- 460          if (result == -ETIMEDOUT) {
  461 
  462             /* Will not happen if async */
- 463             if (!retry--)
 - 464                return -ETIME;
  465 
 - 466             continue;
  467 
- 468          } else if ((result == 0) && !async && transferred_len) {
 - 468       (T) && T && T
 - 468       (T) && T && F
 - 468       (T) && F && _
 - 468       (F) && _ && _
  469 
  470             thispass -= transferred_len;
- 471             if (thispass) {
- 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
 - 488             break;
  489       };
  490 
- 491       if (result)
 - 492          return result;
  493 
  494       (*bytes_written) += passsize;
  495       count            -= passsize;
  496 
  497       /* Force new allocation in next iteration */
- 498       if (fromuser || fromkern)
 - 498     T || _
 - 498     F || T
 - 498     F || F
  499          index = -1;
  500 
- 501    } while (count > 0);
  502 
- 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      &