Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
0002 /*
0003  * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
0004  *
0005  * VGA text mode console part
0006  *
0007  * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
0008  *
0009  * If distributed as part of the Linux kernel, this code is licensed under the
0010  * terms of the GPL v2.
0011  *
0012  * Otherwise, the following license terms apply:
0013  *
0014  * * Redistribution and use in source and binary forms, with or without
0015  * * modification, are permitted provided that the following conditions
0016  * * are met:
0017  * * 1) Redistributions of source code must retain the above copyright
0018  * *    notice, this list of conditions and the following disclaimer.
0019  * * 2) Redistributions in binary form must reproduce the above copyright
0020  * *    notice, this list of conditions and the following disclaimer in the
0021  * *    documentation and/or other materials provided with the distribution.
0022  * * 3) The name of the author may not be used to endorse or promote products
0023  * *    derived from this software without specific psisusbr written permission.
0024  * *
0025  * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
0026  * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
0027  * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
0028  * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
0029  * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
0030  * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
0031  * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
0032  * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
0033  * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
0034  * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
0035  *
0036  * Author: Thomas Winischhofer <thomas@winischhofer.net>
0037  *
0038  * Portions based on vgacon.c which are
0039  *  Created 28 Sep 1997 by Geert Uytterhoeven
0040  *      Rewritten by Martin Mares <mj@ucw.cz>, July 1998
0041  *      based on code Copyright (C) 1991, 1992  Linus Torvalds
0042  *              1995  Jay Estabrook
0043  *
0044  * A note on using in_atomic() in here: We can't handle console
0045  * calls from non-schedulable context due to our USB-dependend
0046  * nature. For now, this driver just ignores any calls if it
0047  * detects this state.
0048  *
0049  */
0050 
0051 #include <linux/mutex.h>
0052 #include <linux/module.h>
0053 #include <linux/kernel.h>
0054 #include <linux/signal.h>
0055 #include <linux/fs.h>
0056 #include <linux/usb.h>
0057 #include <linux/tty.h>
0058 #include <linux/console.h>
0059 #include <linux/string.h>
0060 #include <linux/kd.h>
0061 #include <linux/init.h>
0062 #include <linux/vt_kern.h>
0063 #include <linux/selection.h>
0064 #include <linux/spinlock.h>
0065 #include <linux/kref.h>
0066 #include <linux/ioport.h>
0067 #include <linux/interrupt.h>
0068 #include <linux/vmalloc.h>
0069 
0070 #include "sisusb.h"
0071 #include "sisusb_init.h"
0072 
0073 /* vc_data -> sisusb conversion table */
0074 static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES];
0075 
0076 /* Forward declaration */
0077 static const struct consw sisusb_con;
0078 
0079 static inline void
0080 sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
0081 {
0082     memset16(s, c, count / 2);
0083 }
0084 
0085 static inline void
0086 sisusb_initialize(struct sisusb_usb_data *sisusb)
0087 {
0088     /* Reset cursor and start address */
0089     if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00))
0090         return;
0091     if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00))
0092         return;
0093     if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00))
0094         return;
0095     sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00);
0096 }
0097 
0098 static inline void
0099 sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c)
0100 {
0101     sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2;
0102 
0103     sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
0104     sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
0105 }
0106 
0107 void
0108 sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location)
0109 {
0110     if (sisusb->sisusb_cursor_loc == location)
0111         return;
0112 
0113     sisusb->sisusb_cursor_loc = location;
0114 
0115     /* Hardware bug: Text cursor appears twice or not at all
0116      * at some positions. Work around it with the cursor skew
0117      * bits.
0118      */
0119 
0120     if ((location & 0x0007) == 0x0007) {
0121         sisusb->bad_cursor_pos = 1;
0122         location--;
0123         if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20))
0124             return;
0125     } else if (sisusb->bad_cursor_pos) {
0126         if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f))
0127             return;
0128         sisusb->bad_cursor_pos = 0;
0129     }
0130 
0131     if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8)))
0132         return;
0133     sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff));
0134 }
0135 
0136 static inline struct sisusb_usb_data *
0137 sisusb_get_sisusb(unsigned short console)
0138 {
0139     return mysisusbs[console];
0140 }
0141 
0142 static inline int
0143 sisusb_sisusb_valid(struct sisusb_usb_data *sisusb)
0144 {
0145     if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev)
0146         return 0;
0147 
0148     return 1;
0149 }
0150 
0151 static struct sisusb_usb_data *
0152 sisusb_get_sisusb_lock_and_check(unsigned short console)
0153 {
0154     struct sisusb_usb_data *sisusb;
0155 
0156     /* We can't handle console calls in non-schedulable
0157      * context due to our locks and the USB transport.
0158      * So we simply ignore them. This should only affect
0159      * some calls to printk.
0160      */
0161     if (in_atomic())
0162         return NULL;
0163 
0164     sisusb = sisusb_get_sisusb(console);
0165     if (!sisusb)
0166         return NULL;
0167 
0168     mutex_lock(&sisusb->lock);
0169 
0170     if (!sisusb_sisusb_valid(sisusb) ||
0171         !sisusb->havethisconsole[console]) {
0172         mutex_unlock(&sisusb->lock);
0173         return NULL;
0174     }
0175 
0176     return sisusb;
0177 }
0178 
0179 static int
0180 sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb)
0181 {
0182     if (sisusb->is_gfx ||
0183         sisusb->textmodedestroyed ||
0184         c->vc_mode != KD_TEXT)
0185         return 1;
0186 
0187     return 0;
0188 }
0189 
0190 /* con_startup console interface routine */
0191 static const char *
0192 sisusbcon_startup(void)
0193 {
0194     return "SISUSBCON";
0195 }
0196 
0197 /* con_init console interface routine */
0198 static void
0199 sisusbcon_init(struct vc_data *c, int init)
0200 {
0201     struct sisusb_usb_data *sisusb;
0202     int cols, rows;
0203 
0204     /* This is called by do_take_over_console(),
0205      * ie by us/under our control. It is
0206      * only called after text mode and fonts
0207      * are set up/restored.
0208      */
0209 
0210     sisusb = sisusb_get_sisusb(c->vc_num);
0211     if (!sisusb)
0212         return;
0213 
0214     mutex_lock(&sisusb->lock);
0215 
0216     if (!sisusb_sisusb_valid(sisusb)) {
0217         mutex_unlock(&sisusb->lock);
0218         return;
0219     }
0220 
0221     c->vc_can_do_color = 1;
0222 
0223     c->vc_complement_mask = 0x7700;
0224 
0225     c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0;
0226 
0227     sisusb->haveconsole = 1;
0228 
0229     sisusb->havethisconsole[c->vc_num] = 1;
0230 
0231     /* We only support 640x400 */
0232     c->vc_scan_lines = 400;
0233 
0234     c->vc_font.height = sisusb->current_font_height;
0235 
0236     /* We only support width = 8 */
0237     cols = 80;
0238     rows = c->vc_scan_lines / c->vc_font.height;
0239 
0240     /* Increment usage count for our sisusb.
0241      * Doing so saves us from upping/downing
0242      * the disconnect semaphore; we can't
0243      * lose our sisusb until this is undone
0244      * in con_deinit. For all other console
0245      * interface functions, it suffices to
0246      * use sisusb->lock and do a quick check
0247      * of sisusb for device disconnection.
0248      */
0249     kref_get(&sisusb->kref);
0250 
0251     if (!*c->uni_pagedict_loc)
0252         con_set_default_unimap(c);
0253 
0254     mutex_unlock(&sisusb->lock);
0255 
0256     if (init) {
0257         c->vc_cols = cols;
0258         c->vc_rows = rows;
0259     } else
0260         vc_resize(c, cols, rows);
0261 }
0262 
0263 /* con_deinit console interface routine */
0264 static void
0265 sisusbcon_deinit(struct vc_data *c)
0266 {
0267     struct sisusb_usb_data *sisusb;
0268     int i;
0269 
0270     /* This is called by do_take_over_console()
0271      * and others, ie not under our control.
0272      */
0273 
0274     sisusb = sisusb_get_sisusb(c->vc_num);
0275     if (!sisusb)
0276         return;
0277 
0278     mutex_lock(&sisusb->lock);
0279 
0280     /* Clear ourselves in mysisusbs */
0281     mysisusbs[c->vc_num] = NULL;
0282 
0283     sisusb->havethisconsole[c->vc_num] = 0;
0284 
0285     /* Free our font buffer if all consoles are gone */
0286     if (sisusb->font_backup) {
0287         for(i = 0; i < MAX_NR_CONSOLES; i++) {
0288             if (sisusb->havethisconsole[c->vc_num])
0289                 break;
0290         }
0291         if (i == MAX_NR_CONSOLES) {
0292             vfree(sisusb->font_backup);
0293             sisusb->font_backup = NULL;
0294         }
0295     }
0296 
0297     mutex_unlock(&sisusb->lock);
0298 
0299     /* decrement the usage count on our sisusb */
0300     kref_put(&sisusb->kref, sisusb_delete);
0301 }
0302 
0303 /* interface routine */
0304 static u8
0305 sisusbcon_build_attr(struct vc_data *c, u8 color, enum vc_intensity intensity,
0306                 bool blink, bool underline, bool reverse,
0307                 bool unused)
0308 {
0309     u8 attr = color;
0310 
0311     if (underline)
0312         attr = (attr & 0xf0) | c->vc_ulcolor;
0313     else if (intensity == VCI_HALF_BRIGHT)
0314         attr = (attr & 0xf0) | c->vc_halfcolor;
0315 
0316     if (reverse)
0317         attr = ((attr) & 0x88) |
0318                ((((attr) >> 4) |
0319                ((attr) << 4)) & 0x77);
0320 
0321     if (blink)
0322         attr ^= 0x80;
0323 
0324     if (intensity == VCI_BOLD)
0325         attr ^= 0x08;
0326 
0327     return attr;
0328 }
0329 
0330 /* Interface routine */
0331 static void
0332 sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count)
0333 {
0334     /* Invert a region. This is called with a pointer
0335      * to the console's internal screen buffer. So we
0336      * simply do the inversion there and rely on
0337      * a call to putc(s) to update the real screen.
0338      */
0339 
0340     while (count--) {
0341         u16 a = *p;
0342 
0343         *p++ = ((a) & 0x88ff)        |
0344                (((a) & 0x7000) >> 4) |
0345                (((a) & 0x0700) << 4);
0346     }
0347 }
0348 
0349 static inline void *sisusb_vaddr(const struct sisusb_usb_data *sisusb,
0350         const struct vc_data *c, unsigned int x, unsigned int y)
0351 {
0352     return (u16 *)c->vc_origin + y * sisusb->sisusb_num_columns + x;
0353 }
0354 
0355 static inline unsigned long sisusb_haddr(const struct sisusb_usb_data *sisusb,
0356           const struct vc_data *c, unsigned int x, unsigned int y)
0357 {
0358     unsigned long offset = c->vc_origin - sisusb->scrbuf;
0359 
0360     /* 2 bytes per each character */
0361     offset += 2 * (y * sisusb->sisusb_num_columns + x);
0362 
0363     return sisusb->vrambase + offset;
0364 }
0365 
0366 /* Interface routine */
0367 static void
0368 sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
0369 {
0370     struct sisusb_usb_data *sisusb;
0371 
0372     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0373     if (!sisusb)
0374         return;
0375 
0376     /* sisusb->lock is down */
0377     if (sisusb_is_inactive(c, sisusb)) {
0378         mutex_unlock(&sisusb->lock);
0379         return;
0380     }
0381 
0382     sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
0383                 sisusb_haddr(sisusb, c, x, y), 2);
0384 
0385     mutex_unlock(&sisusb->lock);
0386 }
0387 
0388 /* Interface routine */
0389 static void
0390 sisusbcon_putcs(struct vc_data *c, const unsigned short *s,
0391                  int count, int y, int x)
0392 {
0393     struct sisusb_usb_data *sisusb;
0394 
0395     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0396     if (!sisusb)
0397         return;
0398 
0399     /* sisusb->lock is down */
0400 
0401     /* Need to put the characters into the buffer ourselves,
0402      * because the vt does this AFTER calling us.
0403      */
0404 
0405     memcpy(sisusb_vaddr(sisusb, c, x, y), s, count * 2);
0406 
0407     if (sisusb_is_inactive(c, sisusb)) {
0408         mutex_unlock(&sisusb->lock);
0409         return;
0410     }
0411 
0412     sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
0413             sisusb_haddr(sisusb, c, x, y), count * 2);
0414 
0415     mutex_unlock(&sisusb->lock);
0416 }
0417 
0418 /* Interface routine */
0419 static void
0420 sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width)
0421 {
0422     struct sisusb_usb_data *sisusb;
0423     u16 eattr = c->vc_video_erase_char;
0424     int i, length, cols;
0425     u16 *dest;
0426 
0427     if (width <= 0 || height <= 0)
0428         return;
0429 
0430     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0431     if (!sisusb)
0432         return;
0433 
0434     /* sisusb->lock is down */
0435 
0436     /* Need to clear buffer ourselves, because the vt does
0437      * this AFTER calling us.
0438      */
0439 
0440     dest = sisusb_vaddr(sisusb, c, x, y);
0441 
0442     cols = sisusb->sisusb_num_columns;
0443 
0444     if (width > cols)
0445         width = cols;
0446 
0447     if (x == 0 && width >= c->vc_cols) {
0448 
0449         sisusbcon_memsetw(dest, eattr, height * cols * 2);
0450 
0451     } else {
0452 
0453         for (i = height; i > 0; i--, dest += cols)
0454             sisusbcon_memsetw(dest, eattr, width * 2);
0455 
0456     }
0457 
0458     if (sisusb_is_inactive(c, sisusb)) {
0459         mutex_unlock(&sisusb->lock);
0460         return;
0461     }
0462 
0463     length = ((height * cols) - x - (cols - width - x)) * 2;
0464 
0465 
0466     sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
0467             sisusb_haddr(sisusb, c, x, y), length);
0468 
0469     mutex_unlock(&sisusb->lock);
0470 }
0471 
0472 /* interface routine */
0473 static int
0474 sisusbcon_switch(struct vc_data *c)
0475 {
0476     struct sisusb_usb_data *sisusb;
0477     int length;
0478 
0479     /* Returnvalue 0 means we have fully restored screen,
0480      *  and vt doesn't need to call do_update_region().
0481      * Returnvalue != 0 naturally means the opposite.
0482      */
0483 
0484     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0485     if (!sisusb)
0486         return 0;
0487 
0488     /* sisusb->lock is down */
0489 
0490     /* Don't write to screen if in gfx mode */
0491     if (sisusb_is_inactive(c, sisusb)) {
0492         mutex_unlock(&sisusb->lock);
0493         return 0;
0494     }
0495 
0496     /* That really should not happen. It would mean we are
0497      * being called while the vc is using its private buffer
0498      * as origin.
0499      */
0500     if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
0501         mutex_unlock(&sisusb->lock);
0502         dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n");
0503         return 0;
0504     }
0505 
0506     /* Check that we don't copy too much */
0507     length = min((int)c->vc_screenbuf_size,
0508             (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
0509 
0510     /* Restore the screen contents */
0511     memcpy((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, length);
0512 
0513     sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
0514             sisusb_haddr(sisusb, c, 0, 0), length);
0515 
0516     mutex_unlock(&sisusb->lock);
0517 
0518     return 0;
0519 }
0520 
0521 /* interface routine */
0522 static void
0523 sisusbcon_save_screen(struct vc_data *c)
0524 {
0525     struct sisusb_usb_data *sisusb;
0526     int length;
0527 
0528     /* Save the current screen contents to vc's private
0529      * buffer.
0530      */
0531 
0532     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0533     if (!sisusb)
0534         return;
0535 
0536     /* sisusb->lock is down */
0537 
0538     if (sisusb_is_inactive(c, sisusb)) {
0539         mutex_unlock(&sisusb->lock);
0540         return;
0541     }
0542 
0543     /* Check that we don't copy too much */
0544     length = min((int)c->vc_screenbuf_size,
0545             (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
0546 
0547     /* Save the screen contents to vc's private buffer */
0548     memcpy((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin, length);
0549 
0550     mutex_unlock(&sisusb->lock);
0551 }
0552 
0553 /* interface routine */
0554 static void
0555 sisusbcon_set_palette(struct vc_data *c, const unsigned char *table)
0556 {
0557     struct sisusb_usb_data *sisusb;
0558     int i, j;
0559 
0560     /* Return value not used by vt */
0561 
0562     if (!con_is_visible(c))
0563         return;
0564 
0565     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0566     if (!sisusb)
0567         return;
0568 
0569     /* sisusb->lock is down */
0570 
0571     if (sisusb_is_inactive(c, sisusb)) {
0572         mutex_unlock(&sisusb->lock);
0573         return;
0574     }
0575 
0576     for (i = j = 0; i < 16; i++) {
0577         if (sisusb_setreg(sisusb, SISCOLIDX, table[i]))
0578             break;
0579         if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
0580             break;
0581         if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
0582             break;
0583         if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
0584             break;
0585     }
0586 
0587     mutex_unlock(&sisusb->lock);
0588 }
0589 
0590 /* interface routine */
0591 static int
0592 sisusbcon_blank(struct vc_data *c, int blank, int mode_switch)
0593 {
0594     struct sisusb_usb_data *sisusb;
0595     u8 sr1, cr17, pmreg, cr63;
0596     int ret = 0;
0597 
0598     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0599     if (!sisusb)
0600         return 0;
0601 
0602     /* sisusb->lock is down */
0603 
0604     if (mode_switch)
0605         sisusb->is_gfx = blank ? 1 : 0;
0606 
0607     if (sisusb_is_inactive(c, sisusb)) {
0608         mutex_unlock(&sisusb->lock);
0609         return 0;
0610     }
0611 
0612     switch (blank) {
0613 
0614     case 1:     /* Normal blanking: Clear screen */
0615     case -1:
0616         sisusbcon_memsetw((u16 *)c->vc_origin,
0617                 c->vc_video_erase_char,
0618                 c->vc_screenbuf_size);
0619         sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
0620                 sisusb_haddr(sisusb, c, 0, 0),
0621                 c->vc_screenbuf_size);
0622         sisusb->con_blanked = 1;
0623         ret = 1;
0624         break;
0625 
0626     default:    /* VESA blanking */
0627         switch (blank) {
0628         case 0: /* Unblank */
0629             sr1   = 0x00;
0630             cr17  = 0x80;
0631             pmreg = 0x00;
0632             cr63  = 0x00;
0633             ret = 1;
0634             sisusb->con_blanked = 0;
0635             break;
0636         case VESA_VSYNC_SUSPEND + 1:
0637             sr1   = 0x20;
0638             cr17  = 0x80;
0639             pmreg = 0x80;
0640             cr63  = 0x40;
0641             break;
0642         case VESA_HSYNC_SUSPEND + 1:
0643             sr1   = 0x20;
0644             cr17  = 0x80;
0645             pmreg = 0x40;
0646             cr63  = 0x40;
0647             break;
0648         case VESA_POWERDOWN + 1:
0649             sr1   = 0x20;
0650             cr17  = 0x00;
0651             pmreg = 0xc0;
0652             cr63  = 0x40;
0653             break;
0654         default:
0655             mutex_unlock(&sisusb->lock);
0656             return -EINVAL;
0657         }
0658 
0659         sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1);
0660         sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17);
0661         sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg);
0662         sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63);
0663 
0664     }
0665 
0666     mutex_unlock(&sisusb->lock);
0667 
0668     return ret;
0669 }
0670 
0671 /* interface routine */
0672 static void
0673 sisusbcon_scrolldelta(struct vc_data *c, int lines)
0674 {
0675     struct sisusb_usb_data *sisusb;
0676 
0677     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0678     if (!sisusb)
0679         return;
0680 
0681     /* sisusb->lock is down */
0682 
0683     if (sisusb_is_inactive(c, sisusb)) {
0684         mutex_unlock(&sisusb->lock);
0685         return;
0686     }
0687 
0688     vc_scrolldelta_helper(c, lines, sisusb->con_rolled_over,
0689             (void *)sisusb->scrbuf, sisusb->scrbuf_size);
0690 
0691     sisusbcon_set_start_address(sisusb, c);
0692 
0693     mutex_unlock(&sisusb->lock);
0694 }
0695 
0696 /* Interface routine */
0697 static void
0698 sisusbcon_cursor(struct vc_data *c, int mode)
0699 {
0700     struct sisusb_usb_data *sisusb;
0701     int from, to, baseline;
0702 
0703     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0704     if (!sisusb)
0705         return;
0706 
0707     /* sisusb->lock is down */
0708 
0709     if (sisusb_is_inactive(c, sisusb)) {
0710         mutex_unlock(&sisusb->lock);
0711         return;
0712     }
0713 
0714     if (c->vc_origin != c->vc_visible_origin) {
0715         c->vc_visible_origin = c->vc_origin;
0716         sisusbcon_set_start_address(sisusb, c);
0717     }
0718 
0719     if (mode == CM_ERASE) {
0720         sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20);
0721         sisusb->sisusb_cursor_size_to = -1;
0722         mutex_unlock(&sisusb->lock);
0723         return;
0724     }
0725 
0726     sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2);
0727 
0728     baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2);
0729 
0730     switch (CUR_SIZE(c->vc_cursor_type)) {
0731         case CUR_BLOCK:     from = 1;
0732                     to   = c->vc_font.height;
0733                     break;
0734         case CUR_TWO_THIRDS:    from = c->vc_font.height / 3;
0735                     to   = baseline;
0736                     break;
0737         case CUR_LOWER_HALF:    from = c->vc_font.height / 2;
0738                     to   = baseline;
0739                     break;
0740         case CUR_LOWER_THIRD:   from = (c->vc_font.height * 2) / 3;
0741                     to   = baseline;
0742                     break;
0743         case CUR_NONE:      from = 31;
0744                     to = 30;
0745                     break;
0746         default:
0747         case CUR_UNDERLINE: from = baseline - 1;
0748                     to   = baseline;
0749                     break;
0750     }
0751 
0752     if (sisusb->sisusb_cursor_size_from != from ||
0753         sisusb->sisusb_cursor_size_to != to) {
0754 
0755         sisusb_setidxreg(sisusb, SISCR, 0x0a, from);
0756         sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to);
0757 
0758         sisusb->sisusb_cursor_size_from = from;
0759         sisusb->sisusb_cursor_size_to   = to;
0760     }
0761 
0762     mutex_unlock(&sisusb->lock);
0763 }
0764 
0765 static bool
0766 sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
0767         unsigned int t, unsigned int b, enum con_scroll dir,
0768         unsigned int lines)
0769 {
0770     int cols = sisusb->sisusb_num_columns;
0771     int length = ((b - t) * cols) * 2;
0772     u16 eattr = c->vc_video_erase_char;
0773 
0774     /* sisusb->lock is down */
0775 
0776     /* Scroll an area which does not match the
0777      * visible screen's dimensions. This needs
0778      * to be done separately, as it does not
0779      * use hardware panning.
0780      */
0781 
0782     switch (dir) {
0783 
0784         case SM_UP:
0785             memmove(sisusb_vaddr(sisusb, c, 0, t),
0786                        sisusb_vaddr(sisusb, c, 0, t + lines),
0787                        (b - t - lines) * cols * 2);
0788             sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, b - lines),
0789                     eattr, lines * cols * 2);
0790             break;
0791 
0792         case SM_DOWN:
0793             memmove(sisusb_vaddr(sisusb, c, 0, t + lines),
0794                        sisusb_vaddr(sisusb, c, 0, t),
0795                        (b - t - lines) * cols * 2);
0796             sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, t), eattr,
0797                       lines * cols * 2);
0798             break;
0799     }
0800 
0801     sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, 0, t),
0802             sisusb_haddr(sisusb, c, 0, t), length);
0803 
0804     mutex_unlock(&sisusb->lock);
0805 
0806     return true;
0807 }
0808 
0809 /* Interface routine */
0810 static bool
0811 sisusbcon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
0812         enum con_scroll dir, unsigned int lines)
0813 {
0814     struct sisusb_usb_data *sisusb;
0815     u16 eattr = c->vc_video_erase_char;
0816     int copyall = 0;
0817     unsigned long oldorigin;
0818     unsigned int delta = lines * c->vc_size_row;
0819 
0820     /* Returning != 0 means we have done the scrolling successfully.
0821      * Returning 0 makes vt do the scrolling on its own.
0822      * Note that con_scroll is only called if the console is
0823      * visible. In that case, the origin should be our buffer,
0824      * not the vt's private one.
0825      */
0826 
0827     if (!lines)
0828         return true;
0829 
0830     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0831     if (!sisusb)
0832         return false;
0833 
0834     /* sisusb->lock is down */
0835 
0836     if (sisusb_is_inactive(c, sisusb)) {
0837         mutex_unlock(&sisusb->lock);
0838         return false;
0839     }
0840 
0841     /* Special case */
0842     if (t || b != c->vc_rows)
0843         return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines);
0844 
0845     if (c->vc_origin != c->vc_visible_origin) {
0846         c->vc_visible_origin = c->vc_origin;
0847         sisusbcon_set_start_address(sisusb, c);
0848     }
0849 
0850     /* limit amount to maximum realistic size */
0851     if (lines > c->vc_rows)
0852         lines = c->vc_rows;
0853 
0854     oldorigin = c->vc_origin;
0855 
0856     switch (dir) {
0857 
0858     case SM_UP:
0859 
0860         if (c->vc_scr_end + delta >=
0861                 sisusb->scrbuf + sisusb->scrbuf_size) {
0862             memcpy((u16 *)sisusb->scrbuf,
0863                       (u16 *)(oldorigin + delta),
0864                       c->vc_screenbuf_size - delta);
0865             c->vc_origin = sisusb->scrbuf;
0866             sisusb->con_rolled_over = oldorigin - sisusb->scrbuf;
0867             copyall = 1;
0868         } else
0869             c->vc_origin += delta;
0870 
0871         sisusbcon_memsetw(
0872             (u16 *)(c->vc_origin + c->vc_screenbuf_size - delta),
0873                     eattr, delta);
0874 
0875         break;
0876 
0877     case SM_DOWN:
0878 
0879         if (oldorigin - delta < sisusb->scrbuf) {
0880             memmove((void *)sisusb->scrbuf + sisusb->scrbuf_size -
0881                     c->vc_screenbuf_size + delta,
0882                     (u16 *)oldorigin,
0883                     c->vc_screenbuf_size - delta);
0884             c->vc_origin = sisusb->scrbuf +
0885                     sisusb->scrbuf_size -
0886                     c->vc_screenbuf_size;
0887             sisusb->con_rolled_over = 0;
0888             copyall = 1;
0889         } else
0890             c->vc_origin -= delta;
0891 
0892         c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
0893 
0894         scr_memsetw((u16 *)(c->vc_origin), eattr, delta);
0895 
0896         break;
0897     }
0898 
0899     if (copyall)
0900         sisusb_copy_memory(sisusb,
0901             (u8 *)c->vc_origin,
0902             sisusb_haddr(sisusb, c, 0, 0),
0903             c->vc_screenbuf_size);
0904     else if (dir == SM_UP)
0905         sisusb_copy_memory(sisusb,
0906             (u8 *)c->vc_origin + c->vc_screenbuf_size - delta,
0907             sisusb_haddr(sisusb, c, 0, 0) +
0908                     c->vc_screenbuf_size - delta,
0909             delta);
0910     else
0911         sisusb_copy_memory(sisusb,
0912             (u8 *)c->vc_origin,
0913             sisusb_haddr(sisusb, c, 0, 0),
0914             delta);
0915 
0916     c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
0917     c->vc_visible_origin = c->vc_origin;
0918 
0919     sisusbcon_set_start_address(sisusb, c);
0920 
0921     c->vc_pos = c->vc_pos - oldorigin + c->vc_origin;
0922 
0923     mutex_unlock(&sisusb->lock);
0924 
0925     return true;
0926 }
0927 
0928 /* Interface routine */
0929 static int
0930 sisusbcon_set_origin(struct vc_data *c)
0931 {
0932     struct sisusb_usb_data *sisusb;
0933 
0934     /* Returning != 0 means we were successful.
0935      * Returning 0 will vt make to use its own
0936      *  screenbuffer as the origin.
0937      */
0938 
0939     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0940     if (!sisusb)
0941         return 0;
0942 
0943     /* sisusb->lock is down */
0944 
0945     if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
0946         mutex_unlock(&sisusb->lock);
0947         return 0;
0948     }
0949 
0950     c->vc_origin = c->vc_visible_origin = sisusb->scrbuf;
0951 
0952     sisusbcon_set_start_address(sisusb, c);
0953 
0954     sisusb->con_rolled_over = 0;
0955 
0956     mutex_unlock(&sisusb->lock);
0957 
0958     return true;
0959 }
0960 
0961 /* Interface routine */
0962 static int
0963 sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows,
0964          unsigned int user)
0965 {
0966     struct sisusb_usb_data *sisusb;
0967     int fh;
0968 
0969     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
0970     if (!sisusb)
0971         return -ENODEV;
0972 
0973     fh = sisusb->current_font_height;
0974 
0975     mutex_unlock(&sisusb->lock);
0976 
0977     /* We are quite unflexible as regards resizing. The vt code
0978      * handles sizes where the line length isn't equal the pitch
0979      * quite badly. As regards the rows, our panning tricks only
0980      * work well if the number of rows equals the visible number
0981      * of rows.
0982      */
0983 
0984     if (newcols != 80 || c->vc_scan_lines / fh != newrows)
0985         return -EINVAL;
0986 
0987     return 0;
0988 }
0989 
0990 int
0991 sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
0992             u8 *arg, int cmapsz, int ch512, int dorecalc,
0993             struct vc_data *c, int fh, int uplock)
0994 {
0995     int font_select = 0x00, i, err = 0;
0996     u32 offset = 0;
0997     u8 dummy;
0998 
0999     /* sisusb->lock is down */
1000 
1001     /*
1002      * The default font is kept in slot 0.
1003      * A user font is loaded in slot 2 (256 ch)
1004      * or 2+3 (512 ch).
1005      */
1006 
1007     if ((slot != 0 && slot != 2) || !fh) {
1008         if (uplock)
1009             mutex_unlock(&sisusb->lock);
1010         return -EINVAL;
1011     }
1012 
1013     if (set)
1014         sisusb->font_slot = slot;
1015 
1016     /* Default font is always 256 */
1017     if (slot == 0)
1018         ch512 = 0;
1019     else
1020         offset = 4 * cmapsz;
1021 
1022     font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a);
1023 
1024     err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
1025     err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */
1026     err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */
1027     err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */
1028 
1029     if (err)
1030         goto font_op_error;
1031 
1032     err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */
1033     err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */
1034     err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */
1035 
1036     if (err)
1037         goto font_op_error;
1038 
1039     if (arg) {
1040         if (set)
1041             for (i = 0; i < cmapsz; i++) {
1042                 err |= sisusb_writeb(sisusb,
1043                     sisusb->vrambase + offset + i,
1044                     arg[i]);
1045                 if (err)
1046                     break;
1047             }
1048         else
1049             for (i = 0; i < cmapsz; i++) {
1050                 err |= sisusb_readb(sisusb,
1051                     sisusb->vrambase + offset + i,
1052                     &arg[i]);
1053                 if (err)
1054                     break;
1055             }
1056 
1057         /*
1058          * In 512-character mode, the character map is not contiguous if
1059          * we want to remain EGA compatible -- which we do
1060          */
1061 
1062         if (ch512) {
1063             if (set)
1064                 for (i = 0; i < cmapsz; i++) {
1065                     err |= sisusb_writeb(sisusb,
1066                         sisusb->vrambase + offset +
1067                             (2 * cmapsz) + i,
1068                         arg[cmapsz + i]);
1069                     if (err)
1070                         break;
1071                 }
1072             else
1073                 for (i = 0; i < cmapsz; i++) {
1074                     err |= sisusb_readb(sisusb,
1075                         sisusb->vrambase + offset +
1076                             (2 * cmapsz) + i,
1077                         &arg[cmapsz + i]);
1078                     if (err)
1079                         break;
1080                 }
1081         }
1082     }
1083 
1084     if (err)
1085         goto font_op_error;
1086 
1087     err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
1088     err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */
1089     err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */
1090     if (set)
1091         sisusb_setidxreg(sisusb, SISSR, 0x03, font_select);
1092     err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */
1093 
1094     if (err)
1095         goto font_op_error;
1096 
1097     err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */
1098     err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */
1099     err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */
1100 
1101     if (err)
1102         goto font_op_error;
1103 
1104     if ((set) && (ch512 != sisusb->current_font_512)) {
1105 
1106         /* Font is shared among all our consoles.
1107          * And so is the hi_font_mask.
1108          */
1109         for (i = 0; i < MAX_NR_CONSOLES; i++) {
1110             struct vc_data *d = vc_cons[i].d;
1111             if (d && d->vc_sw == &sisusb_con)
1112                 d->vc_hi_font_mask = ch512 ? 0x0800 : 0;
1113         }
1114 
1115         sisusb->current_font_512 = ch512;
1116 
1117         /* color plane enable register:
1118             256-char: enable intensity bit
1119             512-char: disable intensity bit */
1120         sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1121         sisusb_setreg(sisusb, SISAR, 0x12);
1122         sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f);
1123 
1124         sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1125         sisusb_setreg(sisusb, SISAR, 0x20);
1126         sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1127     }
1128 
1129     if (dorecalc) {
1130 
1131         /*
1132          * Adjust the screen to fit a font of a certain height
1133          */
1134 
1135         unsigned char ovr, vde, fsr;
1136         int rows = 0, maxscan = 0;
1137 
1138         if (c) {
1139 
1140             /* Number of video rows */
1141             rows = c->vc_scan_lines / fh;
1142             /* Scan lines to actually display-1 */
1143             maxscan = rows * fh - 1;
1144 
1145             /*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n",
1146                 rows, maxscan, fh, c->vc_scan_lines);*/
1147 
1148             sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr);
1149             vde = maxscan & 0xff;
1150             ovr = (ovr & 0xbd) |
1151                   ((maxscan & 0x100) >> 7) |
1152                   ((maxscan & 0x200) >> 3);
1153             sisusb_setidxreg(sisusb, SISCR, 0x07, ovr);
1154             sisusb_setidxreg(sisusb, SISCR, 0x12, vde);
1155 
1156         }
1157 
1158         sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr);
1159         fsr = (fsr & 0xe0) | (fh - 1);
1160         sisusb_setidxreg(sisusb, SISCR, 0x09, fsr);
1161         sisusb->current_font_height = fh;
1162 
1163         sisusb->sisusb_cursor_size_from = -1;
1164         sisusb->sisusb_cursor_size_to   = -1;
1165 
1166     }
1167 
1168     if (uplock)
1169         mutex_unlock(&sisusb->lock);
1170 
1171     if (dorecalc && c) {
1172         int rows = c->vc_scan_lines / fh;
1173 
1174         /* Now adjust our consoles' size */
1175 
1176         for (i = 0; i < MAX_NR_CONSOLES; i++) {
1177             struct vc_data *vc = vc_cons[i].d;
1178 
1179             if (vc && vc->vc_sw == &sisusb_con) {
1180                 if (con_is_visible(vc)) {
1181                     vc->vc_sw->con_cursor(vc, CM_DRAW);
1182                 }
1183                 vc->vc_font.height = fh;
1184                 vc_resize(vc, 0, rows);
1185             }
1186         }
1187     }
1188 
1189     return 0;
1190 
1191 font_op_error:
1192     if (uplock)
1193         mutex_unlock(&sisusb->lock);
1194 
1195     return -EIO;
1196 }
1197 
1198 /* Interface routine */
1199 static int
1200 sisusbcon_font_set(struct vc_data *c, struct console_font *font,
1201            unsigned int flags)
1202 {
1203     struct sisusb_usb_data *sisusb;
1204     unsigned charcount = font->charcount;
1205 
1206     if (font->width != 8 || (charcount != 256 && charcount != 512))
1207         return -EINVAL;
1208 
1209     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
1210     if (!sisusb)
1211         return -ENODEV;
1212 
1213     /* sisusb->lock is down */
1214 
1215     /* Save the user-provided font into a buffer. This
1216      * is used for restoring text mode after quitting
1217      * from X and for the con_getfont routine.
1218      */
1219     if (sisusb->font_backup) {
1220         if (sisusb->font_backup_size < charcount) {
1221             vfree(sisusb->font_backup);
1222             sisusb->font_backup = NULL;
1223         }
1224     }
1225 
1226     if (!sisusb->font_backup)
1227         sisusb->font_backup = vmalloc(array_size(charcount, 32));
1228 
1229     if (sisusb->font_backup) {
1230         memcpy(sisusb->font_backup, font->data, array_size(charcount, 32));
1231         sisusb->font_backup_size = charcount;
1232         sisusb->font_backup_height = font->height;
1233         sisusb->font_backup_512 = (charcount == 512) ? 1 : 0;
1234     }
1235 
1236     /* do_font_op ups sisusb->lock */
1237 
1238     return sisusbcon_do_font_op(sisusb, 1, 2, font->data,
1239             8192, (charcount == 512),
1240             (!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0,
1241             c, font->height, 1);
1242 }
1243 
1244 /* Interface routine */
1245 static int
1246 sisusbcon_font_get(struct vc_data *c, struct console_font *font)
1247 {
1248     struct sisusb_usb_data *sisusb;
1249 
1250     sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
1251     if (!sisusb)
1252         return -ENODEV;
1253 
1254     /* sisusb->lock is down */
1255 
1256     font->width = 8;
1257     font->height = c->vc_font.height;
1258     font->charcount = 256;
1259 
1260     if (!font->data) {
1261         mutex_unlock(&sisusb->lock);
1262         return 0;
1263     }
1264 
1265     if (!sisusb->font_backup) {
1266         mutex_unlock(&sisusb->lock);
1267         return -ENODEV;
1268     }
1269 
1270     /* Copy 256 chars only, like vgacon */
1271     memcpy(font->data, sisusb->font_backup, 256 * 32);
1272 
1273     mutex_unlock(&sisusb->lock);
1274 
1275     return 0;
1276 }
1277 
1278 /*
1279  *  The console `switch' structure for the sisusb console
1280  */
1281 
1282 static const struct consw sisusb_con = {
1283     .owner =        THIS_MODULE,
1284     .con_startup =      sisusbcon_startup,
1285     .con_init =     sisusbcon_init,
1286     .con_deinit =       sisusbcon_deinit,
1287     .con_clear =        sisusbcon_clear,
1288     .con_putc =     sisusbcon_putc,
1289     .con_putcs =        sisusbcon_putcs,
1290     .con_cursor =       sisusbcon_cursor,
1291     .con_scroll =       sisusbcon_scroll,
1292     .con_switch =       sisusbcon_switch,
1293     .con_blank =        sisusbcon_blank,
1294     .con_font_set =     sisusbcon_font_set,
1295     .con_font_get =     sisusbcon_font_get,
1296     .con_set_palette =  sisusbcon_set_palette,
1297     .con_scrolldelta =  sisusbcon_scrolldelta,
1298     .con_build_attr =   sisusbcon_build_attr,
1299     .con_invert_region =    sisusbcon_invert_region,
1300     .con_set_origin =   sisusbcon_set_origin,
1301     .con_save_screen =  sisusbcon_save_screen,
1302     .con_resize =       sisusbcon_resize,
1303 };
1304 
1305 /* Our very own dummy console driver */
1306 
1307 static const char *sisusbdummycon_startup(void)
1308 {
1309     return "SISUSBVGADUMMY";
1310 }
1311 
1312 static void sisusbdummycon_init(struct vc_data *vc, int init)
1313 {
1314     vc->vc_can_do_color = 1;
1315     if (init) {
1316     vc->vc_cols = 80;
1317     vc->vc_rows = 25;
1318     } else
1319     vc_resize(vc, 80, 25);
1320 }
1321 
1322 static void sisusbdummycon_deinit(struct vc_data *vc) { }
1323 static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx,
1324                  int height, int width) { }
1325 static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos,
1326                 int xpos) { }
1327 static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s,
1328                  int count, int ypos, int xpos) { }
1329 static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { }
1330 
1331 static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top,
1332                   unsigned int bottom, enum con_scroll dir,
1333                   unsigned int lines)
1334 {
1335     return false;
1336 }
1337 
1338 static int sisusbdummycon_switch(struct vc_data *vc)
1339 {
1340     return 0;
1341 }
1342 
1343 static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch)
1344 {
1345     return 0;
1346 }
1347 
1348 static const struct consw sisusb_dummy_con = {
1349     .owner =        THIS_MODULE,
1350     .con_startup =      sisusbdummycon_startup,
1351     .con_init =     sisusbdummycon_init,
1352     .con_deinit =       sisusbdummycon_deinit,
1353     .con_clear =        sisusbdummycon_clear,
1354     .con_putc =     sisusbdummycon_putc,
1355     .con_putcs =        sisusbdummycon_putcs,
1356     .con_cursor =       sisusbdummycon_cursor,
1357     .con_scroll =       sisusbdummycon_scroll,
1358     .con_switch =       sisusbdummycon_switch,
1359     .con_blank =        sisusbdummycon_blank,
1360 };
1361 
1362 int
1363 sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1364 {
1365     int i, ret;
1366 
1367     mutex_lock(&sisusb->lock);
1368 
1369     /* Erm.. that should not happen */
1370     if (sisusb->haveconsole || !sisusb->SiS_Pr) {
1371         mutex_unlock(&sisusb->lock);
1372         return 1;
1373     }
1374 
1375     sisusb->con_first = first;
1376     sisusb->con_last  = last;
1377 
1378     if (first > last ||
1379         first > MAX_NR_CONSOLES ||
1380         last > MAX_NR_CONSOLES) {
1381         mutex_unlock(&sisusb->lock);
1382         return 1;
1383     }
1384 
1385     /* If gfxcore not initialized or no consoles given, quit graciously */
1386     if (!sisusb->gfxinit || first < 1 || last < 1) {
1387         mutex_unlock(&sisusb->lock);
1388         return 0;
1389     }
1390 
1391     sisusb->sisusb_cursor_loc       = -1;
1392     sisusb->sisusb_cursor_size_from = -1;
1393     sisusb->sisusb_cursor_size_to   = -1;
1394 
1395     /* Set up text mode (and upload  default font) */
1396     if (sisusb_reset_text_mode(sisusb, 1)) {
1397         mutex_unlock(&sisusb->lock);
1398         dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n");
1399         return 1;
1400     }
1401 
1402     /* Initialize some gfx registers */
1403     sisusb_initialize(sisusb);
1404 
1405     for (i = first - 1; i <= last - 1; i++) {
1406         /* Save sisusb for our interface routines */
1407         mysisusbs[i] = sisusb;
1408     }
1409 
1410     /* Initial console setup */
1411     sisusb->sisusb_num_columns = 80;
1412 
1413     /* Use a 32K buffer (matches b8000-bffff area) */
1414     sisusb->scrbuf_size = 32 * 1024;
1415 
1416     /* Allocate screen buffer */
1417     if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
1418         mutex_unlock(&sisusb->lock);
1419         dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n");
1420         return 1;
1421     }
1422 
1423     mutex_unlock(&sisusb->lock);
1424 
1425     /* Now grab the desired console(s) */
1426     console_lock();
1427     ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0);
1428     console_unlock();
1429     if (!ret)
1430         sisusb->haveconsole = 1;
1431     else {
1432         for (i = first - 1; i <= last - 1; i++)
1433             mysisusbs[i] = NULL;
1434     }
1435 
1436     return ret;
1437 }
1438 
1439 void
1440 sisusb_console_exit(struct sisusb_usb_data *sisusb)
1441 {
1442     int i;
1443 
1444     /* This is called if the device is disconnected
1445      * and while disconnect and lock semaphores
1446      * are up. This should be save because we
1447      * can't lose our sisusb any other way but by
1448      * disconnection (and hence, the disconnect
1449      * sema is for protecting all other access
1450      * functions from disconnection, not the
1451      * other way round).
1452      */
1453 
1454     /* Now what do we do in case of disconnection:
1455      * One alternative would be to simply call
1456      * give_up_console(). Nah, not a good idea.
1457      * give_up_console() is obviously buggy as it
1458      * only discards the consw pointer from the
1459      * driver_map, but doesn't adapt vc->vc_sw
1460      * of the affected consoles. Hence, the next
1461      * call to any of the console functions will
1462      * eventually take a trip to oops county.
1463      * Also, give_up_console for some reason
1464      * doesn't decrement our module refcount.
1465      * Instead, we switch our consoles to a private
1466      * dummy console. This, of course, keeps our
1467      * refcount up as well, but it works perfectly.
1468      */
1469 
1470     if (sisusb->haveconsole) {
1471         for (i = 0; i < MAX_NR_CONSOLES; i++)
1472             if (sisusb->havethisconsole[i]) {
1473                 console_lock();
1474                 do_take_over_console(&sisusb_dummy_con, i, i, 0);
1475                 console_unlock();
1476                 /* At this point, con_deinit for all our
1477                  * consoles is executed by do_take_over_console().
1478                  */
1479             }
1480         sisusb->haveconsole = 0;
1481     }
1482 
1483     vfree((void *)sisusb->scrbuf);
1484     sisusb->scrbuf = 0;
1485 
1486     vfree(sisusb->font_backup);
1487     sisusb->font_backup = NULL;
1488 }
1489 
1490 void __init sisusb_init_concode(void)
1491 {
1492     int i;
1493 
1494     for (i = 0; i < MAX_NR_CONSOLES; i++)
1495         mysisusbs[i] = NULL;
1496 }