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