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