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 ssize_t written; 374 375 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 376 if (!sisusb) 377 return; 378 379 /* sisusb->lock is down */ 380 if (sisusb_is_inactive(c, sisusb)) { 381 mutex_unlock(&sisusb->lock); 382 return; 383 } 384 385 386 sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y), 387 (long)SISUSB_HADDR(x, y), 2, &written); 388 389 mutex_unlock(&sisusb->lock); 390} 391 392/* Interface routine */ 393static void 394sisusbcon_putcs(struct vc_data *c, const unsigned short *s, 395 int count, int y, int x) 396{ 397 struct sisusb_usb_data *sisusb; 398 ssize_t written; 399 u16 *dest; 400 int i; 401 402 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 403 if (!sisusb) 404 return; 405 406 /* sisusb->lock is down */ 407 408 /* Need to put the characters into the buffer ourselves, 409 * because the vt does this AFTER calling us. 410 */ 411 412 dest = SISUSB_VADDR(x, y); 413 414 for (i = count; i > 0; i--) 415 sisusbcon_writew(sisusbcon_readw(s++), dest++); 416 417 if (sisusb_is_inactive(c, sisusb)) { 418 mutex_unlock(&sisusb->lock); 419 return; 420 } 421 422 sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(x, y), 423 (long)SISUSB_HADDR(x, y), count * 2, &written); 424 425 mutex_unlock(&sisusb->lock); 426} 427 428/* Interface routine */ 429static void 430sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width) 431{ 432 struct sisusb_usb_data *sisusb; 433 u16 eattr = c->vc_video_erase_char; 434 ssize_t written; 435 int i, length, cols; 436 u16 *dest; 437 438 if (width <= 0 || height <= 0) 439 return; 440 441 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 442 if (!sisusb) 443 return; 444 445 /* sisusb->lock is down */ 446 447 /* Need to clear buffer ourselves, because the vt does 448 * this AFTER calling us. 449 */ 450 451 dest = SISUSB_VADDR(x, y); 452 453 cols = sisusb->sisusb_num_columns; 454 455 if (width > cols) 456 width = cols; 457 458 if (x == 0 && width >= c->vc_cols) { 459 460 sisusbcon_memsetw(dest, eattr, height * cols * 2); 461 462 } else { 463 464 for (i = height; i > 0; i--, dest += cols) 465 sisusbcon_memsetw(dest, eattr, width * 2); 466 467 } 468 469 if (sisusb_is_inactive(c, sisusb)) { 470 mutex_unlock(&sisusb->lock); 471 return; 472 } 473 474 length = ((height * cols) - x - (cols - width - x)) * 2; 475 476 477 sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(x, y), 478 (long)SISUSB_HADDR(x, y), length, &written); 479 480 mutex_unlock(&sisusb->lock); 481} 482 483/* Interface routine */ 484static void 485sisusbcon_bmove(struct vc_data *c, int sy, int sx, 486 int dy, int dx, int height, int width) 487{ 488 struct sisusb_usb_data *sisusb; 489 ssize_t written; 490 int cols, length; 491 492 if (width <= 0 || height <= 0) 493 return; 494 495 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 496 if (!sisusb) 497 return; 498 499 /* sisusb->lock is down */ 500 501 cols = sisusb->sisusb_num_columns; 502 503 if (sisusb_is_inactive(c, sisusb)) { 504 mutex_unlock(&sisusb->lock); 505 return; 506 } 507 508 length = ((height * cols) - dx - (cols - width - dx)) * 2; 509 510 511 sisusb_copy_memory(sisusb, (unsigned char *)SISUSB_VADDR(dx, dy), 512 (long)SISUSB_HADDR(dx, dy), length, &written); 513 514 mutex_unlock(&sisusb->lock); 515} 516 517/* interface routine */ 518static int 519sisusbcon_switch(struct vc_data *c) 520{ 521 struct sisusb_usb_data *sisusb; 522 ssize_t written; 523 int length; 524 525 /* Returnvalue 0 means we have fully restored screen, 526 * and vt doesn't need to call do_update_region(). 527 * Returnvalue != 0 naturally means the opposite. 528 */ 529 530 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 531 if (!sisusb) 532 return 0; 533 534 /* sisusb->lock is down */ 535 536 /* Don't write to screen if in gfx mode */ 537 if (sisusb_is_inactive(c, sisusb)) { 538 mutex_unlock(&sisusb->lock); 539 return 0; 540 } 541 542 /* That really should not happen. It would mean we are 543 * being called while the vc is using its private buffer 544 * as origin. 545 */ 546 if (c->vc_origin == (unsigned long)c->vc_screenbuf) { 547 mutex_unlock(&sisusb->lock); 548 dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n"); 549 return 0; 550 } 551 552 /* Check that we don't copy too much */ 553 length = min((int)c->vc_screenbuf_size, 554 (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); 555 556 /* Restore the screen contents */ 557 sisusbcon_memcpyw((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, 558 length); 559 560 sisusb_copy_memory(sisusb, (unsigned char *)c->vc_origin, 561 (long)SISUSB_HADDR(0, 0), 562 length, &written); 563 564 mutex_unlock(&sisusb->lock); 565 566 return 0; 567} 568 569/* interface routine */ 570static void 571sisusbcon_save_screen(struct vc_data *c) 572{ 573 struct sisusb_usb_data *sisusb; 574 int length; 575 576 /* Save the current screen contents to vc's private 577 * buffer. 578 */ 579 580 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 581 if (!sisusb) 582 return; 583 584 /* sisusb->lock is down */ 585 586 if (sisusb_is_inactive(c, sisusb)) { 587 mutex_unlock(&sisusb->lock); 588 return; 589 } 590 591 /* Check that we don't copy too much */ 592 length = min((int)c->vc_screenbuf_size, 593 (int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); 594 595 /* Save the screen contents to vc's private buffer */ 596 sisusbcon_memcpyw((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin, 597 length); 598 599 mutex_unlock(&sisusb->lock); 600} 601 602/* interface routine */ 603static int 604sisusbcon_set_palette(struct vc_data *c, unsigned char *table) 605{ 606 struct sisusb_usb_data *sisusb; 607 int i, j; 608 609 /* Return value not used by vt */ 610 611 if (!CON_IS_VISIBLE(c)) 612 return -EINVAL; 613 614 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 615 if (!sisusb) 616 return -EINVAL; 617 618 /* sisusb->lock is down */ 619 620 if (sisusb_is_inactive(c, sisusb)) { 621 mutex_unlock(&sisusb->lock); 622 return -EINVAL; 623 } 624 625 for (i = j = 0; i < 16; i++) { 626 if (sisusb_setreg(sisusb, SISCOLIDX, table[i])) 627 break; 628 if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) 629 break; 630 if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) 631 break; 632 if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) 633 break; 634 } 635 636 mutex_unlock(&sisusb->lock); 637 638 return 0; 639} 640 641/* interface routine */ 642static int 643sisusbcon_blank(struct vc_data *c, int blank, int mode_switch) 644{ 645 struct sisusb_usb_data *sisusb; 646 u8 sr1, cr17, pmreg, cr63; 647 ssize_t written; 648 int ret = 0; 649 650 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 651 if (!sisusb) 652 return 0; 653 654 /* sisusb->lock is down */ 655 656 if (mode_switch) 657 sisusb->is_gfx = blank ? 1 : 0; 658 659 if (sisusb_is_inactive(c, sisusb)) { 660 mutex_unlock(&sisusb->lock); 661 return 0; 662 } 663 664 switch (blank) { 665 666 case 1: /* Normal blanking: Clear screen */ 667 case -1: 668 sisusbcon_memsetw((u16 *)c->vc_origin, 669 c->vc_video_erase_char, 670 c->vc_screenbuf_size); 671 sisusb_copy_memory(sisusb, 672 (unsigned char *)c->vc_origin, 673 (u32)(sisusb->vrambase + 674 (c->vc_origin - sisusb->scrbuf)), 675 c->vc_screenbuf_size, &written); 676 sisusb->con_blanked = 1; 677 ret = 1; 678 break; 679 680 default: /* VESA blanking */ 681 switch (blank) { 682 case 0: /* Unblank */ 683 sr1 = 0x00; 684 cr17 = 0x80; 685 pmreg = 0x00; 686 cr63 = 0x00; 687 ret = 1; 688 sisusb->con_blanked = 0; 689 break; 690 case VESA_VSYNC_SUSPEND + 1: 691 sr1 = 0x20; 692 cr17 = 0x80; 693 pmreg = 0x80; 694 cr63 = 0x40; 695 break; 696 case VESA_HSYNC_SUSPEND + 1: 697 sr1 = 0x20; 698 cr17 = 0x80; 699 pmreg = 0x40; 700 cr63 = 0x40; 701 break; 702 case VESA_POWERDOWN + 1: 703 sr1 = 0x20; 704 cr17 = 0x00; 705 pmreg = 0xc0; 706 cr63 = 0x40; 707 break; 708 default: 709 mutex_unlock(&sisusb->lock); 710 return -EINVAL; 711 } 712 713 sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1); 714 sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17); 715 sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg); 716 sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63); 717 718 } 719 720 mutex_unlock(&sisusb->lock); 721 722 return ret; 723} 724 725/* interface routine */ 726static int 727sisusbcon_scrolldelta(struct vc_data *c, int lines) 728{ 729 struct sisusb_usb_data *sisusb; 730 int margin = c->vc_size_row * 4; 731 int ul, we, p, st; 732 733 /* The return value does not seem to be used */ 734 735 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 736 if (!sisusb) 737 return 0; 738 739 /* sisusb->lock is down */ 740 741 if (sisusb_is_inactive(c, sisusb)) { 742 mutex_unlock(&sisusb->lock); 743 return 0; 744 } 745 746 if (!lines) /* Turn scrollback off */ 747 c->vc_visible_origin = c->vc_origin; 748 else { 749 750 if (sisusb->con_rolled_over > 751 (c->vc_scr_end - sisusb->scrbuf) + margin) { 752 753 ul = c->vc_scr_end - sisusb->scrbuf; 754 we = sisusb->con_rolled_over + c->vc_size_row; 755 756 } else { 757 758 ul = 0; 759 we = sisusb->scrbuf_size; 760 761 } 762 763 p = (c->vc_visible_origin - sisusb->scrbuf - ul + we) % we + 764 lines * c->vc_size_row; 765 766 st = (c->vc_origin - sisusb->scrbuf - ul + we) % we; 767 768 if (st < 2 * margin) 769 margin = 0; 770 771 if (p < margin) 772 p = 0; 773 774 if (p > st - margin) 775 p = st; 776 777 c->vc_visible_origin = sisusb->scrbuf + (p + ul) % we; 778 } 779 780 sisusbcon_set_start_address(sisusb, c); 781 782 mutex_unlock(&sisusb->lock); 783 784 return 1; 785} 786 787/* Interface routine */ 788static void 789sisusbcon_cursor(struct vc_data *c, int mode) 790{ 791 struct sisusb_usb_data *sisusb; 792 int from, to, baseline; 793 794 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 795 if (!sisusb) 796 return; 797 798 /* sisusb->lock is down */ 799 800 if (sisusb_is_inactive(c, sisusb)) { 801 mutex_unlock(&sisusb->lock); 802 return; 803 } 804 805 if (c->vc_origin != c->vc_visible_origin) { 806 c->vc_visible_origin = c->vc_origin; 807 sisusbcon_set_start_address(sisusb, c); 808 } 809 810 if (mode == CM_ERASE) { 811 sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20); 812 sisusb->sisusb_cursor_size_to = -1; 813 mutex_unlock(&sisusb->lock); 814 return; 815 } 816 817 sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2); 818 819 baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2); 820 821 switch (c->vc_cursor_type & 0x0f) { 822 case CUR_BLOCK: from = 1; 823 to = c->vc_font.height; 824 break; 825 case CUR_TWO_THIRDS: from = c->vc_font.height / 3; 826 to = baseline; 827 break; 828 case CUR_LOWER_HALF: from = c->vc_font.height / 2; 829 to = baseline; 830 break; 831 case CUR_LOWER_THIRD: from = (c->vc_font.height * 2) / 3; 832 to = baseline; 833 break; 834 case CUR_NONE: from = 31; 835 to = 30; 836 break; 837 default: 838 case CUR_UNDERLINE: from = baseline - 1; 839 to = baseline; 840 break; 841 } 842 843 if (sisusb->sisusb_cursor_size_from != from || 844 sisusb->sisusb_cursor_size_to != to) { 845 846 sisusb_setidxreg(sisusb, SISCR, 0x0a, from); 847 sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to); 848 849 sisusb->sisusb_cursor_size_from = from; 850 sisusb->sisusb_cursor_size_to = to; 851 } 852 853 mutex_unlock(&sisusb->lock); 854} 855 856static int 857sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb, 858 int t, int b, int dir, int lines) 859{ 860 int cols = sisusb->sisusb_num_columns; 861 int length = ((b - t) * cols) * 2; 862 u16 eattr = c->vc_video_erase_char; 863 ssize_t written; 864 865 /* sisusb->lock is down */ 866 867 /* Scroll an area which does not match the 868 * visible screen's dimensions. This needs 869 * to be done separately, as it does not 870 * use hardware panning. 871 */ 872 873 switch (dir) { 874 875 case SM_UP: 876 sisusbcon_memmovew(SISUSB_VADDR(0, t), 877 SISUSB_VADDR(0, t + lines), 878 (b - t - lines) * cols * 2); 879 sisusbcon_memsetw(SISUSB_VADDR(0, b - lines), eattr, 880 lines * cols * 2); 881 break; 882 883 case SM_DOWN: 884 sisusbcon_memmovew(SISUSB_VADDR(0, t + lines), 885 SISUSB_VADDR(0, t), 886 (b - t - lines) * cols * 2); 887 sisusbcon_memsetw(SISUSB_VADDR(0, t), eattr, 888 lines * cols * 2); 889 break; 890 } 891 892 sisusb_copy_memory(sisusb, (char *)SISUSB_VADDR(0, t), 893 (long)SISUSB_HADDR(0, t), length, &written); 894 895 mutex_unlock(&sisusb->lock); 896 897 return 1; 898} 899 900/* Interface routine */ 901static int 902sisusbcon_scroll(struct vc_data *c, int t, int b, int dir, int lines) 903{ 904 struct sisusb_usb_data *sisusb; 905 u16 eattr = c->vc_video_erase_char; 906 ssize_t written; 907 int copyall = 0; 908 unsigned long oldorigin; 909 unsigned int delta = lines * c->vc_size_row; 910 u32 originoffset; 911 912 /* Returning != 0 means we have done the scrolling successfully. 913 * Returning 0 makes vt do the scrolling on its own. 914 * Note that con_scroll is only called if the console is 915 * visible. In that case, the origin should be our buffer, 916 * not the vt's private one. 917 */ 918 919 if (!lines) 920 return 1; 921 922 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 923 if (!sisusb) 924 return 0; 925 926 /* sisusb->lock is down */ 927 928 if (sisusb_is_inactive(c, sisusb)) { 929 mutex_unlock(&sisusb->lock); 930 return 0; 931 } 932 933 /* Special case */ 934 if (t || b != c->vc_rows) 935 return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines); 936 937 if (c->vc_origin != c->vc_visible_origin) { 938 c->vc_visible_origin = c->vc_origin; 939 sisusbcon_set_start_address(sisusb, c); 940 } 941 942 /* limit amount to maximum realistic size */ 943 if (lines > c->vc_rows) 944 lines = c->vc_rows; 945 946 oldorigin = c->vc_origin; 947 948 switch (dir) { 949 950 case SM_UP: 951 952 if (c->vc_scr_end + delta >= 953 sisusb->scrbuf + sisusb->scrbuf_size) { 954 sisusbcon_memcpyw((u16 *)sisusb->scrbuf, 955 (u16 *)(oldorigin + delta), 956 c->vc_screenbuf_size - delta); 957 c->vc_origin = sisusb->scrbuf; 958 sisusb->con_rolled_over = oldorigin - sisusb->scrbuf; 959 copyall = 1; 960 } else 961 c->vc_origin += delta; 962 963 sisusbcon_memsetw( 964 (u16 *)(c->vc_origin + c->vc_screenbuf_size - delta), 965 eattr, delta); 966 967 break; 968 969 case SM_DOWN: 970 971 if (oldorigin - delta < sisusb->scrbuf) { 972 sisusbcon_memmovew((u16 *)(sisusb->scrbuf + 973 sisusb->scrbuf_size - 974 c->vc_screenbuf_size + 975 delta), 976 (u16 *)oldorigin, 977 c->vc_screenbuf_size - delta); 978 c->vc_origin = sisusb->scrbuf + 979 sisusb->scrbuf_size - 980 c->vc_screenbuf_size; 981 sisusb->con_rolled_over = 0; 982 copyall = 1; 983 } else 984 c->vc_origin -= delta; 985 986 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; 987 988 scr_memsetw((u16 *)(c->vc_origin), eattr, delta); 989 990 break; 991 } 992 993 originoffset = (u32)(c->vc_origin - sisusb->scrbuf); 994 995 if (copyall) 996 sisusb_copy_memory(sisusb, 997 (char *)c->vc_origin, 998 (u32)(sisusb->vrambase + originoffset), 999 c->vc_screenbuf_size, &written); 1000 else if (dir == SM_UP) 1001 sisusb_copy_memory(sisusb, 1002 (char *)c->vc_origin + c->vc_screenbuf_size - delta, 1003 (u32)sisusb->vrambase + originoffset + 1004 c->vc_screenbuf_size - delta, 1005 delta, &written); 1006 else 1007 sisusb_copy_memory(sisusb, 1008 (char *)c->vc_origin, 1009 (u32)(sisusb->vrambase + originoffset), 1010 delta, &written); 1011 1012 c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; 1013 c->vc_visible_origin = c->vc_origin; 1014 1015 sisusbcon_set_start_address(sisusb, c); 1016 1017 c->vc_pos = c->vc_pos - oldorigin + c->vc_origin; 1018 1019 mutex_unlock(&sisusb->lock); 1020 1021 return 1; 1022} 1023 1024/* Interface routine */ 1025static int 1026sisusbcon_set_origin(struct vc_data *c) 1027{ 1028 struct sisusb_usb_data *sisusb; 1029 1030 /* Returning != 0 means we were successful. 1031 * Returning 0 will vt make to use its own 1032 * screenbuffer as the origin. 1033 */ 1034 1035 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 1036 if (!sisusb) 1037 return 0; 1038 1039 /* sisusb->lock is down */ 1040 1041 if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) { 1042 mutex_unlock(&sisusb->lock); 1043 return 0; 1044 } 1045 1046 c->vc_origin = c->vc_visible_origin = sisusb->scrbuf; 1047 1048 sisusbcon_set_start_address(sisusb, c); 1049 1050 sisusb->con_rolled_over = 0; 1051 1052 mutex_unlock(&sisusb->lock); 1053 1054 return 1; 1055} 1056 1057/* Interface routine */ 1058static int 1059sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows, 1060 unsigned int user) 1061{ 1062 struct sisusb_usb_data *sisusb; 1063 int fh; 1064 1065 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 1066 if (!sisusb) 1067 return -ENODEV; 1068 1069 fh = sisusb->current_font_height; 1070 1071 mutex_unlock(&sisusb->lock); 1072 1073 /* We are quite unflexible as regards resizing. The vt code 1074 * handles sizes where the line length isn't equal the pitch 1075 * quite badly. As regards the rows, our panning tricks only 1076 * work well if the number of rows equals the visible number 1077 * of rows. 1078 */ 1079 1080 if (newcols != 80 || c->vc_scan_lines / fh != newrows) 1081 return -EINVAL; 1082 1083 return 0; 1084} 1085 1086int 1087sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, 1088 u8 *arg, int cmapsz, int ch512, int dorecalc, 1089 struct vc_data *c, int fh, int uplock) 1090{ 1091 int font_select = 0x00, i, err = 0; 1092 u32 offset = 0; 1093 u8 dummy; 1094 1095 /* sisusb->lock is down */ 1096 1097 /* 1098 * The default font is kept in slot 0. 1099 * A user font is loaded in slot 2 (256 ch) 1100 * or 2+3 (512 ch). 1101 */ 1102 1103 if ((slot != 0 && slot != 2) || !fh) { 1104 if (uplock) 1105 mutex_unlock(&sisusb->lock); 1106 return -EINVAL; 1107 } 1108 1109 if (set) 1110 sisusb->font_slot = slot; 1111 1112 /* Default font is always 256 */ 1113 if (slot == 0) 1114 ch512 = 0; 1115 else 1116 offset = 4 * cmapsz; 1117 1118 font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a); 1119 1120 err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ 1121 err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */ 1122 err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */ 1123 err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */ 1124 1125 if (err) 1126 goto font_op_error; 1127 1128 err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */ 1129 err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */ 1130 err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */ 1131 1132 if (err) 1133 goto font_op_error; 1134 1135 if (arg) { 1136 if (set) 1137 for (i = 0; i < cmapsz; i++) { 1138 err |= sisusb_writeb(sisusb, 1139 sisusb->vrambase + offset + i, 1140 arg[i]); 1141 if (err) 1142 break; 1143 } 1144 else 1145 for (i = 0; i < cmapsz; i++) { 1146 err |= sisusb_readb(sisusb, 1147 sisusb->vrambase + offset + i, 1148 &arg[i]); 1149 if (err) 1150 break; 1151 } 1152 1153 /* 1154 * In 512-character mode, the character map is not contiguous if 1155 * we want to remain EGA compatible -- which we do 1156 */ 1157 1158 if (ch512) { 1159 if (set) 1160 for (i = 0; i < cmapsz; i++) { 1161 err |= sisusb_writeb(sisusb, 1162 sisusb->vrambase + offset + 1163 (2 * cmapsz) + i, 1164 arg[cmapsz + i]); 1165 if (err) 1166 break; 1167 } 1168 else 1169 for (i = 0; i < cmapsz; i++) { 1170 err |= sisusb_readb(sisusb, 1171 sisusb->vrambase + offset + 1172 (2 * cmapsz) + i, 1173 &arg[cmapsz + i]); 1174 if (err) 1175 break; 1176 } 1177 } 1178 } 1179 1180 if (err) 1181 goto font_op_error; 1182 1183 err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ 1184 err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */ 1185 err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */ 1186 if (set) 1187 sisusb_setidxreg(sisusb, SISSR, 0x03, font_select); 1188 err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */ 1189 1190 if (err) 1191 goto font_op_error; 1192 1193 err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */ 1194 err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */ 1195 err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */ 1196 1197 if (err) 1198 goto font_op_error; 1199 1200 if ((set) && (ch512 != sisusb->current_font_512)) { 1201 1202 /* Font is shared among all our consoles. 1203 * And so is the hi_font_mask. 1204 */ 1205 for (i = 0; i < MAX_NR_CONSOLES; i++) { 1206 struct vc_data *d = vc_cons[i].d; 1207 if (d && d->vc_sw == &sisusb_con) 1208 d->vc_hi_font_mask = ch512 ? 0x0800 : 0; 1209 } 1210 1211 sisusb->current_font_512 = ch512; 1212 1213 /* color plane enable register: 1214 256-char: enable intensity bit 1215 512-char: disable intensity bit */ 1216 sisusb_getreg(sisusb, SISINPSTAT, &dummy); 1217 sisusb_setreg(sisusb, SISAR, 0x12); 1218 sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f); 1219 1220 sisusb_getreg(sisusb, SISINPSTAT, &dummy); 1221 sisusb_setreg(sisusb, SISAR, 0x20); 1222 sisusb_getreg(sisusb, SISINPSTAT, &dummy); 1223 } 1224 1225 if (dorecalc) { 1226 1227 /* 1228 * Adjust the screen to fit a font of a certain height 1229 */ 1230 1231 unsigned char ovr, vde, fsr; 1232 int rows = 0, maxscan = 0; 1233 1234 if (c) { 1235 1236 /* Number of video rows */ 1237 rows = c->vc_scan_lines / fh; 1238 /* Scan lines to actually display-1 */ 1239 maxscan = rows * fh - 1; 1240 1241 /*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n", 1242 rows, maxscan, fh, c->vc_scan_lines);*/ 1243 1244 sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr); 1245 vde = maxscan & 0xff; 1246 ovr = (ovr & 0xbd) | 1247 ((maxscan & 0x100) >> 7) | 1248 ((maxscan & 0x200) >> 3); 1249 sisusb_setidxreg(sisusb, SISCR, 0x07, ovr); 1250 sisusb_setidxreg(sisusb, SISCR, 0x12, vde); 1251 1252 } 1253 1254 sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr); 1255 fsr = (fsr & 0xe0) | (fh - 1); 1256 sisusb_setidxreg(sisusb, SISCR, 0x09, fsr); 1257 sisusb->current_font_height = fh; 1258 1259 sisusb->sisusb_cursor_size_from = -1; 1260 sisusb->sisusb_cursor_size_to = -1; 1261 1262 } 1263 1264 if (uplock) 1265 mutex_unlock(&sisusb->lock); 1266 1267 if (dorecalc && c) { 1268 int rows = c->vc_scan_lines / fh; 1269 1270 /* Now adjust our consoles' size */ 1271 1272 for (i = 0; i < MAX_NR_CONSOLES; i++) { 1273 struct vc_data *vc = vc_cons[i].d; 1274 1275 if (vc && vc->vc_sw == &sisusb_con) { 1276 if (CON_IS_VISIBLE(vc)) { 1277 vc->vc_sw->con_cursor(vc, CM_DRAW); 1278 } 1279 vc->vc_font.height = fh; 1280 vc_resize(vc, 0, rows); 1281 } 1282 } 1283 } 1284 1285 return 0; 1286 1287font_op_error: 1288 if (uplock) 1289 mutex_unlock(&sisusb->lock); 1290 1291 return -EIO; 1292} 1293 1294/* Interface routine */ 1295static int 1296sisusbcon_font_set(struct vc_data *c, struct console_font *font, 1297 unsigned flags) 1298{ 1299 struct sisusb_usb_data *sisusb; 1300 unsigned charcount = font->charcount; 1301 1302 if (font->width != 8 || (charcount != 256 && charcount != 512)) 1303 return -EINVAL; 1304 1305 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 1306 if (!sisusb) 1307 return -ENODEV; 1308 1309 /* sisusb->lock is down */ 1310 1311 /* Save the user-provided font into a buffer. This 1312 * is used for restoring text mode after quitting 1313 * from X and for the con_getfont routine. 1314 */ 1315 if (sisusb->font_backup) { 1316 if (sisusb->font_backup_size < charcount) { 1317 vfree(sisusb->font_backup); 1318 sisusb->font_backup = NULL; 1319 } 1320 } 1321 1322 if (!sisusb->font_backup) 1323 sisusb->font_backup = vmalloc(charcount * 32); 1324 1325 if (sisusb->font_backup) { 1326 memcpy(sisusb->font_backup, font->data, charcount * 32); 1327 sisusb->font_backup_size = charcount; 1328 sisusb->font_backup_height = font->height; 1329 sisusb->font_backup_512 = (charcount == 512) ? 1 : 0; 1330 } 1331 1332 /* do_font_op ups sisusb->lock */ 1333 1334 return sisusbcon_do_font_op(sisusb, 1, 2, font->data, 1335 8192, (charcount == 512), 1336 (!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0, 1337 c, font->height, 1); 1338} 1339 1340/* Interface routine */ 1341static int 1342sisusbcon_font_get(struct vc_data *c, struct console_font *font) 1343{ 1344 struct sisusb_usb_data *sisusb; 1345 1346 sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); 1347 if (!sisusb) 1348 return -ENODEV; 1349 1350 /* sisusb->lock is down */ 1351 1352 font->width = 8; 1353 font->height = c->vc_font.height; 1354 font->charcount = 256; 1355 1356 if (!font->data) { 1357 mutex_unlock(&sisusb->lock); 1358 return 0; 1359 } 1360 1361 if (!sisusb->font_backup) { 1362 mutex_unlock(&sisusb->lock); 1363 return -ENODEV; 1364 } 1365 1366 /* Copy 256 chars only, like vgacon */ 1367 memcpy(font->data, sisusb->font_backup, 256 * 32); 1368 1369 mutex_unlock(&sisusb->lock); 1370 1371 return 0; 1372} 1373 1374/* 1375 * The console `switch' structure for the sisusb console 1376 */ 1377 1378static const struct consw sisusb_con = { 1379 .owner = THIS_MODULE, 1380 .con_startup = sisusbcon_startup, 1381 .con_init = sisusbcon_init, 1382 .con_deinit = sisusbcon_deinit, 1383 .con_clear = sisusbcon_clear, 1384 .con_putc = sisusbcon_putc, 1385 .con_putcs = sisusbcon_putcs, 1386 .con_cursor = sisusbcon_cursor, 1387 .con_scroll = sisusbcon_scroll, 1388 .con_bmove = sisusbcon_bmove, 1389 .con_switch = sisusbcon_switch, 1390 .con_blank = sisusbcon_blank, 1391 .con_font_set = sisusbcon_font_set, 1392 .con_font_get = sisusbcon_font_get, 1393 .con_set_palette = sisusbcon_set_palette, 1394 .con_scrolldelta = sisusbcon_scrolldelta, 1395 .con_build_attr = sisusbcon_build_attr, 1396 .con_invert_region = sisusbcon_invert_region, 1397 .con_set_origin = sisusbcon_set_origin, 1398 .con_save_screen = sisusbcon_save_screen, 1399 .con_resize = sisusbcon_resize, 1400}; 1401 1402/* Our very own dummy console driver */ 1403 1404static const char *sisusbdummycon_startup(void) 1405{ 1406 return "SISUSBVGADUMMY"; 1407} 1408 1409static void sisusbdummycon_init(struct vc_data *vc, int init) 1410{ 1411 vc->vc_can_do_color = 1; 1412 if (init) { 1413 vc->vc_cols = 80; 1414 vc->vc_rows = 25; 1415 } else 1416 vc_resize(vc, 80, 25); 1417} 1418 1419static int sisusbdummycon_dummy(void) 1420{ 1421 return 0; 1422} 1423 1424#define SISUSBCONDUMMY (void *)sisusbdummycon_dummy 1425 1426static const struct consw sisusb_dummy_con = { 1427 .owner = THIS_MODULE, 1428 .con_startup = sisusbdummycon_startup, 1429 .con_init = sisusbdummycon_init, 1430 .con_deinit = SISUSBCONDUMMY, 1431 .con_clear = SISUSBCONDUMMY, 1432 .con_putc = SISUSBCONDUMMY, 1433 .con_putcs = SISUSBCONDUMMY, 1434 .con_cursor = SISUSBCONDUMMY, 1435 .con_scroll = SISUSBCONDUMMY, 1436 .con_bmove = SISUSBCONDUMMY, 1437 .con_switch = SISUSBCONDUMMY, 1438 .con_blank = SISUSBCONDUMMY, 1439 .con_font_set = SISUSBCONDUMMY, 1440 .con_font_get = SISUSBCONDUMMY, 1441 .con_font_default = SISUSBCONDUMMY, 1442 .con_font_copy = SISUSBCONDUMMY, 1443 .con_set_palette = SISUSBCONDUMMY, 1444 .con_scrolldelta = SISUSBCONDUMMY, 1445}; 1446 1447int 1448sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) 1449{ 1450 int i, ret; 1451 1452 mutex_lock(&sisusb->lock); 1453 1454 /* Erm.. that should not happen */ 1455 if (sisusb->haveconsole || !sisusb->SiS_Pr) { 1456 mutex_unlock(&sisusb->lock); 1457 return 1; 1458 } 1459 1460 sisusb->con_first = first; 1461 sisusb->con_last = last; 1462 1463 if (first > last || 1464 first > MAX_NR_CONSOLES || 1465 last > MAX_NR_CONSOLES) { 1466 mutex_unlock(&sisusb->lock); 1467 return 1; 1468 } 1469 1470 /* If gfxcore not initialized or no consoles given, quit graciously */ 1471 if (!sisusb->gfxinit || first < 1 || last < 1) { 1472 mutex_unlock(&sisusb->lock); 1473 return 0; 1474 } 1475 1476 sisusb->sisusb_cursor_loc = -1; 1477 sisusb->sisusb_cursor_size_from = -1; 1478 sisusb->sisusb_cursor_size_to = -1; 1479 1480 /* Set up text mode (and upload default font) */ 1481 if (sisusb_reset_text_mode(sisusb, 1)) { 1482 mutex_unlock(&sisusb->lock); 1483 dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n"); 1484 return 1; 1485 } 1486 1487 /* Initialize some gfx registers */ 1488 sisusb_initialize(sisusb); 1489 1490 for (i = first - 1; i <= last - 1; i++) { 1491 /* Save sisusb for our interface routines */ 1492 mysisusbs[i] = sisusb; 1493 } 1494 1495 /* Initial console setup */ 1496 sisusb->sisusb_num_columns = 80; 1497 1498 /* Use a 32K buffer (matches b8000-bffff area) */ 1499 sisusb->scrbuf_size = 32 * 1024; 1500 1501 /* Allocate screen buffer */ 1502 if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { 1503 mutex_unlock(&sisusb->lock); 1504 dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n"); 1505 return 1; 1506 } 1507 1508 mutex_unlock(&sisusb->lock); 1509 1510 /* Now grab the desired console(s) */ 1511 console_lock(); 1512 ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0); 1513 console_unlock(); 1514 if (!ret) 1515 sisusb->haveconsole = 1; 1516 else { 1517 for (i = first - 1; i <= last - 1; i++) 1518 mysisusbs[i] = NULL; 1519 } 1520 1521 return ret; 1522} 1523 1524void 1525sisusb_console_exit(struct sisusb_usb_data *sisusb) 1526{ 1527 int i; 1528 1529 /* This is called if the device is disconnected 1530 * and while disconnect and lock semaphores 1531 * are up. This should be save because we 1532 * can't lose our sisusb any other way but by 1533 * disconnection (and hence, the disconnect 1534 * sema is for protecting all other access 1535 * functions from disconnection, not the 1536 * other way round). 1537 */ 1538 1539 /* Now what do we do in case of disconnection: 1540 * One alternative would be to simply call 1541 * give_up_console(). Nah, not a good idea. 1542 * give_up_console() is obviously buggy as it 1543 * only discards the consw pointer from the 1544 * driver_map, but doesn't adapt vc->vc_sw 1545 * of the affected consoles. Hence, the next 1546 * call to any of the console functions will 1547 * eventually take a trip to oops county. 1548 * Also, give_up_console for some reason 1549 * doesn't decrement our module refcount. 1550 * Instead, we switch our consoles to a private 1551 * dummy console. This, of course, keeps our 1552 * refcount up as well, but it works perfectly. 1553 */ 1554 1555 if (sisusb->haveconsole) { 1556 for (i = 0; i < MAX_NR_CONSOLES; i++) 1557 if (sisusb->havethisconsole[i]) { 1558 console_lock(); 1559 do_take_over_console(&sisusb_dummy_con, i, i, 0); 1560 console_unlock(); 1561 /* At this point, con_deinit for all our 1562 * consoles is executed by do_take_over_console(). 1563 */ 1564 } 1565 sisusb->haveconsole = 0; 1566 } 1567 1568 vfree((void *)sisusb->scrbuf); 1569 sisusb->scrbuf = 0; 1570 1571 vfree(sisusb->font_backup); 1572 sisusb->font_backup = NULL; 1573} 1574 1575void __init sisusb_init_concode(void) 1576{ 1577 int i; 1578 1579 for (i = 0; i < MAX_NR_CONSOLES; i++) 1580 mysisusbs[i] = NULL; 1581} 1582 1583#endif /* INCL_CON */ 1584 1585 1586 1587