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 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 186static int 187sisusb_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 */ 198static const char * 199sisusbcon_startup(void) 200{ 201 return "SISUSBCON"; 202} 203 204/* con_init console interface routine */ 205static void 206sisusbcon_init(struct vc_data *c, int init) 207{ 208 struct sisusb_usb_data *sisusb; 209 int cols, rows; 210 211 /* This is called by do_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) 258 con_set_default_unimap(c); 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 */ 270static void 271sisusbcon_deinit(struct vc_data *c) 272{ 273 struct sisusb_usb_data *sisusb; 274 int i; 275 276 /* This is called by do_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 */ 309static u8 310sisusbcon_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 */ 335static void 336sisusbcon_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 */ 366static void 367sisusbcon_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 */ 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 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 */ 424static void 425sisusbcon_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 */ 478static void 479sisusbcon_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 */ 511static int 512sisusbcon_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 */ 562static void 563sisusbcon_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 */ 594static int 595sisusbcon_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 */ 632static int 633sisusbcon_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, 658 c->vc_video_erase_char, 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 */ 715static int 716sisusbcon_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 */ 735 c->vc_visible_origin = c->vc_origin; 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 */ 776static void 777sisusbcon_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) { 793 c->vc_visible_origin = c->vc_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 843static int 844sisusbcon_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 */ 888static int 889sisusbcon_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) { 924 c->vc_visible_origin = c->vc_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 - 966 c->vc_screenbuf_size; 967 sisusb->con_rolled_over = 0; 968 copyall = 1; 969 } else 970 c->vc_origin -= delta; 971 972 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; 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 998 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; 999 c->vc_visible_origin = c->vc_origin; 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 */ 1011static int 1012sisusbcon_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 */ 1043static int 1044sisusbcon_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 1070int 1071sisusbcon_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 1271font_op_error: 1272 if (uplock) 1273 mutex_unlock(&sisusb->lock); 1274 1275 return -EIO; 1276} 1277 1278/* Interface routine */ 1279static int 1280sisusbcon_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 */ 1324static int 1325sisusbcon_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 1360static 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 1386static const char *sisusbdummycon_startup(void) 1387{ 1388 return "SISUSBVGADUMMY"; 1389} 1390 1391static 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 1401static int sisusbdummycon_dummy(void) 1402{ 1403 return 0; 1404} 1405 1406#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy 1407 1408static 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 1429int 1430sisusb_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 console_lock(); 1494 ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0); 1495 console_unlock(); 1496 if (!ret) 1497 sisusb->haveconsole = 1; 1498 else { 1499 for (i = first - 1; i <= last - 1; i++) 1500 mysisusbs[i] = NULL; 1501 } 1502 1503 return ret; 1504} 1505 1506void 1507sisusb_console_exit(struct sisusb_usb_data *sisusb) 1508{ 1509 int i; 1510 1511 /* This is called if the device is disconnected 1512 * and while disconnect and lock semaphores 1513 * are up. This should be save because we 1514 * can't lose our sisusb any other way but by 1515 * disconnection (and hence, the disconnect 1516 * sema is for protecting all other access 1517 * functions from disconnection, not the 1518 * other way round). 1519 */ 1520 1521 /* Now what do we do in case of disconnection: 1522 * One alternative would be to simply call 1523 * give_up_console(). Nah, not a good idea. 1524 * give_up_console() is obviously buggy as it 1525 * only discards the consw pointer from the 1526 * driver_map, but doesn't adapt vc->vc_sw 1527 * of the affected consoles. Hence, the next 1528 * call to any of the console functions will 1529 * eventually take a trip to oops county. 1530 * Also, give_up_console for some reason 1531 * doesn't decrement our module refcount. 1532 * Instead, we switch our consoles to a private 1533 * dummy console. This, of course, keeps our 1534 * refcount up as well, but it works perfectly. 1535 */ 1536 1537 if (sisusb->haveconsole) { 1538 for (i = 0; i < MAX_NR_CONSOLES; i++) 1539 if (sisusb->havethisconsole[i]) { 1540 console_lock(); 1541 do_take_over_console(&sisusb_dummy_con, i, i, 0); 1542 console_unlock(); 1543 /* At this point, con_deinit for all our 1544 * consoles is executed by do_take_over_console(). 1545 */ 1546 } 1547 sisusb->haveconsole = 0; 1548 } 1549 1550 vfree((void *)sisusb->scrbuf); 1551 sisusb->scrbuf = 0; 1552 1553 vfree(sisusb->font_backup); 1554 sisusb->font_backup = NULL; 1555} 1556 1557void __init sisusb_init_concode(void) 1558{ 1559 int i; 1560 1561 for (i = 0; i < MAX_NR_CONSOLES; i++) 1562 mysisusbs[i] = NULL; 1563} 1564 1565#endif /* INCL_CON */ 1566 1567 1568 1569