• Home
  • History
  • Annotate
  • Line#
  • Navigate
  • Raw
  • Download
  • only in /netgear-R7000-V1.0.7.12_1.2.5/components/opensource/linux/linux-2.6.36/drivers/usb/misc/sisusbvga/
1/*
2 * sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles
3 *
4 * VGA text mode console part
5 *
6 * Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria
7 *
8 * If distributed as part of the Linux kernel, this code is licensed under the
9 * terms of the GPL v2.
10 *
11 * Otherwise, the following license terms apply:
12 *
13 * * Redistribution and use in source and binary forms, with or without
14 * * modification, are permitted provided that the following conditions
15 * * are met:
16 * * 1) Redistributions of source code must retain the above copyright
17 * *    notice, this list of conditions and the following disclaimer.
18 * * 2) Redistributions in binary form must reproduce the above copyright
19 * *    notice, this list of conditions and the following disclaimer in the
20 * *    documentation and/or other materials provided with the distribution.
21 * * 3) The name of the author may not be used to endorse or promote products
22 * *    derived from this software without specific psisusbr written permission.
23 * *
24 * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR
25 * * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
26 * * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
27 * * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
28 * * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
29 * * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
30 * * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
31 * * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
32 * * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
33 * * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 *
35 * Author: Thomas Winischhofer <thomas@winischhofer.net>
36 *
37 * Portions based on vgacon.c which are
38 *	Created 28 Sep 1997 by Geert Uytterhoeven
39 *      Rewritten by Martin Mares <mj@ucw.cz>, July 1998
40 *      based on code Copyright (C) 1991, 1992  Linus Torvalds
41 *			    1995  Jay Estabrook
42 *
43 * A note on using in_atomic() in here: We can't handle console
44 * calls from non-schedulable context due to our USB-dependend
45 * nature. For now, this driver just ignores any calls if it
46 * detects this state.
47 *
48 */
49
50#include <linux/mutex.h>
51#include <linux/module.h>
52#include <linux/kernel.h>
53#include <linux/signal.h>
54#include <linux/fs.h>
55#include <linux/usb.h>
56#include <linux/tty.h>
57#include <linux/console.h>
58#include <linux/string.h>
59#include <linux/kd.h>
60#include <linux/init.h>
61#include <linux/vt_kern.h>
62#include <linux/selection.h>
63#include <linux/spinlock.h>
64#include <linux/kref.h>
65#include <linux/ioport.h>
66#include <linux/interrupt.h>
67#include <linux/vmalloc.h>
68
69#include "sisusb.h"
70#include "sisusb_init.h"
71
72#ifdef INCL_SISUSB_CON
73
74#define sisusbcon_writew(val, addr)	(*(addr) = (val))
75#define sisusbcon_readw(addr)		(*(addr))
76#define sisusbcon_memmovew(d, s, c)	memmove(d, s, c)
77#define sisusbcon_memcpyw(d, s, c)	memcpy(d, s, c)
78
79/* vc_data -> sisusb conversion table */
80static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES];
81
82/* Forward declaration */
83static const struct consw sisusb_con;
84
85static inline void
86sisusbcon_memsetw(u16 *s, u16 c, unsigned int count)
87{
88	count /= 2;
89	while (count--)
90		sisusbcon_writew(c, s++);
91}
92
93static inline void
94sisusb_initialize(struct sisusb_usb_data *sisusb)
95{
96	/* Reset cursor and start address */
97	if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00))
98		return;
99	if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00))
100		return;
101	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00))
102		return;
103	sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00);
104}
105
106static inline void
107sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c)
108{
109	sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2;
110
111	sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8));
112	sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff));
113}
114
115void
116sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location)
117{
118	if (sisusb->sisusb_cursor_loc == location)
119		return;
120
121	sisusb->sisusb_cursor_loc = location;
122
123
124	if ((location & 0x0007) == 0x0007) {
125		sisusb->bad_cursor_pos = 1;
126		location--;
127		if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20))
128			return;
129	} else if (sisusb->bad_cursor_pos) {
130		if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f))
131			return;
132		sisusb->bad_cursor_pos = 0;
133	}
134
135	if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8)))
136		return;
137	sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff));
138}
139
140static inline struct sisusb_usb_data *
141sisusb_get_sisusb(unsigned short console)
142{
143	return mysisusbs[console];
144}
145
146static inline int
147sisusb_sisusb_valid(struct sisusb_usb_data *sisusb)
148{
149	if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev)
150		return 0;
151
152	return 1;
153}
154
155static struct sisusb_usb_data *
156sisusb_get_sisusb_lock_and_check(unsigned short console)
157{
158	struct sisusb_usb_data *sisusb;
159
160	/* We can't handle console calls in non-schedulable
161	 * context due to our locks and the USB transport.
162	 * So we simply ignore them. This should only affect
163	 * some calls to printk.
164	 */
165	if (in_atomic())
166		return NULL;
167
168	if (!(sisusb = sisusb_get_sisusb(console)))
169		return NULL;
170
171	mutex_lock(&sisusb->lock);
172
173	if (!sisusb_sisusb_valid(sisusb) ||
174	    !sisusb->havethisconsole[console]) {
175		mutex_unlock(&sisusb->lock);
176		return NULL;
177	}
178
179	return sisusb;
180}
181
182static int
183sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb)
184{
185	if (sisusb->is_gfx ||
186	    sisusb->textmodedestroyed ||
187	    c->vc_mode != KD_TEXT)
188		return 1;
189
190	return 0;
191}
192
193/* con_startup console interface routine */
194static const char *
195sisusbcon_startup(void)
196{
197	return "SISUSBCON";
198}
199
200/* con_init console interface routine */
201static void
202sisusbcon_init(struct vc_data *c, int init)
203{
204	struct sisusb_usb_data *sisusb;
205	int cols, rows;
206
207	/* This is called by take_over_console(),
208	 * ie by us/under our control. It is
209	 * only called after text mode and fonts
210	 * are set up/restored.
211	 */
212
213	if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
214		return;
215
216	mutex_lock(&sisusb->lock);
217
218	if (!sisusb_sisusb_valid(sisusb)) {
219		mutex_unlock(&sisusb->lock);
220		return;
221	}
222
223	c->vc_can_do_color = 1;
224
225	c->vc_complement_mask = 0x7700;
226
227	c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0;
228
229	sisusb->haveconsole = 1;
230
231	sisusb->havethisconsole[c->vc_num] = 1;
232
233	/* We only support 640x400 */
234	c->vc_scan_lines = 400;
235
236	c->vc_font.height = sisusb->current_font_height;
237
238	/* We only support width = 8 */
239	cols = 80;
240	rows = c->vc_scan_lines / c->vc_font.height;
241
242	/* Increment usage count for our sisusb.
243	 * Doing so saves us from upping/downing
244	 * the disconnect semaphore; we can't
245	 * lose our sisusb until this is undone
246	 * in con_deinit. For all other console
247	 * interface functions, it suffices to
248	 * use sisusb->lock and do a quick check
249	 * of sisusb for device disconnection.
250	 */
251	kref_get(&sisusb->kref);
252
253	if (!*c->vc_uni_pagedir_loc)
254		con_set_default_unimap(c);
255
256	mutex_unlock(&sisusb->lock);
257
258	if (init) {
259		c->vc_cols = cols;
260		c->vc_rows = rows;
261	} else
262		vc_resize(c, cols, rows);
263}
264
265/* con_deinit console interface routine */
266static void
267sisusbcon_deinit(struct vc_data *c)
268{
269	struct sisusb_usb_data *sisusb;
270	int i;
271
272	/* This is called by take_over_console()
273	 * and others, ie not under our control.
274	 */
275
276	if (!(sisusb = sisusb_get_sisusb(c->vc_num)))
277		return;
278
279	mutex_lock(&sisusb->lock);
280
281	/* Clear ourselves in mysisusbs */
282	mysisusbs[c->vc_num] = NULL;
283
284	sisusb->havethisconsole[c->vc_num] = 0;
285
286	/* Free our font buffer if all consoles are gone */
287	if (sisusb->font_backup) {
288		for(i = 0; i < MAX_NR_CONSOLES; i++) {
289			if (sisusb->havethisconsole[c->vc_num])
290				break;
291		}
292		if (i == MAX_NR_CONSOLES) {
293			vfree(sisusb->font_backup);
294			sisusb->font_backup = NULL;
295		}
296	}
297
298	mutex_unlock(&sisusb->lock);
299
300	/* decrement the usage count on our sisusb */
301	kref_put(&sisusb->kref, sisusb_delete);
302}
303
304/* interface routine */
305static u8
306sisusbcon_build_attr(struct vc_data *c, u8 color, u8 intensity,
307			    u8 blink, u8 underline, u8 reverse, u8 unused)
308{
309	u8 attr = color;
310
311	if (underline)
312		attr = (attr & 0xf0) | c->vc_ulcolor;
313	else if (intensity == 0)
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 == 2)
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 = sisusbcon_readw(p);
342
343		a = ((a) & 0x88ff)        |
344		    (((a) & 0x7000) >> 4) |
345		    (((a) & 0x0700) << 4);
346
347		sisusbcon_writew(a, p++);
348	}
349}
350
351#define SISUSB_VADDR(x,y) \
352	((u16 *)c->vc_origin + \
353	(y) * sisusb->sisusb_num_columns + \
354	(x))
355
356#define SISUSB_HADDR(x,y) \
357	((u16 *)(sisusb->vrambase + (c->vc_origin - sisusb->scrbuf)) + \
358	(y) * sisusb->sisusb_num_columns + \
359	(x))
360
361/* Interface routine */
362static void
363sisusbcon_putc(struct vc_data *c, int ch, int y, int x)
364{
365	struct sisusb_usb_data *sisusb;
366	ssize_t written;
367
368	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
369		return;
370
371	/* sisusb->lock is down */
372	if (sisusb_is_inactive(c, sisusb)) {
373		mutex_unlock(&sisusb->lock);
374		return;
375	}
376
377
378	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
379				(long)SISUSB_HADDR(x, y), 2, &written);
380
381	mutex_unlock(&sisusb->lock);
382}
383
384/* Interface routine */
385static void
386sisusbcon_putcs(struct vc_data *c, const unsigned short *s,
387		         int count, int y, int x)
388{
389	struct sisusb_usb_data *sisusb;
390	ssize_t written;
391	u16 *dest;
392	int i;
393
394	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
395		return;
396
397	/* sisusb->lock is down */
398
399	/* Need to put the characters into the buffer ourselves,
400	 * because the vt does this AFTER calling us.
401	 */
402
403	dest = SISUSB_VADDR(x, y);
404
405	for (i = count; i > 0; i--)
406		sisusbcon_writew(sisusbcon_readw(s++), dest++);
407
408	if (sisusb_is_inactive(c, sisusb)) {
409		mutex_unlock(&sisusb->lock);
410		return;
411	}
412
413	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y),
414				(long)SISUSB_HADDR(x, y), count * 2, &written);
415
416	mutex_unlock(&sisusb->lock);
417}
418
419/* Interface routine */
420static void
421sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width)
422{
423	struct sisusb_usb_data *sisusb;
424	u16 eattr = c->vc_video_erase_char;
425	ssize_t written;
426	int i, length, cols;
427	u16 *dest;
428
429	if (width <= 0 || height <= 0)
430		return;
431
432	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
433		return;
434
435	/* sisusb->lock is down */
436
437	/* Need to clear buffer ourselves, because the vt does
438	 * this AFTER calling us.
439	 */
440
441	dest = SISUSB_VADDR(x, y);
442
443	cols = sisusb->sisusb_num_columns;
444
445	if (width > cols)
446		width = cols;
447
448	if (x == 0 && width >= c->vc_cols) {
449
450		sisusbcon_memsetw(dest, eattr, height * cols * 2);
451
452	} else {
453
454		for (i = height; i > 0; i--, dest += cols)
455			sisusbcon_memsetw(dest, eattr, width * 2);
456
457	}
458
459	if (sisusb_is_inactive(c, sisusb)) {
460		mutex_unlock(&sisusb->lock);
461		return;
462	}
463
464	length = ((height * cols) - x - (cols - width - x)) * 2;
465
466
467	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y),
468				(long)SISUSB_HADDR(x, y), length, &written);
469
470	mutex_unlock(&sisusb->lock);
471}
472
473/* Interface routine */
474static void
475sisusbcon_bmove(struct vc_data *c, int sy, int sx,
476			 int dy, int dx, int height, int width)
477{
478	struct sisusb_usb_data *sisusb;
479	ssize_t written;
480	int cols, length;
481
482	if (width <= 0 || height <= 0)
483		return;
484
485	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
486		return;
487
488	/* sisusb->lock is down */
489
490	cols = sisusb->sisusb_num_columns;
491
492	if (sisusb_is_inactive(c, sisusb)) {
493		mutex_unlock(&sisusb->lock);
494		return;
495	}
496
497	length = ((height * cols) - dx - (cols - width - dx)) * 2;
498
499
500	sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy),
501				(long)SISUSB_HADDR(dx, dy), length, &written);
502
503	mutex_unlock(&sisusb->lock);
504}
505
506/* interface routine */
507static int
508sisusbcon_switch(struct vc_data *c)
509{
510	struct sisusb_usb_data *sisusb;
511	ssize_t written;
512	int length;
513
514	/* Returnvalue 0 means we have fully restored screen,
515	 *	and vt doesn't need to call do_update_region().
516	 * Returnvalue != 0 naturally means the opposite.
517	 */
518
519	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
520		return 0;
521
522	/* sisusb->lock is down */
523
524	/* Don't write to screen if in gfx mode */
525	if (sisusb_is_inactive(c, sisusb)) {
526		mutex_unlock(&sisusb->lock);
527		return 0;
528	}
529
530	/* That really should not happen. It would mean we are
531	 * being called while the vc is using its private buffer
532	 * as origin.
533	 */
534	if (c->vc_origin == (unsigned long)c->vc_screenbuf) {
535		mutex_unlock(&sisusb->lock);
536		dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n");
537		return 0;
538	}
539
540	/* Check that we don't copy too much */
541	length = min((int)c->vc_screenbuf_size,
542			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
543
544	/* Restore the screen contents */
545	sisusbcon_memcpyw((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf,
546								length);
547
548	sisusb_copy_memory(sisusb, (unsigned char *)c->vc_origin,
549				(long)SISUSB_HADDR(0, 0),
550				length, &written);
551
552	mutex_unlock(&sisusb->lock);
553
554	return 0;
555}
556
557/* interface routine */
558static void
559sisusbcon_save_screen(struct vc_data *c)
560{
561	struct sisusb_usb_data *sisusb;
562	int length;
563
564	/* Save the current screen contents to vc's private
565	 * buffer.
566	 */
567
568	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
569		return;
570
571	/* sisusb->lock is down */
572
573	if (sisusb_is_inactive(c, sisusb)) {
574		mutex_unlock(&sisusb->lock);
575		return;
576	}
577
578	/* Check that we don't copy too much */
579	length = min((int)c->vc_screenbuf_size,
580			(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin));
581
582	/* Save the screen contents to vc's private buffer */
583	sisusbcon_memcpyw((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin,
584								length);
585
586	mutex_unlock(&sisusb->lock);
587}
588
589/* interface routine */
590static int
591sisusbcon_set_palette(struct vc_data *c, unsigned char *table)
592{
593	struct sisusb_usb_data *sisusb;
594	int i, j;
595
596	/* Return value not used by vt */
597
598	if (!CON_IS_VISIBLE(c))
599		return -EINVAL;
600
601	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
602		return -EINVAL;
603
604	/* sisusb->lock is down */
605
606	if (sisusb_is_inactive(c, sisusb)) {
607		mutex_unlock(&sisusb->lock);
608		return -EINVAL;
609	}
610
611	for (i = j = 0; i < 16; i++) {
612		if (sisusb_setreg(sisusb, SISCOLIDX, table[i]))
613			break;
614		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
615			break;
616		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
617			break;
618		if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2))
619			break;
620	}
621
622	mutex_unlock(&sisusb->lock);
623
624	return 0;
625}
626
627/* interface routine */
628static int
629sisusbcon_blank(struct vc_data *c, int blank, int mode_switch)
630{
631	struct sisusb_usb_data *sisusb;
632	u8 sr1, cr17, pmreg, cr63;
633	ssize_t written;
634	int ret = 0;
635
636	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
637		return 0;
638
639	/* sisusb->lock is down */
640
641	if (mode_switch)
642		sisusb->is_gfx = blank ? 1 : 0;
643
644	if (sisusb_is_inactive(c, sisusb)) {
645		mutex_unlock(&sisusb->lock);
646		return 0;
647	}
648
649	switch (blank) {
650
651	case 1:		/* Normal blanking: Clear screen */
652	case -1:
653		sisusbcon_memsetw((u16 *)c->vc_origin,
654				c->vc_video_erase_char,
655				c->vc_screenbuf_size);
656		sisusb_copy_memory(sisusb,
657				(unsigned char *)c->vc_origin,
658				(u32)(sisusb->vrambase +
659					(c->vc_origin - sisusb->scrbuf)),
660				c->vc_screenbuf_size, &written);
661		sisusb->con_blanked = 1;
662		ret = 1;
663		break;
664
665	default:	/* VESA blanking */
666		switch (blank) {
667		case 0: /* Unblank */
668			sr1   = 0x00;
669			cr17  = 0x80;
670			pmreg = 0x00;
671			cr63  = 0x00;
672			ret = 1;
673			sisusb->con_blanked = 0;
674			break;
675		case VESA_VSYNC_SUSPEND + 1:
676			sr1   = 0x20;
677			cr17  = 0x80;
678			pmreg = 0x80;
679			cr63  = 0x40;
680			break;
681		case VESA_HSYNC_SUSPEND + 1:
682			sr1   = 0x20;
683			cr17  = 0x80;
684			pmreg = 0x40;
685			cr63  = 0x40;
686			break;
687		case VESA_POWERDOWN + 1:
688			sr1   = 0x20;
689			cr17  = 0x00;
690			pmreg = 0xc0;
691			cr63  = 0x40;
692			break;
693		default:
694			mutex_unlock(&sisusb->lock);
695			return -EINVAL;
696		}
697
698		sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1);
699		sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17);
700		sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg);
701		sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63);
702
703	}
704
705	mutex_unlock(&sisusb->lock);
706
707	return ret;
708}
709
710/* interface routine */
711static int
712sisusbcon_scrolldelta(struct vc_data *c, int lines)
713{
714	struct sisusb_usb_data *sisusb;
715	int margin = c->vc_size_row * 4;
716	int ul, we, p, st;
717
718	/* The return value does not seem to be used */
719
720	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
721		return 0;
722
723	/* sisusb->lock is down */
724
725	if (sisusb_is_inactive(c, sisusb)) {
726		mutex_unlock(&sisusb->lock);
727		return 0;
728	}
729
730	if (!lines)		/* Turn scrollback off */
731		c->vc_visible_origin = c->vc_origin;
732	else {
733
734		if (sisusb->con_rolled_over >
735				(c->vc_scr_end - sisusb->scrbuf) + margin) {
736
737			ul = c->vc_scr_end - sisusb->scrbuf;
738			we = sisusb->con_rolled_over + c->vc_size_row;
739
740		} else {
741
742			ul = 0;
743			we = sisusb->scrbuf_size;
744
745		}
746
747		p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we +
748				lines * c->vc_size_row;
749
750		st = (c->vc_origin - sisusb->scrbuf - ul + we) % we;
751
752		if (st < 2 * margin)
753			margin = 0;
754
755		if (p < margin)
756			p = 0;
757
758		if (p > st - margin)
759			p = st;
760
761		c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we;
762	}
763
764	sisusbcon_set_start_address(sisusb, c);
765
766	mutex_unlock(&sisusb->lock);
767
768	return 1;
769}
770
771/* Interface routine */
772static void
773sisusbcon_cursor(struct vc_data *c, int mode)
774{
775	struct sisusb_usb_data *sisusb;
776	int from, to, baseline;
777
778	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
779		return;
780
781	/* sisusb->lock is down */
782
783	if (sisusb_is_inactive(c, sisusb)) {
784		mutex_unlock(&sisusb->lock);
785		return;
786	}
787
788	if (c->vc_origin != c->vc_visible_origin) {
789		c->vc_visible_origin = c->vc_origin;
790		sisusbcon_set_start_address(sisusb, c);
791	}
792
793	if (mode == CM_ERASE) {
794		sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20);
795		sisusb->sisusb_cursor_size_to = -1;
796		mutex_unlock(&sisusb->lock);
797		return;
798	}
799
800	sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2);
801
802	baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2);
803
804	switch (c->vc_cursor_type & 0x0f) {
805		case CUR_BLOCK:		from = 1;
806					to   = c->vc_font.height;
807					break;
808		case CUR_TWO_THIRDS:	from = c->vc_font.height / 3;
809					to   = baseline;
810					break;
811		case CUR_LOWER_HALF:	from = c->vc_font.height / 2;
812					to   = baseline;
813					break;
814		case CUR_LOWER_THIRD:	from = (c->vc_font.height * 2) / 3;
815					to   = baseline;
816					break;
817		case CUR_NONE:		from = 31;
818					to = 30;
819					break;
820		default:
821		case CUR_UNDERLINE:	from = baseline - 1;
822					to   = baseline;
823					break;
824	}
825
826	if (sisusb->sisusb_cursor_size_from != from ||
827	    sisusb->sisusb_cursor_size_to != to) {
828
829		sisusb_setidxreg(sisusb, SISCR, 0x0a, from);
830		sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to);
831
832		sisusb->sisusb_cursor_size_from = from;
833		sisusb->sisusb_cursor_size_to   = to;
834	}
835
836	mutex_unlock(&sisusb->lock);
837}
838
839static int
840sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb,
841					int t, int b, int dir, int lines)
842{
843	int cols = sisusb->sisusb_num_columns;
844	int length = ((b - t) * cols) * 2;
845	u16 eattr = c->vc_video_erase_char;
846	ssize_t written;
847
848	/* sisusb->lock is down */
849
850	/* Scroll an area which does not match the
851	 * visible screen's dimensions. This needs
852	 * to be done separately, as it does not
853	 * use hardware panning.
854	 */
855
856	switch (dir) {
857
858		case SM_UP:
859			sisusbcon_memmovew(SISUSB_VADDR(0, t),
860					   SISUSB_VADDR(0, t + lines),
861					   (b - t - lines) * cols * 2);
862			sisusbcon_memsetw(SISUSB_VADDR(0, b - lines), eattr,
863					  lines * cols * 2);
864			break;
865
866		case SM_DOWN:
867			sisusbcon_memmovew(SISUSB_VADDR(0, t + lines),
868					   SISUSB_VADDR(0, t),
869					   (b - t - lines) * cols * 2);
870			sisusbcon_memsetw(SISUSB_VADDR(0, t), eattr,
871					  lines * cols * 2);
872			break;
873	}
874
875	sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(0, t),
876				(long)SISUSB_HADDR(0, t), length, &written);
877
878	mutex_unlock(&sisusb->lock);
879
880	return 1;
881}
882
883/* Interface routine */
884static int
885sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines)
886{
887	struct sisusb_usb_data *sisusb;
888	u16 eattr = c->vc_video_erase_char;
889	ssize_t written;
890	int copyall = 0;
891	unsigned long oldorigin;
892	unsigned int delta = lines * c->vc_size_row;
893	u32 originoffset;
894
895	/* Returning != 0 means we have done the scrolling successfully.
896	 * Returning 0 makes vt do the scrolling on its own.
897	 * Note that con_scroll is only called if the console is
898	 * visible. In that case, the origin should be our buffer,
899	 * not the vt's private one.
900	 */
901
902	if (!lines)
903		return 1;
904
905	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
906		return 0;
907
908	/* sisusb->lock is down */
909
910	if (sisusb_is_inactive(c, sisusb)) {
911		mutex_unlock(&sisusb->lock);
912		return 0;
913	}
914
915	/* Special case */
916	if (t || b != c->vc_rows)
917		return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines);
918
919	if (c->vc_origin != c->vc_visible_origin) {
920		c->vc_visible_origin = c->vc_origin;
921		sisusbcon_set_start_address(sisusb, c);
922	}
923
924	/* limit amount to maximum realistic size */
925	if (lines > c->vc_rows)
926		lines = c->vc_rows;
927
928	oldorigin = c->vc_origin;
929
930	switch (dir) {
931
932	case SM_UP:
933
934		if (c->vc_scr_end + delta >=
935				sisusb->scrbuf + sisusb->scrbuf_size) {
936			sisusbcon_memcpyw((u16 *)sisusb->scrbuf,
937					  (u16 *)(oldorigin + delta),
938					  c->vc_screenbuf_size - delta);
939			c->vc_origin = sisusb->scrbuf;
940			sisusb->con_rolled_over = oldorigin - sisusb->scrbuf;
941			copyall = 1;
942		} else
943			c->vc_origin += delta;
944
945		sisusbcon_memsetw(
946			(u16 *)(c->vc_origin + c->vc_screenbuf_size - delta),
947					eattr, delta);
948
949		break;
950
951	case SM_DOWN:
952
953		if (oldorigin - delta < sisusb->scrbuf) {
954			sisusbcon_memmovew((u16 *)(sisusb->scrbuf +
955							sisusb->scrbuf_size -
956							c->vc_screenbuf_size +
957							delta),
958					   (u16 *)oldorigin,
959					   c->vc_screenbuf_size - delta);
960			c->vc_origin = sisusb->scrbuf +
961					sisusb->scrbuf_size -
962					c->vc_screenbuf_size;
963			sisusb->con_rolled_over = 0;
964			copyall = 1;
965		} else
966			c->vc_origin -= delta;
967
968		c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
969
970		scr_memsetw((u16 *)(c->vc_origin), eattr, delta);
971
972		break;
973	}
974
975	originoffset = (u32)(c->vc_origin - sisusb->scrbuf);
976
977	if (copyall)
978		sisusb_copy_memory(sisusb,
979			(char *)c->vc_origin,
980			(u32)(sisusb->vrambase + originoffset),
981			c->vc_screenbuf_size, &written);
982	else if (dir == SM_UP)
983		sisusb_copy_memory(sisusb,
984			(char *)c->vc_origin + c->vc_screenbuf_size - delta,
985			(u32)sisusb->vrambase + originoffset +
986					c->vc_screenbuf_size - delta,
987			delta, &written);
988	else
989		sisusb_copy_memory(sisusb,
990			(char *)c->vc_origin,
991			(u32)(sisusb->vrambase + originoffset),
992			delta, &written);
993
994	c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size;
995	c->vc_visible_origin = c->vc_origin;
996
997	sisusbcon_set_start_address(sisusb, c);
998
999	c->vc_pos = c->vc_pos - oldorigin + c->vc_origin;
1000
1001	mutex_unlock(&sisusb->lock);
1002
1003	return 1;
1004}
1005
1006/* Interface routine */
1007static int
1008sisusbcon_set_origin(struct vc_data *c)
1009{
1010	struct sisusb_usb_data *sisusb;
1011
1012	/* Returning != 0 means we were successful.
1013	 * Returning 0 will vt make to use its own
1014	 *	screenbuffer as the origin.
1015	 */
1016
1017	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
1018		return 0;
1019
1020	/* sisusb->lock is down */
1021
1022	if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) {
1023		mutex_unlock(&sisusb->lock);
1024		return 0;
1025	}
1026
1027	c->vc_origin = c->vc_visible_origin = sisusb->scrbuf;
1028
1029	sisusbcon_set_start_address(sisusb, c);
1030
1031	sisusb->con_rolled_over = 0;
1032
1033	mutex_unlock(&sisusb->lock);
1034
1035	return 1;
1036}
1037
1038/* Interface routine */
1039static int
1040sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows,
1041		 unsigned int user)
1042{
1043	struct sisusb_usb_data *sisusb;
1044	int fh;
1045
1046	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
1047		return -ENODEV;
1048
1049	fh = sisusb->current_font_height;
1050
1051	mutex_unlock(&sisusb->lock);
1052
1053	/* We are quite unflexible as regards resizing. The vt code
1054	 * handles sizes where the line length isn't equal the pitch
1055	 * quite badly. As regards the rows, our panning tricks only
1056	 * work well if the number of rows equals the visible number
1057	 * of rows.
1058	 */
1059
1060	if (newcols != 80 || c->vc_scan_lines / fh != newrows)
1061		return -EINVAL;
1062
1063	return 0;
1064}
1065
1066int
1067sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot,
1068			u8 *arg, int cmapsz, int ch512, int dorecalc,
1069			struct vc_data *c, int fh, int uplock)
1070{
1071	int font_select = 0x00, i, err = 0;
1072	u32 offset = 0;
1073	u8 dummy;
1074
1075	/* sisusb->lock is down */
1076
1077	/*
1078	 * The default font is kept in slot 0.
1079	 * A user font is loaded in slot 2 (256 ch)
1080	 * or 2+3 (512 ch).
1081	 */
1082
1083	if ((slot != 0 && slot != 2) || !fh) {
1084		if (uplock)
1085			mutex_unlock(&sisusb->lock);
1086		return -EINVAL;
1087	}
1088
1089	if (set)
1090		sisusb->font_slot = slot;
1091
1092	/* Default font is always 256 */
1093	if (slot == 0)
1094		ch512 = 0;
1095	else
1096		offset = 4 * cmapsz;
1097
1098	font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a);
1099
1100	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
1101	err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */
1102	err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */
1103	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */
1104
1105	if (err)
1106		goto font_op_error;
1107
1108	err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */
1109	err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */
1110	err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */
1111
1112	if (err)
1113		goto font_op_error;
1114
1115	if (arg) {
1116		if (set)
1117			for (i = 0; i < cmapsz; i++) {
1118				err |= sisusb_writeb(sisusb,
1119					sisusb->vrambase + offset + i,
1120					arg[i]);
1121				if (err)
1122					break;
1123			}
1124		else
1125			for (i = 0; i < cmapsz; i++) {
1126				err |= sisusb_readb(sisusb,
1127					sisusb->vrambase + offset + i,
1128					&arg[i]);
1129				if (err)
1130					break;
1131			}
1132
1133		/*
1134		 * In 512-character mode, the character map is not contiguous if
1135		 * we want to remain EGA compatible -- which we do
1136		 */
1137
1138		if (ch512) {
1139			if (set)
1140				for (i = 0; i < cmapsz; i++) {
1141					err |= sisusb_writeb(sisusb,
1142						sisusb->vrambase + offset +
1143							(2 * cmapsz) + i,
1144						arg[cmapsz + i]);
1145					if (err)
1146						break;
1147				}
1148			else
1149				for (i = 0; i < cmapsz; i++) {
1150					err |= sisusb_readb(sisusb,
1151						sisusb->vrambase + offset +
1152							(2 * cmapsz) + i,
1153						&arg[cmapsz + i]);
1154					if (err)
1155						break;
1156				}
1157		}
1158	}
1159
1160	if (err)
1161		goto font_op_error;
1162
1163	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */
1164	err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */
1165	err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */
1166	if (set)
1167		sisusb_setidxreg(sisusb, SISSR, 0x03, font_select);
1168	err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */
1169
1170	if (err)
1171		goto font_op_error;
1172
1173	err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */
1174	err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */
1175	err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */
1176
1177	if (err)
1178		goto font_op_error;
1179
1180	if ((set) && (ch512 != sisusb->current_font_512)) {
1181
1182		/* Font is shared among all our consoles.
1183		 * And so is the hi_font_mask.
1184		 */
1185		for (i = 0; i < MAX_NR_CONSOLES; i++) {
1186			struct vc_data *d = vc_cons[i].d;
1187			if (d && d->vc_sw == &sisusb_con)
1188				d->vc_hi_font_mask = ch512 ? 0x0800 : 0;
1189		}
1190
1191		sisusb->current_font_512 = ch512;
1192
1193		/* color plane enable register:
1194			256-char: enable intensity bit
1195			512-char: disable intensity bit */
1196		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1197		sisusb_setreg(sisusb, SISAR, 0x12);
1198		sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f);
1199
1200		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1201		sisusb_setreg(sisusb, SISAR, 0x20);
1202		sisusb_getreg(sisusb, SISINPSTAT, &dummy);
1203	}
1204
1205	if (dorecalc) {
1206
1207		/*
1208		 * Adjust the screen to fit a font of a certain height
1209		 */
1210
1211		unsigned char ovr, vde, fsr;
1212		int rows = 0, maxscan = 0;
1213
1214		if (c) {
1215
1216			/* Number of video rows */
1217			rows = c->vc_scan_lines / fh;
1218			/* Scan lines to actually display-1 */
1219			maxscan = rows * fh - 1;
1220
1221			/*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n",
1222				rows, maxscan, fh, c->vc_scan_lines);*/
1223
1224			sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr);
1225			vde = maxscan & 0xff;
1226			ovr = (ovr & 0xbd) |
1227			      ((maxscan & 0x100) >> 7) |
1228			      ((maxscan & 0x200) >> 3);
1229			sisusb_setidxreg(sisusb, SISCR, 0x07, ovr);
1230			sisusb_setidxreg(sisusb, SISCR, 0x12, vde);
1231
1232		}
1233
1234		sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr);
1235		fsr = (fsr & 0xe0) | (fh - 1);
1236		sisusb_setidxreg(sisusb, SISCR, 0x09, fsr);
1237		sisusb->current_font_height = fh;
1238
1239		sisusb->sisusb_cursor_size_from = -1;
1240		sisusb->sisusb_cursor_size_to   = -1;
1241
1242	}
1243
1244	if (uplock)
1245		mutex_unlock(&sisusb->lock);
1246
1247	if (dorecalc && c) {
1248		int rows = c->vc_scan_lines / fh;
1249
1250		/* Now adjust our consoles' size */
1251
1252		for (i = 0; i < MAX_NR_CONSOLES; i++) {
1253			struct vc_data *vc = vc_cons[i].d;
1254
1255			if (vc && vc->vc_sw == &sisusb_con) {
1256				if (CON_IS_VISIBLE(vc)) {
1257					vc->vc_sw->con_cursor(vc, CM_DRAW);
1258				}
1259				vc->vc_font.height = fh;
1260				vc_resize(vc, 0, rows);
1261			}
1262		}
1263	}
1264
1265	return 0;
1266
1267font_op_error:
1268	if (uplock)
1269		mutex_unlock(&sisusb->lock);
1270
1271	return -EIO;
1272}
1273
1274/* Interface routine */
1275static int
1276sisusbcon_font_set(struct vc_data *c, struct console_font *font,
1277							unsigned flags)
1278{
1279	struct sisusb_usb_data *sisusb;
1280	unsigned charcount = font->charcount;
1281
1282	if (font->width != 8 || (charcount != 256 && charcount != 512))
1283		return -EINVAL;
1284
1285	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
1286		return -ENODEV;
1287
1288	/* sisusb->lock is down */
1289
1290	/* Save the user-provided font into a buffer. This
1291	 * is used for restoring text mode after quitting
1292	 * from X and for the con_getfont routine.
1293	 */
1294	if (sisusb->font_backup) {
1295		if (sisusb->font_backup_size < charcount) {
1296			vfree(sisusb->font_backup);
1297			sisusb->font_backup = NULL;
1298		}
1299	}
1300
1301	if (!sisusb->font_backup)
1302		sisusb->font_backup = vmalloc(charcount * 32);
1303
1304	if (sisusb->font_backup) {
1305		memcpy(sisusb->font_backup, font->data, charcount * 32);
1306		sisusb->font_backup_size = charcount;
1307		sisusb->font_backup_height = font->height;
1308		sisusb->font_backup_512 = (charcount == 512) ? 1 : 0;
1309	}
1310
1311	/* do_font_op ups sisusb->lock */
1312
1313	return sisusbcon_do_font_op(sisusb, 1, 2, font->data,
1314			8192, (charcount == 512),
1315			(!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0,
1316			c, font->height, 1);
1317}
1318
1319/* Interface routine */
1320static int
1321sisusbcon_font_get(struct vc_data *c, struct console_font *font)
1322{
1323	struct sisusb_usb_data *sisusb;
1324
1325	if (!(sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num)))
1326		return -ENODEV;
1327
1328	/* sisusb->lock is down */
1329
1330	font->width = 8;
1331	font->height = c->vc_font.height;
1332	font->charcount = 256;
1333
1334	if (!font->data) {
1335		mutex_unlock(&sisusb->lock);
1336		return 0;
1337	}
1338
1339	if (!sisusb->font_backup) {
1340		mutex_unlock(&sisusb->lock);
1341		return -ENODEV;
1342	}
1343
1344	/* Copy 256 chars only, like vgacon */
1345	memcpy(font->data, sisusb->font_backup, 256 * 32);
1346
1347	mutex_unlock(&sisusb->lock);
1348
1349	return 0;
1350}
1351
1352/*
1353 *  The console `switch' structure for the sisusb console
1354 */
1355
1356static const struct consw sisusb_con = {
1357	.owner =		THIS_MODULE,
1358	.con_startup =		sisusbcon_startup,
1359	.con_init =		sisusbcon_init,
1360	.con_deinit =		sisusbcon_deinit,
1361	.con_clear =		sisusbcon_clear,
1362	.con_putc =		sisusbcon_putc,
1363	.con_putcs =		sisusbcon_putcs,
1364	.con_cursor =		sisusbcon_cursor,
1365	.con_scroll =		sisusbcon_scroll,
1366	.con_bmove =		sisusbcon_bmove,
1367	.con_switch =		sisusbcon_switch,
1368	.con_blank =		sisusbcon_blank,
1369	.con_font_set =		sisusbcon_font_set,
1370	.con_font_get =		sisusbcon_font_get,
1371	.con_set_palette =	sisusbcon_set_palette,
1372	.con_scrolldelta =	sisusbcon_scrolldelta,
1373	.con_build_attr =	sisusbcon_build_attr,
1374	.con_invert_region =	sisusbcon_invert_region,
1375	.con_set_origin =	sisusbcon_set_origin,
1376	.con_save_screen =	sisusbcon_save_screen,
1377	.con_resize =		sisusbcon_resize,
1378};
1379
1380/* Our very own dummy console driver */
1381
1382static const char *sisusbdummycon_startup(void)
1383{
1384    return "SISUSBVGADUMMY";
1385}
1386
1387static void sisusbdummycon_init(struct vc_data *vc, int init)
1388{
1389    vc->vc_can_do_color = 1;
1390    if (init) {
1391	vc->vc_cols = 80;
1392	vc->vc_rows = 25;
1393    } else
1394	vc_resize(vc, 80, 25);
1395}
1396
1397static int sisusbdummycon_dummy(void)
1398{
1399    return 0;
1400}
1401
1402#define SISUSBCONDUMMY	(void *)sisusbdummycon_dummy
1403
1404static const struct consw sisusb_dummy_con = {
1405	.owner =		THIS_MODULE,
1406	.con_startup =		sisusbdummycon_startup,
1407	.con_init =		sisusbdummycon_init,
1408	.con_deinit =		SISUSBCONDUMMY,
1409	.con_clear =		SISUSBCONDUMMY,
1410	.con_putc =		SISUSBCONDUMMY,
1411	.con_putcs =		SISUSBCONDUMMY,
1412	.con_cursor =		SISUSBCONDUMMY,
1413	.con_scroll =		SISUSBCONDUMMY,
1414	.con_bmove =		SISUSBCONDUMMY,
1415	.con_switch =		SISUSBCONDUMMY,
1416	.con_blank =		SISUSBCONDUMMY,
1417	.con_font_set =		SISUSBCONDUMMY,
1418	.con_font_get =		SISUSBCONDUMMY,
1419	.con_font_default =	SISUSBCONDUMMY,
1420	.con_font_copy =	SISUSBCONDUMMY,
1421	.con_set_palette =	SISUSBCONDUMMY,
1422	.con_scrolldelta =	SISUSBCONDUMMY,
1423};
1424
1425int
1426sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last)
1427{
1428	int i, ret;
1429
1430	mutex_lock(&sisusb->lock);
1431
1432	/* Erm.. that should not happen */
1433	if (sisusb->haveconsole || !sisusb->SiS_Pr) {
1434		mutex_unlock(&sisusb->lock);
1435		return 1;
1436	}
1437
1438	sisusb->con_first = first;
1439	sisusb->con_last  = last;
1440
1441	if (first > last ||
1442	    first > MAX_NR_CONSOLES ||
1443	    last > MAX_NR_CONSOLES) {
1444		mutex_unlock(&sisusb->lock);
1445		return 1;
1446	}
1447
1448	/* If gfxcore not initialized or no consoles given, quit graciously */
1449	if (!sisusb->gfxinit || first < 1 || last < 1) {
1450		mutex_unlock(&sisusb->lock);
1451		return 0;
1452	}
1453
1454	sisusb->sisusb_cursor_loc       = -1;
1455	sisusb->sisusb_cursor_size_from = -1;
1456	sisusb->sisusb_cursor_size_to   = -1;
1457
1458	/* Set up text mode (and upload  default font) */
1459	if (sisusb_reset_text_mode(sisusb, 1)) {
1460		mutex_unlock(&sisusb->lock);
1461		dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n");
1462		return 1;
1463	}
1464
1465	/* Initialize some gfx registers */
1466	sisusb_initialize(sisusb);
1467
1468	for (i = first - 1; i <= last - 1; i++) {
1469		/* Save sisusb for our interface routines */
1470		mysisusbs[i] = sisusb;
1471	}
1472
1473	/* Initial console setup */
1474	sisusb->sisusb_num_columns = 80;
1475
1476	/* Use a 32K buffer (matches b8000-bffff area) */
1477	sisusb->scrbuf_size = 32 * 1024;
1478
1479	/* Allocate screen buffer */
1480	if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) {
1481		mutex_unlock(&sisusb->lock);
1482		dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n");
1483		return 1;
1484	}
1485
1486	mutex_unlock(&sisusb->lock);
1487
1488	/* Now grab the desired console(s) */
1489	ret = take_over_console(&sisusb_con, first - 1, last - 1, 0);
1490
1491	if (!ret)
1492		sisusb->haveconsole = 1;
1493	else {
1494		for (i = first - 1; i <= last - 1; i++)
1495			mysisusbs[i] = NULL;
1496	}
1497
1498	return ret;
1499}
1500
1501void
1502sisusb_console_exit(struct sisusb_usb_data *sisusb)
1503{
1504	int i;
1505
1506	/* This is called if the device is disconnected
1507	 * and while disconnect and lock semaphores
1508	 * are up. This should be save because we
1509	 * can't lose our sisusb any other way but by
1510	 * disconnection (and hence, the disconnect
1511	 * sema is for protecting all other access
1512	 * functions from disconnection, not the
1513	 * other way round).
1514	 */
1515
1516	/* Now what do we do in case of disconnection:
1517	 * One alternative would be to simply call
1518	 * give_up_console(). Nah, not a good idea.
1519	 * give_up_console() is obviously buggy as it
1520	 * only discards the consw pointer from the
1521	 * driver_map, but doesn't adapt vc->vc_sw
1522	 * of the affected consoles. Hence, the next
1523	 * call to any of the console functions will
1524	 * eventually take a trip to oops county.
1525	 * Also, give_up_console for some reason
1526	 * doesn't decrement our module refcount.
1527	 * Instead, we switch our consoles to a private
1528	 * dummy console. This, of course, keeps our
1529	 * refcount up as well, but it works perfectly.
1530	 */
1531
1532	if (sisusb->haveconsole) {
1533		for (i = 0; i < MAX_NR_CONSOLES; i++)
1534			if (sisusb->havethisconsole[i])
1535				take_over_console(&sisusb_dummy_con, i, i, 0);
1536				/* At this point, con_deinit for all our
1537				 * consoles is executed by take_over_console().
1538				 */
1539		sisusb->haveconsole = 0;
1540	}
1541
1542	vfree((void *)sisusb->scrbuf);
1543	sisusb->scrbuf = 0;
1544
1545	vfree(sisusb->font_backup);
1546	sisusb->font_backup = NULL;
1547}
1548
1549void __init sisusb_init_concode(void)
1550{
1551	int i;
1552
1553	for (i = 0; i < MAX_NR_CONSOLES; i++)
1554		mysisusbs[i] = NULL;
1555}
1556
1557#endif /* INCL_CON */
1558