Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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