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