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