cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

sisusb_con.c (35841B)


      1// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause)
      2/*
      3 * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
      4 *
      5 * VGA text mode console part
      6 *
      7 * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
      8 *
      9 * If distributed as part of the Linux kernel, this code is licensed under the
     10 * terms of the GPL v2.
     11 *
     12 * Otherwise, the following license terms apply:
     13 *
     14 * * Redistribution and use in source and binary forms, with or without
     15 * * modification, are permitted provided that the following conditions
     16 * * are met:
     17 * * 1) Redistributions of source code must retain the above copyright
     18 * *    notice, this list of conditions and the following disclaimer.
     19 * * 2) Redistributions in binary form must reproduce the above copyright
     20 * *    notice, this list of conditions and the following disclaimer in the
     21 * *    documentation and/or other materials provided with the distribution.
     22 * * 3) The name of the author may not be used to endorse or promote products
     23 * *    derived from this software without specific psisusbr written permission.
     24 * *
     25 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
     26 * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     27 * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     28 * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     29 * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     30 * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     31 * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     32 * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     33 * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     34 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     35 *
     36 * Author: Thomas Winischhofer <thomas@winischhofer.net>
     37 *
     38 * Portions based on vgacon.c which are
     39 *	Created 28 Sep 1997 by Geert Uytterhoeven
     40 *      Rewritten by Martin Mares <mj@ucw.cz>, July 1998
     41 *      based on code Copyright (C) 1991, 1992  Linus Torvalds
     42 *			    1995  Jay Estabrook
     43 *
     44 * A note on using in_atomic() in here: We can't handle console
     45 * calls from non-schedulable context due to our USB-dependend
     46 * nature. For now, this driver just ignores any calls if it
     47 * detects this state.
     48 *
     49 */
     50
     51#include <linux/mutex.h>
     52#include <linux/module.h>
     53#include <linux/kernel.h>
     54#include <linux/signal.h>
     55#include <linux/fs.h>
     56#include <linux/usb.h>
     57#include <linux/tty.h>
     58#include <linux/console.h>
     59#include <linux/string.h>
     60#include <linux/kd.h>
     61#include <linux/init.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/ioport.h>
     67#include <linux/interrupt.h>
     68#include <linux/vmalloc.h>
     69
     70#include "sisusb.h"
     71#include "sisusb_init.h"
     72
     73/* vc_data -> sisusb conversion table */
     74static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES];
     75
     76/* Forward declaration */
     77static const struct consw sisusb_con;
     78
     79static inline void
     80sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
     81{
     82	memset16(s, c, count / 2);
     83}
     84
     85static inline void
     86sisusb_initialize(struct sisusb_usb_data *sisusb)
     87{
     88	/* Reset cursor and start address */
     89	if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00))
     90		return;
     91	if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00))
     92		return;
     93	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00))
     94		return;
     95	sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00);
     96}
     97
     98static inline void
     99sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c)
    100{
    101	sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2;
    102
    103	sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
    104	sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
    105}
    106
    107void
    108sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location)
    109{
    110	if (sisusb->sisusb_cursor_loc == location)
    111		return;
    112
    113	sisusb->sisusb_cursor_loc = location;
    114
    115	/* Hardware bug: Text cursor appears twice or not at all
    116	 * at some positions. Work around it with the cursor skew
    117	 * bits.
    118	 */
    119
    120	if ((location & 0x0007) == 0x0007) {
    121		sisusb->bad_cursor_pos = 1;
    122		location--;
    123		if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20))
    124			return;
    125	} else if (sisusb->bad_cursor_pos) {
    126		if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f))
    127			return;
    128		sisusb->bad_cursor_pos = 0;
    129	}
    130
    131	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8)))
    132		return;
    133	sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff));
    134}
    135
    136static inline struct sisusb_usb_data *
    137sisusb_get_sisusb(unsigned short console)
    138{
    139	return mysisusbs[console];
    140}
    141
    142static inline int
    143sisusb_sisusb_valid(struct sisusb_usb_data *sisusb)
    144{
    145	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev)
    146		return 0;
    147
    148	return 1;
    149}
    150
    151static struct sisusb_usb_data *
    152sisusb_get_sisusb_lock_and_check(unsigned short console)
    153{
    154	struct sisusb_usb_data *sisusb;
    155
    156	/* We can't handle console calls in non-schedulable
    157	 * context due to our locks and the USB transport.
    158	 * So we simply ignore them. This should only affect
    159	 * some calls to printk.
    160	 */
    161	if (in_atomic())
    162		return NULL;
    163
    164	sisusb = sisusb_get_sisusb(console);
    165	if (!sisusb)
    166		return NULL;
    167
    168	mutex_lock(&sisusb->lock);
    169
    170	if (!sisusb_sisusb_valid(sisusb) ||
    171	    !sisusb->havethisconsole[console]) {
    172		mutex_unlock(&sisusb->lock);
    173		return NULL;
    174	}
    175
    176	return sisusb;
    177}
    178
    179static int
    180sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb)
    181{
    182	if (sisusb->is_gfx ||
    183	    sisusb->textmodedestroyed ||
    184	    c->vc_mode != KD_TEXT)
    185		return 1;
    186
    187	return 0;
    188}
    189
    190/* con_startup console interface routine */
    191static const char *
    192sisusbcon_startup(void)
    193{
    194	return "SISUSBCON";
    195}
    196
    197/* con_init console interface routine */
    198static void
    199sisusbcon_init(struct vc_data *c, int init)
    200{
    201	struct sisusb_usb_data *sisusb;
    202	int cols, rows;
    203
    204	/* This is called by do_take_over_console(),
    205	 * ie by us/under our control. It is
    206	 * only called after text mode and fonts
    207	 * are set up/restored.
    208	 */
    209
    210	sisusb = sisusb_get_sisusb(c->vc_num);
    211	if (!sisusb)
    212		return;
    213
    214	mutex_lock(&sisusb->lock);
    215
    216	if (!sisusb_sisusb_valid(sisusb)) {
    217		mutex_unlock(&sisusb->lock);
    218		return;
    219	}
    220
    221	c->vc_can_do_color = 1;
    222
    223	c->vc_complement_mask = 0x7700;
    224
    225	c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0;
    226
    227	sisusb->haveconsole = 1;
    228
    229	sisusb->havethisconsole[c->vc_num] = 1;
    230
    231	/* We only support 640x400 */
    232	c->vc_scan_lines = 400;
    233
    234	c->vc_font.height = sisusb->current_font_height;
    235
    236	/* We only support width = 8 */
    237	cols = 80;
    238	rows = c->vc_scan_lines / c->vc_font.height;
    239
    240	/* Increment usage count for our sisusb.
    241	 * Doing so saves us from upping/downing
    242	 * the disconnect semaphore; we can't
    243	 * lose our sisusb until this is undone
    244	 * in con_deinit. For all other console
    245	 * interface functions, it suffices to
    246	 * use sisusb->lock and do a quick check
    247	 * of sisusb for device disconnection.
    248	 */
    249	kref_get(&sisusb->kref);
    250
    251	if (!*c->vc_uni_pagedir_loc)
    252		con_set_default_unimap(c);
    253
    254	mutex_unlock(&sisusb->lock);
    255
    256	if (init) {
    257		c->vc_cols = cols;
    258		c->vc_rows = rows;
    259	} else
    260		vc_resize(c, cols, rows);
    261}
    262
    263/* con_deinit console interface routine */
    264static void
    265sisusbcon_deinit(struct vc_data *c)
    266{
    267	struct sisusb_usb_data *sisusb;
    268	int i;
    269
    270	/* This is called by do_take_over_console()
    271	 * and others, ie not under our control.
    272	 */
    273
    274	sisusb = sisusb_get_sisusb(c->vc_num);
    275	if (!sisusb)
    276		return;
    277
    278	mutex_lock(&sisusb->lock);
    279
    280	/* Clear ourselves in mysisusbs */
    281	mysisusbs[c->vc_num] = NULL;
    282
    283	sisusb->havethisconsole[c->vc_num] = 0;
    284
    285	/* Free our font buffer if all consoles are gone */
    286	if (sisusb->font_backup) {
    287		for(i = 0; i < MAX_NR_CONSOLES; i++) {
    288			if (sisusb->havethisconsole[c->vc_num])
    289				break;
    290		}
    291		if (i == MAX_NR_CONSOLES) {
    292			vfree(sisusb->font_backup);
    293			sisusb->font_backup = NULL;
    294		}
    295	}
    296
    297	mutex_unlock(&sisusb->lock);
    298
    299	/* decrement the usage count on our sisusb */
    300	kref_put(&sisusb->kref, sisusb_delete);
    301}
    302
    303/* interface routine */
    304static u8
    305sisusbcon_build_attr(struct vc_data *c, u8 color, enum vc_intensity intensity,
    306			    bool blink, bool underline, bool reverse,
    307			    bool unused)
    308{
    309	u8 attr = color;
    310
    311	if (underline)
    312		attr = (attr & 0xf0) | c->vc_ulcolor;
    313	else if (intensity == VCI_HALF_BRIGHT)
    314		attr = (attr & 0xf0) | c->vc_halfcolor;
    315
    316	if (reverse)
    317		attr = ((attr) & 0x88) |
    318		       ((((attr) >> 4) |
    319		       ((attr) << 4)) & 0x77);
    320
    321	if (blink)
    322		attr ^= 0x80;
    323
    324	if (intensity == VCI_BOLD)
    325		attr ^= 0x08;
    326
    327	return attr;
    328}
    329
    330/* Interface routine */
    331static void
    332sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count)
    333{
    334	/* Invert a region. This is called with a pointer
    335	 * to the console's internal screen buffer. So we
    336	 * simply do the inversion there and rely on
    337	 * a call to putc(s) to update the real screen.
    338	 */
    339
    340	while (count--) {
    341		u16 a = *p;
    342
    343		*p++ = ((a) & 0x88ff)        |
    344		       (((a) & 0x7000) >> 4) |
    345		       (((a) & 0x0700) << 4);
    346	}
    347}
    348
    349static inline void *sisusb_vaddr(const struct sisusb_usb_data *sisusb,
    350		const struct vc_data *c, unsigned int x, unsigned int y)
    351{
    352	return (u16 *)c->vc_origin + y * sisusb->sisusb_num_columns + x;
    353}
    354
    355static inline unsigned long sisusb_haddr(const struct sisusb_usb_data *sisusb,
    356	      const struct vc_data *c, unsigned int x, unsigned int y)
    357{
    358	unsigned long offset = c->vc_origin - sisusb->scrbuf;
    359
    360	/* 2 bytes per each character */
    361	offset += 2 * (y * sisusb->sisusb_num_columns + x);
    362
    363	return sisusb->vrambase + offset;
    364}
    365
    366/* Interface routine */
    367static void
    368sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
    369{
    370	struct sisusb_usb_data *sisusb;
    371
    372	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    373	if (!sisusb)
    374		return;
    375
    376	/* sisusb->lock is down */
    377	if (sisusb_is_inactive(c, sisusb)) {
    378		mutex_unlock(&sisusb->lock);
    379		return;
    380	}
    381
    382	sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
    383				sisusb_haddr(sisusb, c, x, y), 2);
    384
    385	mutex_unlock(&sisusb->lock);
    386}
    387
    388/* Interface routine */
    389static void
    390sisusbcon_putcs(struct vc_data *c, const unsigned short *s,
    391		         int count, int y, int x)
    392{
    393	struct sisusb_usb_data *sisusb;
    394
    395	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    396	if (!sisusb)
    397		return;
    398
    399	/* sisusb->lock is down */
    400
    401	/* Need to put the characters into the buffer ourselves,
    402	 * because the vt does this AFTER calling us.
    403	 */
    404
    405	memcpy(sisusb_vaddr(sisusb, c, x, y), s, count * 2);
    406
    407	if (sisusb_is_inactive(c, sisusb)) {
    408		mutex_unlock(&sisusb->lock);
    409		return;
    410	}
    411
    412	sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
    413			sisusb_haddr(sisusb, c, x, y), count * 2);
    414
    415	mutex_unlock(&sisusb->lock);
    416}
    417
    418/* Interface routine */
    419static void
    420sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width)
    421{
    422	struct sisusb_usb_data *sisusb;
    423	u16 eattr = c->vc_video_erase_char;
    424	int i, length, cols;
    425	u16 *dest;
    426
    427	if (width <= 0 || height <= 0)
    428		return;
    429
    430	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    431	if (!sisusb)
    432		return;
    433
    434	/* sisusb->lock is down */
    435
    436	/* Need to clear buffer ourselves, because the vt does
    437	 * this AFTER calling us.
    438	 */
    439
    440	dest = sisusb_vaddr(sisusb, c, x, y);
    441
    442	cols = sisusb->sisusb_num_columns;
    443
    444	if (width > cols)
    445		width = cols;
    446
    447	if (x == 0 && width >= c->vc_cols) {
    448
    449		sisusbcon_memsetw(dest, eattr, height * cols * 2);
    450
    451	} else {
    452
    453		for (i = height; i > 0; i--, dest += cols)
    454			sisusbcon_memsetw(dest, eattr, width * 2);
    455
    456	}
    457
    458	if (sisusb_is_inactive(c, sisusb)) {
    459		mutex_unlock(&sisusb->lock);
    460		return;
    461	}
    462
    463	length = ((height * cols) - x - (cols - width - x)) * 2;
    464
    465
    466	sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y),
    467			sisusb_haddr(sisusb, c, x, y), length);
    468
    469	mutex_unlock(&sisusb->lock);
    470}
    471
    472/* interface routine */
    473static int
    474sisusbcon_switch(struct vc_data *c)
    475{
    476	struct sisusb_usb_data *sisusb;
    477	int length;
    478
    479	/* Returnvalue 0 means we have fully restored screen,
    480	 *	and vt doesn't need to call do_update_region().
    481	 * Returnvalue != 0 naturally means the opposite.
    482	 */
    483
    484	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    485	if (!sisusb)
    486		return 0;
    487
    488	/* sisusb->lock is down */
    489
    490	/* Don't write to screen if in gfx mode */
    491	if (sisusb_is_inactive(c, sisusb)) {
    492		mutex_unlock(&sisusb->lock);
    493		return 0;
    494	}
    495
    496	/* That really should not happen. It would mean we are
    497	 * being called while the vc is using its private buffer
    498	 * as origin.
    499	 */
    500	if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
    501		mutex_unlock(&sisusb->lock);
    502		dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n");
    503		return 0;
    504	}
    505
    506	/* Check that we don't copy too much */
    507	length = min((int)c->vc_screenbuf_size,
    508			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
    509
    510	/* Restore the screen contents */
    511	memcpy((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, length);
    512
    513	sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
    514			sisusb_haddr(sisusb, c, 0, 0), length);
    515
    516	mutex_unlock(&sisusb->lock);
    517
    518	return 0;
    519}
    520
    521/* interface routine */
    522static void
    523sisusbcon_save_screen(struct vc_data *c)
    524{
    525	struct sisusb_usb_data *sisusb;
    526	int length;
    527
    528	/* Save the current screen contents to vc's private
    529	 * buffer.
    530	 */
    531
    532	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    533	if (!sisusb)
    534		return;
    535
    536	/* sisusb->lock is down */
    537
    538	if (sisusb_is_inactive(c, sisusb)) {
    539		mutex_unlock(&sisusb->lock);
    540		return;
    541	}
    542
    543	/* Check that we don't copy too much */
    544	length = min((int)c->vc_screenbuf_size,
    545			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
    546
    547	/* Save the screen contents to vc's private buffer */
    548	memcpy((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin, length);
    549
    550	mutex_unlock(&sisusb->lock);
    551}
    552
    553/* interface routine */
    554static void
    555sisusbcon_set_palette(struct vc_data *c, const unsigned char *table)
    556{
    557	struct sisusb_usb_data *sisusb;
    558	int i, j;
    559
    560	/* Return value not used by vt */
    561
    562	if (!con_is_visible(c))
    563		return;
    564
    565	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    566	if (!sisusb)
    567		return;
    568
    569	/* sisusb->lock is down */
    570
    571	if (sisusb_is_inactive(c, sisusb)) {
    572		mutex_unlock(&sisusb->lock);
    573		return;
    574	}
    575
    576	for (i = j = 0; i < 16; i++) {
    577		if (sisusb_setreg(sisusb, SISCOLIDX, table[i]))
    578			break;
    579		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
    580			break;
    581		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
    582			break;
    583		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
    584			break;
    585	}
    586
    587	mutex_unlock(&sisusb->lock);
    588}
    589
    590/* interface routine */
    591static int
    592sisusbcon_blank(struct vc_data *c, int blank, int mode_switch)
    593{
    594	struct sisusb_usb_data *sisusb;
    595	u8 sr1, cr17, pmreg, cr63;
    596	int ret = 0;
    597
    598	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    599	if (!sisusb)
    600		return 0;
    601
    602	/* sisusb->lock is down */
    603
    604	if (mode_switch)
    605		sisusb->is_gfx = blank ? 1 : 0;
    606
    607	if (sisusb_is_inactive(c, sisusb)) {
    608		mutex_unlock(&sisusb->lock);
    609		return 0;
    610	}
    611
    612	switch (blank) {
    613
    614	case 1:		/* Normal blanking: Clear screen */
    615	case -1:
    616		sisusbcon_memsetw((u16 *)c->vc_origin,
    617				c->vc_video_erase_char,
    618				c->vc_screenbuf_size);
    619		sisusb_copy_memory(sisusb, (u8 *)c->vc_origin,
    620				sisusb_haddr(sisusb, c, 0, 0),
    621				c->vc_screenbuf_size);
    622		sisusb->con_blanked = 1;
    623		ret = 1;
    624		break;
    625
    626	default:	/* VESA blanking */
    627		switch (blank) {
    628		case 0: /* Unblank */
    629			sr1   = 0x00;
    630			cr17  = 0x80;
    631			pmreg = 0x00;
    632			cr63  = 0x00;
    633			ret = 1;
    634			sisusb->con_blanked = 0;
    635			break;
    636		case VESA_VSYNC_SUSPEND + 1:
    637			sr1   = 0x20;
    638			cr17  = 0x80;
    639			pmreg = 0x80;
    640			cr63  = 0x40;
    641			break;
    642		case VESA_HSYNC_SUSPEND + 1:
    643			sr1   = 0x20;
    644			cr17  = 0x80;
    645			pmreg = 0x40;
    646			cr63  = 0x40;
    647			break;
    648		case VESA_POWERDOWN + 1:
    649			sr1   = 0x20;
    650			cr17  = 0x00;
    651			pmreg = 0xc0;
    652			cr63  = 0x40;
    653			break;
    654		default:
    655			mutex_unlock(&sisusb->lock);
    656			return -EINVAL;
    657		}
    658
    659		sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1);
    660		sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17);
    661		sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg);
    662		sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63);
    663
    664	}
    665
    666	mutex_unlock(&sisusb->lock);
    667
    668	return ret;
    669}
    670
    671/* interface routine */
    672static void
    673sisusbcon_scrolldelta(struct vc_data *c, int lines)
    674{
    675	struct sisusb_usb_data *sisusb;
    676
    677	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    678	if (!sisusb)
    679		return;
    680
    681	/* sisusb->lock is down */
    682
    683	if (sisusb_is_inactive(c, sisusb)) {
    684		mutex_unlock(&sisusb->lock);
    685		return;
    686	}
    687
    688	vc_scrolldelta_helper(c, lines, sisusb->con_rolled_over,
    689			(void *)sisusb->scrbuf, sisusb->scrbuf_size);
    690
    691	sisusbcon_set_start_address(sisusb, c);
    692
    693	mutex_unlock(&sisusb->lock);
    694}
    695
    696/* Interface routine */
    697static void
    698sisusbcon_cursor(struct vc_data *c, int mode)
    699{
    700	struct sisusb_usb_data *sisusb;
    701	int from, to, baseline;
    702
    703	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    704	if (!sisusb)
    705		return;
    706
    707	/* sisusb->lock is down */
    708
    709	if (sisusb_is_inactive(c, sisusb)) {
    710		mutex_unlock(&sisusb->lock);
    711		return;
    712	}
    713
    714	if (c->vc_origin != c->vc_visible_origin) {
    715		c->vc_visible_origin = c->vc_origin;
    716		sisusbcon_set_start_address(sisusb, c);
    717	}
    718
    719	if (mode == CM_ERASE) {
    720		sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20);
    721		sisusb->sisusb_cursor_size_to = -1;
    722		mutex_unlock(&sisusb->lock);
    723		return;
    724	}
    725
    726	sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2);
    727
    728	baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2);
    729
    730	switch (CUR_SIZE(c->vc_cursor_type)) {
    731		case CUR_BLOCK:		from = 1;
    732					to   = c->vc_font.height;
    733					break;
    734		case CUR_TWO_THIRDS:	from = c->vc_font.height / 3;
    735					to   = baseline;
    736					break;
    737		case CUR_LOWER_HALF:	from = c->vc_font.height / 2;
    738					to   = baseline;
    739					break;
    740		case CUR_LOWER_THIRD:	from = (c->vc_font.height * 2) / 3;
    741					to   = baseline;
    742					break;
    743		case CUR_NONE:		from = 31;
    744					to = 30;
    745					break;
    746		default:
    747		case CUR_UNDERLINE:	from = baseline - 1;
    748					to   = baseline;
    749					break;
    750	}
    751
    752	if (sisusb->sisusb_cursor_size_from != from ||
    753	    sisusb->sisusb_cursor_size_to != to) {
    754
    755		sisusb_setidxreg(sisusb, SISCR, 0x0a, from);
    756		sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to);
    757
    758		sisusb->sisusb_cursor_size_from = from;
    759		sisusb->sisusb_cursor_size_to   = to;
    760	}
    761
    762	mutex_unlock(&sisusb->lock);
    763}
    764
    765static bool
    766sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
    767		unsigned int t, unsigned int b, enum con_scroll dir,
    768		unsigned int lines)
    769{
    770	int cols = sisusb->sisusb_num_columns;
    771	int length = ((b - t) * cols) * 2;
    772	u16 eattr = c->vc_video_erase_char;
    773
    774	/* sisusb->lock is down */
    775
    776	/* Scroll an area which does not match the
    777	 * visible screen's dimensions. This needs
    778	 * to be done separately, as it does not
    779	 * use hardware panning.
    780	 */
    781
    782	switch (dir) {
    783
    784		case SM_UP:
    785			memmove(sisusb_vaddr(sisusb, c, 0, t),
    786					   sisusb_vaddr(sisusb, c, 0, t + lines),
    787					   (b - t - lines) * cols * 2);
    788			sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, b - lines),
    789					eattr, lines * cols * 2);
    790			break;
    791
    792		case SM_DOWN:
    793			memmove(sisusb_vaddr(sisusb, c, 0, t + lines),
    794					   sisusb_vaddr(sisusb, c, 0, t),
    795					   (b - t - lines) * cols * 2);
    796			sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, t), eattr,
    797					  lines * cols * 2);
    798			break;
    799	}
    800
    801	sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, 0, t),
    802			sisusb_haddr(sisusb, c, 0, t), length);
    803
    804	mutex_unlock(&sisusb->lock);
    805
    806	return true;
    807}
    808
    809/* Interface routine */
    810static bool
    811sisusbcon_scroll(struct vc_data *c, unsigned int t, unsigned int b,
    812		enum con_scroll dir, unsigned int lines)
    813{
    814	struct sisusb_usb_data *sisusb;
    815	u16 eattr = c->vc_video_erase_char;
    816	int copyall = 0;
    817	unsigned long oldorigin;
    818	unsigned int delta = lines * c->vc_size_row;
    819
    820	/* Returning != 0 means we have done the scrolling successfully.
    821	 * Returning 0 makes vt do the scrolling on its own.
    822	 * Note that con_scroll is only called if the console is
    823	 * visible. In that case, the origin should be our buffer,
    824	 * not the vt's private one.
    825	 */
    826
    827	if (!lines)
    828		return true;
    829
    830	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    831	if (!sisusb)
    832		return false;
    833
    834	/* sisusb->lock is down */
    835
    836	if (sisusb_is_inactive(c, sisusb)) {
    837		mutex_unlock(&sisusb->lock);
    838		return false;
    839	}
    840
    841	/* Special case */
    842	if (t || b != c->vc_rows)
    843		return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines);
    844
    845	if (c->vc_origin != c->vc_visible_origin) {
    846		c->vc_visible_origin = c->vc_origin;
    847		sisusbcon_set_start_address(sisusb, c);
    848	}
    849
    850	/* limit amount to maximum realistic size */
    851	if (lines > c->vc_rows)
    852		lines = c->vc_rows;
    853
    854	oldorigin = c->vc_origin;
    855
    856	switch (dir) {
    857
    858	case SM_UP:
    859
    860		if (c->vc_scr_end + delta >=
    861				sisusb->scrbuf + sisusb->scrbuf_size) {
    862			memcpy((u16 *)sisusb->scrbuf,
    863					  (u16 *)(oldorigin + delta),
    864					  c->vc_screenbuf_size - delta);
    865			c->vc_origin = sisusb->scrbuf;
    866			sisusb->con_rolled_over = oldorigin - sisusb->scrbuf;
    867			copyall = 1;
    868		} else
    869			c->vc_origin += delta;
    870
    871		sisusbcon_memsetw(
    872			(u16 *)(c->vc_origin + c->vc_screenbuf_size - delta),
    873					eattr, delta);
    874
    875		break;
    876
    877	case SM_DOWN:
    878
    879		if (oldorigin - delta < sisusb->scrbuf) {
    880			memmove((void *)sisusb->scrbuf + sisusb->scrbuf_size -
    881					c->vc_screenbuf_size + delta,
    882					(u16 *)oldorigin,
    883					c->vc_screenbuf_size - delta);
    884			c->vc_origin = sisusb->scrbuf +
    885					sisusb->scrbuf_size -
    886					c->vc_screenbuf_size;
    887			sisusb->con_rolled_over = 0;
    888			copyall = 1;
    889		} else
    890			c->vc_origin -= delta;
    891
    892		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
    893
    894		scr_memsetw((u16 *)(c->vc_origin), eattr, delta);
    895
    896		break;
    897	}
    898
    899	if (copyall)
    900		sisusb_copy_memory(sisusb,
    901			(u8 *)c->vc_origin,
    902			sisusb_haddr(sisusb, c, 0, 0),
    903			c->vc_screenbuf_size);
    904	else if (dir == SM_UP)
    905		sisusb_copy_memory(sisusb,
    906			(u8 *)c->vc_origin + c->vc_screenbuf_size - delta,
    907			sisusb_haddr(sisusb, c, 0, 0) +
    908					c->vc_screenbuf_size - delta,
    909			delta);
    910	else
    911		sisusb_copy_memory(sisusb,
    912			(u8 *)c->vc_origin,
    913			sisusb_haddr(sisusb, c, 0, 0),
    914			delta);
    915
    916	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
    917	c->vc_visible_origin = c->vc_origin;
    918
    919	sisusbcon_set_start_address(sisusb, c);
    920
    921	c->vc_pos = c->vc_pos - oldorigin + c->vc_origin;
    922
    923	mutex_unlock(&sisusb->lock);
    924
    925	return true;
    926}
    927
    928/* Interface routine */
    929static int
    930sisusbcon_set_origin(struct vc_data *c)
    931{
    932	struct sisusb_usb_data *sisusb;
    933
    934	/* Returning != 0 means we were successful.
    935	 * Returning 0 will vt make to use its own
    936	 *	screenbuffer as the origin.
    937	 */
    938
    939	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    940	if (!sisusb)
    941		return 0;
    942
    943	/* sisusb->lock is down */
    944
    945	if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
    946		mutex_unlock(&sisusb->lock);
    947		return 0;
    948	}
    949
    950	c->vc_origin = c->vc_visible_origin = sisusb->scrbuf;
    951
    952	sisusbcon_set_start_address(sisusb, c);
    953
    954	sisusb->con_rolled_over = 0;
    955
    956	mutex_unlock(&sisusb->lock);
    957
    958	return true;
    959}
    960
    961/* Interface routine */
    962static int
    963sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows,
    964		 unsigned int user)
    965{
    966	struct sisusb_usb_data *sisusb;
    967	int fh;
    968
    969	sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num);
    970	if (!sisusb)
    971		return -ENODEV;
    972
    973	fh = sisusb->current_font_height;
    974
    975	mutex_unlock(&sisusb->lock);
    976
    977	/* We are quite unflexible as regards resizing. The vt code
    978	 * handles sizes where the line length isn't equal the pitch
    979	 * quite badly. As regards the rows, our panning tricks only
    980	 * work well if the number of rows equals the visible number
    981	 * of rows.
    982	 */
    983
    984	if (newcols != 80 || c->vc_scan_lines / fh != newrows)
    985		return -EINVAL;
    986
    987	return 0;
    988}
    989
    990int
    991sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
    992			u8 *arg, int cmapsz, int ch512, int dorecalc,
    993			struct vc_data *c, int fh, int uplock)
    994{
    995	int font_select = 0x00, i, err = 0;
    996	u32 offset = 0;
    997	u8 dummy;
    998
    999	/* 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
   1191font_op_error:
   1192	if (uplock)
   1193		mutex_unlock(&sisusb->lock);
   1194
   1195	return -EIO;
   1196}
   1197
   1198/* Interface routine */
   1199static int
   1200sisusbcon_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 */
   1245static int
   1246sisusbcon_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
   1282static 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
   1307static const char *sisusbdummycon_startup(void)
   1308{
   1309    return "SISUSBVGADUMMY";
   1310}
   1311
   1312static 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
   1322static void sisusbdummycon_deinit(struct vc_data *vc) { }
   1323static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx,
   1324				 int height, int width) { }
   1325static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos,
   1326				int xpos) { }
   1327static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s,
   1328				 int count, int ypos, int xpos) { }
   1329static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { }
   1330
   1331static 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
   1338static int sisusbdummycon_switch(struct vc_data *vc)
   1339{
   1340	return 0;
   1341}
   1342
   1343static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch)
   1344{
   1345	return 0;
   1346}
   1347
   1348static 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
   1362int
   1363sisusb_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
   1439void
   1440sisusb_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
   1490void __init sisusb_init_concode(void)
   1491{
   1492	int i;
   1493
   1494	for (i = 0; i < MAX_NR_CONSOLES; i++)
   1495		mysisusbs[i] = NULL;
   1496}