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