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