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

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


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

Start/ End/    
True False - Line Source

  1 /*
  2  * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
  3  *
  4  * VGA text mode console 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  * Portions based on vgacon.c which are
  38  *   Created 28 Sep 1997 by Geert Uytterhoeven
  39  *      Rewritten by Martin Mares <mj@ucw.cz>, July 1998
  40  *      based on code Copyright (C) 1991, 1992  Linus Torvalds
  41  *             1995  Jay Estabrook
  42  *
  43  * A note on using in_atomic() in here: We can't handle console
  44  * calls from non-schedulable context due to our USB-dependend
  45  * nature. For now, this driver just ignores any calls if it
  46  * detects this state.
  47  *
  48  */
  49 
  50 #include <linux/config.h>
  51 #include <linux/module.h>
  52 #include <linux/kernel.h>
  53 #include <linux/signal.h>
  54 #include <linux/sched.h>
  55 #include <linux/fs.h>
  56 #include <linux/tty.h>
  57 #include <linux/console.h>
  58 #include <linux/string.h>
  59 #include <linux/kd.h>
  60 #include <linux/init.h>
  61 #include <linux/slab.h>
  62 #include <linux/vt_kern.h>
  63 #include <linux/selection.h>
  64 #include <linux/spinlock.h>
  65 #include <linux/kref.h>
  66 #include <linux/smp_lock.h>
  67 #include <linux/ioport.h>
  68 #include <linux/interrupt.h>
  69 #include <linux/vmalloc.h>
  70 
  71 #include "sisusb.h"
  72 
  73 #ifdef INCL_SISUSB_CON
  74 extern int sisusb_setreg(struct sisusb_usb_data *, int, u8);
  75 extern int sisusb_getreg(struct sisusb_usb_data *, int, u8 *);
  76 extern int sisusb_setidxreg(struct sisusb_usb_data *, int, u8, u8);
  77 extern int sisusb_getidxreg(struct sisusb_usb_data *, int, u8, u8 *);
  78 extern int sisusb_setidxregor(struct sisusb_usb_data *, int, u8, u8);
  79 extern int sisusb_setidxregand(struct sisusb_usb_data *, int, u8, u8);
  80 extern int sisusb_setidxregandor(struct sisusb_usb_data *, int, u8, u8, u8);
  81 
  82 extern int sisusb_writeb(struct sisusb_usb_data *sisusb, u32 adr, u8 data);
  83 extern int sisusb_readb(struct sisusb_usb_data *sisusb, u32 adr, u8 *data);
  84 extern int sisusb_writew(struct sisusb_usb_data *sisusb, u32 adr, u16 data);
  85 extern int sisusb_readw(struct sisusb_usb_data *sisusb, u32 adr, u16 *data);
  86 extern int sisusb_copy_memory(struct sisusb_usb_data *sisusb, char *src,
  87          u32 dest, int length, size_t *bytes_written);
  88 
  89 extern void sisusb_delete(struct kref *kref);
  90 extern int sisusb_reset_text_mode(struct sisusb_usb_data *sisusb, int init);
  91 
  92 extern int SiSUSBSetMode(struct SiS_Private *SiS_Pr, unsigned short ModeNo);
  93 
  94 #define sisusbcon_writew(val, addr)   (*(addr) = (val))
  95 #define sisusbcon_readw(addr)      (*(addr))
  96 #define sisusbcon_memmovew(d, s, c)   memmove(d, s, c)
  97 #define sisusbcon_memcpyw(d, s, c)   memcpy(d, s, c)
  98 
  99 /* vc_data -> sisusb conversion table */
  100 static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES];
  101 
  102 /* Forward declaration */
  103 static const struct consw sisusb_con;
  104 
  105 extern struct semaphore disconnect_sem;
  106 
  107 static inline void
  108 sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
  109 {
  110    count /= 2;
  111    while (count--)
  112       sisusbcon_writew(c, s++);
  113 }
  114 
  115 static inline void
  116 sisusb_initialize(struct sisusb_usb_data *sisusb)
  117 {
  118    /* Reset cursor and start address */
  119    if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00))
  120       return;
  121    if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00))
  122       return;
  123    if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00))
  124       return;
  125    sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00);
  126 }
  127 
  128 static inline void
  129 sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c)
  130 {
  131    sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2;
  132 
  133    sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
  134    sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
  135 }
  136 
  137 void
  138 sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location)
  139 {
  140    if (sisusb->sisusb_cursor_loc == location)
  141       return;
  142 
  143    sisusb->sisusb_cursor_loc = location;
  144 
  145    /* Hardware bug: Text cursor appears twice or not at all
  146     * at some positions. Work around it with the cursor skew
  147     * bits.
  148     */
  149 
  150    if ((location & 0x0007) == 0x0007) {
  151       sisusb->bad_cursor_pos = 1;
  152       location--;
  153       if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20))
  154          return;
  155    } else if (sisusb->bad_cursor_pos) {
  156       if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f))
  157          return;
  158       sisusb->bad_cursor_pos = 0;
  159    }
  160 
  161    if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8)))
  162       return;
  163    sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff));
  164 }
  165 
  166 static inline struct sisusb_usb_data *
  167 sisusb_get_sisusb(unsigned short console)
  168 {
  169    return mysisusbs[console];
  170 }
  171 
  172 static inline int
  173 sisusb_sisusb_valid(struct sisusb_usb_data *sisusb)
  174 {
  175    if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev)
  176       return 0;
  177 
  178    return 1;
  179 }
  180 
  181 static struct sisusb_usb_data *
  182 sisusb_get_sisusb_lock_and_check(unsigned short console)
  183 {
  184    struct sisusb_usb_data *sisusb;
  185 
  186    /* We can't handle console calls in non-schedulable
  187     * context due to our locks and the USB transport.
  188     * So we simply ignore them. This should only affect
  189     * some calls to printk.
  190     */
  191    if (in_atomic())
  192       return NULL;
  193 
  194    if (!(sisusb = sisusb_get_sisusb(console)))
  195       return NULL;
  196 
  197    down(&sisusb->lock);
  198 
  199    if (!sisusb_sisusb_valid(sisusb) ||
  200        !sisusb->havethisconsole[console]) {
  201       up(&sisusb->lock);
  202       return NULL;
  203    }
  204 
  205    return sisusb;
  206 }
  207 
  208 static int
  209 sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb)
  210 {
  211    if (sisusb->is_gfx ||
  212        sisusb->textmodedestroyed ||
  213        c->vc_mode != KD_TEXT)
  214       return 1;
  215 
  216    return 0;
  217 }
  218 
  219 /* con_startup console interface routine */
  220 static const char *
  221 sisusbcon_startup(void)
  222 {
  223    return "SISUSBCON";
  224 }
  225 
  226 /* con_init console interface routine */
  227 static void
  228 sisusbcon_init(struct vc_data *c, int init)
  229 {
  230    struct sisusb_usb_data *sisusb;
  231    int cols, rows;
  232 
  233    /* This is called by take_over_console(),
  234     * ie by us/under our control. It is
  235     * only called after text mode and fonts
  236     * are set up/restored.
  237     */
  238 
  239    down(&disconnect_sem);
  240 
  241    if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
  242       up(&disconnect_sem);
  243       return;
  244    }
  245 
  246    down(&sisusb->lock);
  247 
  248    if (!sisusb_sisusb_valid(sisusb)) {
  249       up(&sisusb->lock);
  250       up(&disconnect_sem);
  251       return;
  252    }
  253 
  254    c->vc_can_do_color = 1;
  255 
  256    c->vc_complement_mask = 0x7700;
  257 
  258    c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0;
  259 
  260    sisusb->haveconsole = 1;
  261 
  262    sisusb->havethisconsole[c->vc_num] = 1;
  263 
  264    /* We only support 640x400 */
  265    c->vc_scan_lines = 400;
  266 
  267    c->vc_font.height = sisusb->current_font_height;
  268 
  269    /* We only support width = 8 */
  270    cols = 80;
  271    rows = c->vc_scan_lines / c->vc_font.height;
  272 
  273    /* Increment usage count for our sisusb.
  274     * Doing so saves us from upping/downing
  275     * the disconnect semaphore; we can't
  276     * lose our sisusb until this is undone
  277     * in con_deinit. For all other console
  278     * interface functions, it suffices to
  279     * use sisusb->lock and do a quick check
  280     * of sisusb for device disconnection.
  281     */
  282    kref_get(&sisusb->kref);
  283 
  284    if (!*c->vc_uni_pagedir_loc)
  285       con_set_default_unimap(c);
  286 
  287    up(&sisusb->lock);
  288 
  289    up(&disconnect_sem);
  290 
  291    if (init) {
  292       c->vc_cols = cols;
  293       c->vc_rows = rows;
  294    } else
  295       vc_resize(c, cols, rows);
  296 }
  297 
  298 /* con_deinit console interface routine */
  299 static void
  300 sisusbcon_deinit(struct vc_data *c)
  301 {
  302    struct sisusb_usb_data *sisusb;
  303    int i;
  304 
  305    /* This is called by take_over_console()
  306     * and others, ie not under our control.
  307     */
  308 
  309    down(&disconnect_sem);
  310 
  311    if (!(sisusb = sisusb_get_sisusb(c->vc_num))) {
  312       up(&disconnect_sem);
  313       return;
  314    }
  315 
  316    down(&sisusb->lock);
  317 
  318    /* Clear ourselves in mysisusbs */
  319    mysisusbs[c->vc_num] = NULL;
  320 
  321    sisusb->havethisconsole[c->vc_num] = 0;
  322 
  323    /* Free our font buffer if all consoles are gone */
  324    if (sisusb->font_backup) {
  325       for(i = 0; i < MAX_NR_CONSOLES; i++) {
  326          if (sisusb->havethisconsole[c->vc_num])
  327             break;
  328       }
  329       if (i == MAX_NR_CONSOLES) {
  330          vfree(sisusb->font_backup);
  331          sisusb->font_backup = NULL;
  332       }
  333    }
  334 
  335    up(&sisusb->lock);
  336 
  337    /* decrement the usage count on our sisusb */
  338    kref_put(&sisusb->kref, sisusb_delete);
  339 
  340    up(&disconnect_sem);
  341 }
  342 
  343 /* interface routine */
  344 static u8
  345 sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity,
  346              u8 blink, u8 underline, u8 reverse)
  347 {
  348    u8 attr = color;
  349 
  350    if (underline)
  351       attr = (attr & 0xf0) | c->vc_ulcolor;
  352    else if (intensity == 0)
  353       attr = (attr & 0xf0) | c->vc_halfcolor;
  354 
  355    if (reverse)
  356       attr = ((attr) & 0x88) |
  357              ((((attr) >> 4) |
  358              ((attr) << 4)) & 0x77);
  359 
  360    if (blink)
  361       attr ^= 0x80;
  362 
  363    if (intensity == 2)
  364       attr ^= 0x08;
  365 
  366    return attr;
  367 }
  368 
  369 /* Interface routine */
  370 static void
  371 sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count)
  372 {
  373    /* Invert a region. This is called with a pointer
  374     * to the console's internal screen buffer. So we
  375     * simply do the inversion there and rely on
  376     * a call to putc(s) to update the real screen.
  377     */
  378 
  379    while (count--) {
  380       u16 a = sisusbcon_readw(p);
  381 
  382       a = ((a) & 0x88ff)        |
  383           (((a) & 0x7000) >> 4) |
  384           (((a) & 0x0700) << 4);
  385 
  386       sisusbcon_writew(a, p++);
  387    }
  388 }
  389 
  390 #define SISUSB_VADDR(x,y) \
  391    ((u16 *)c->vc_origin + \
  392    (y) * sisusb->sisusb_num_columns + \
  393    (x))
  394 
  395 #define SISUSB_HADDR(x,y) \
  396    ((u16 *)(sisusb->vrambase + (c->vc_origin - sisusb->scrbuf)) + \
  397    (y) * sisusb->sisusb_num_columns + \
  398    (x))
  399 
  400 /* Interface routine */
  401 static void
  402 sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
  403 {
  404    struct sisusb_usb_data *sisusb;
  405    ssize_t written;
  406 
  407    if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
  408       return;
  409 
  410    /* sisusb->lock is down */
  411 
  412    /* Don't need to put the character into buffer ourselves,
  413     * because the vt does this BEFORE calling us.
  414     */
  415 #if 0
  416    sisusbcon_writew(ch, SISUSB_VADDR(x, y));
  417 #endif
  418 
  419    if (sisusb_is_inactive(c, sisusb)) {
  420       up(&sisusb->lock);
  421       return;
  422    }
  423 
  424 
  425    sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
  426             (u32)SISUSB_HADDR(x, y), 2, &written);
  427 
  428    up(&sisusb->lock);
  429 }
  430 
  431 /* Interface routine */
  432 static void
  433 sisusbcon_putcs(struct vc_data *c, const unsigned short *s,
  434                int count, int y, int x)
  435 {
  436    struct sisusb_usb_data *sisusb;
  437    ssize_t written;
  438    u16 *dest;
  439    int i;
  440 
  441    if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
  442       return;
  443 
  444    /* sisusb->lock is down */
  445 
  446    /* Need to put the characters into the buffer ourselves,
  447     * because the vt does this AFTER calling us.
  448     */
  449 
  450    dest = SISUSB_VADDR(x, y);
  451 
  452    for (i = count; i > 0; i--)
  453       sisusbcon_writew(sisusbcon_readw(s++), dest++);
  454 
  455    if (sisusb_is_inactive(c, sisusb)) {
  456       up(&sisusb->lock);
  457       return;
  458    }
  459 
  460    sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
  461             (u32)SISUSB_HADDR(x, y), count * 2, &written);
  462 
  463    up(&sisusb->lock);
  464 }
  465 
  466 /* Interface routine */
  467 static void
  468 sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width)
  469 {
  470    struct sisusb_usb_data *sisusb;
  471    u16 eattr = c->vc_video_erase_char;
  472    ssize_t written;
  473    int i, length, cols;
  474    u16 *dest;
  475 
  476    if (width <= 0 || height <= 0)
  477       return;
  478 
  479    if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
  480       return;
  481 
  482    /* sisusb->lock is down */
  483 
  484    /* Need to clear buffer ourselves, because the vt does
  485     * this AFTER calling us.
  486     */
  487 
  488    dest = SISUSB_VADDR(x, y);
  489 
  490    cols = sisusb->sisusb_num_columns;
  491 
  492    if (width > cols)
  493       width = cols;
  494 
  495    if (x == 0 && width >= c->vc_cols) {
  496 
  497       sisusbcon_memsetw(dest, eattr, height * cols * 2);
  498 
  499    } else {
  500 
  501       for (i = height; i > 0; i--, dest += cols)
  502          sisusbcon_memsetw(dest, eattr, width * 2);
  503 
  504    }
  505 
  506    if (sisusb_is_inactive(c, sisusb)) {
  507       up(&sisusb->lock);
  508       return;
  509    }
  510 
  511    length = ((height * cols) - x - (cols - width - x)) * 2;
  512 
  513 
  514    sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y),
  515             (u32)SISUSB_HADDR(x, y), length, &written);
  516 
  517    up(&sisusb->lock);
  518 }
  519 
  520 /* Interface routine */
  521 static void
  522 sisusbcon_bmove(struct vc_data *c, int sy, int sx,
  523           int dy, int dx, int height, int width)
  524 {
  525    struct sisusb_usb_data *sisusb;
  526    ssize_t written;
  527    int cols, length;
  528 #if 0
  529    u16 *src, *dest;
  530    int i;
  531 #endif
  532 
  533    if (width <= 0 || height <= 0)
  534       return;
  535 
  536    if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
  537       return;
  538 
  539    /* sisusb->lock is down */
  540 
  541    cols = sisusb->sisusb_num_columns;
  542 
  543    /* Don't need to move data outselves, because
  544     * vt does this BEFORE calling us.
  545     * This is only used by vt's insert/deletechar.
  546     */
  547 #if 0
  548    if (sx == 0 && dx == 0 && width >= c->vc_cols && width <= cols) {
  549 
  550       sisusbcon_memmovew(SISUSB_VADDR(0, dy), SISUSB_VADDR(0, sy),
  551                height * width * 2);
  552 
  553    } else if (dy < sy || (dy == sy && dx < sx)) {
  554 
  555       src  = SISUSB_VADDR(sx, sy);
  556       dest = SISUSB_VADDR(dx, dy);
  557 
  558       for (i = height; i > 0; i--) {
  559          sisusbcon_memmovew(dest, src, width * 2);
  560          src  += cols;
  561          dest += cols;
  562       }
  563 
  564    } else {
  565 
  566       src  = SISUSB_VADDR(sx, sy + height - 1);
  567       dest = SISUSB_VADDR(dx, dy + height - 1);
  568 
  569       for (i = height; i > 0; i--) {
  570          sisusbcon_memmovew(dest, src, width * 2);
  571          src  -= cols;
  572          dest -= cols;
  573       }
  574 
  575    }
  576 #endif
  577 
  578    if (sisusb_is_inactive(c, sisusb)) {
  579       up(&sisusb->lock);
  580       return;
  581    }
  582 
  583    length = ((height * cols) - dx - (cols - width - dx)) * 2;
  584 
  585 
  586    sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy),
  587             (u32)SISUSB_HADDR(dx, dy), length, &written);
  588 
  589    up(&sisusb->lock);
  590 }
  591 
  592 /* interface routine */
  593 static int
  594 sisusbcon_switch(struct vc_data *c)
  595 {
  596    struct sisusb_usb_data *sisusb;
  597    ssize_t written;
  598    int length;
  599 
  600    /* Returnvalue 0 means we have fully restored screen,
  601     *   and vt doesn't need to call do_update_region().
  602     * Returnvalue != 0 naturally means the opposite.
  603     */
  604 
  605    if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
  606       return 0;
  607 
  608    /* sisusb->lock is down */
  609 
  610    /* Don't write to screen if in gfx mode */
  611    if (sisusb_is_inactive(c, sisusb)) {
  612       up(&sisusb->lock);
  613       return 0;
  614    }
  615 
  616    /* That really should not happen. It would mean we are
  617     * being called while the vc is using its private buffer
  618     * as origin.
  619     */
  620    if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
  621       up(&sisusb->lock);
  622       printk(KERN_DEBUG "sisusb: ASSERT ORIGIN != SCREENBUF!\n");
  623       return 0;
  624    }
  625 
  626    /* Check that we don't copy too much */
  627    length = min((int)c->vc_screenbuf_size,
  628          (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
  629 
  630    /* Restore the screen contents */
  631    sisusbcon_memcpyw((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf,
  632                         length);
  633 
  634    sisusb_copy_memory(sisusb, (unsigned char *)c->vc_origin,
  635             (u32)SISUSB_HADDR(0, 0),
  636             length, &written);
  637 
  638    up(&sisusb->lock);
  639 
  640    return 0;
  641 }
  642 
  643 /* interface routine */
  644 static void
  645 sisusbcon_save_screen(struct vc_data *c)