forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1496 lines
35 KiB
1496 lines
35 KiB
// SPDX-License-Identifier: (GPL-2.0 OR BSD-3-Clause) |
|
/* |
|
* sisusb - usb kernel driver for SiS315(E) based USB2VGA dongles |
|
* |
|
* VGA text mode console part |
|
* |
|
* Copyright (C) 2005 by Thomas Winischhofer, Vienna, Austria |
|
* |
|
* If distributed as part of the Linux kernel, this code is licensed under the |
|
* terms of the GPL v2. |
|
* |
|
* Otherwise, the following license terms apply: |
|
* |
|
* * Redistribution and use in source and binary forms, with or without |
|
* * modification, are permitted provided that the following conditions |
|
* * are met: |
|
* * 1) Redistributions of source code must retain the above copyright |
|
* * notice, this list of conditions and the following disclaimer. |
|
* * 2) Redistributions in binary form must reproduce the above copyright |
|
* * notice, this list of conditions and the following disclaimer in the |
|
* * documentation and/or other materials provided with the distribution. |
|
* * 3) The name of the author may not be used to endorse or promote products |
|
* * derived from this software without specific psisusbr written permission. |
|
* * |
|
* * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESSED OR |
|
* * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES |
|
* * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
|
* * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, |
|
* * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
|
* * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
|
* * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
|
* * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|
* * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
|
* * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|
* |
|
* Author: Thomas Winischhofer <[email protected]> |
|
* |
|
* Portions based on vgacon.c which are |
|
* Created 28 Sep 1997 by Geert Uytterhoeven |
|
* Rewritten by Martin Mares <[email protected]>, July 1998 |
|
* based on code Copyright (C) 1991, 1992 Linus Torvalds |
|
* 1995 Jay Estabrook |
|
* |
|
* A note on using in_atomic() in here: We can't handle console |
|
* calls from non-schedulable context due to our USB-dependend |
|
* nature. For now, this driver just ignores any calls if it |
|
* detects this state. |
|
* |
|
*/ |
|
|
|
#include <linux/mutex.h> |
|
#include <linux/module.h> |
|
#include <linux/kernel.h> |
|
#include <linux/signal.h> |
|
#include <linux/fs.h> |
|
#include <linux/usb.h> |
|
#include <linux/tty.h> |
|
#include <linux/console.h> |
|
#include <linux/string.h> |
|
#include <linux/kd.h> |
|
#include <linux/init.h> |
|
#include <linux/vt_kern.h> |
|
#include <linux/selection.h> |
|
#include <linux/spinlock.h> |
|
#include <linux/kref.h> |
|
#include <linux/ioport.h> |
|
#include <linux/interrupt.h> |
|
#include <linux/vmalloc.h> |
|
|
|
#include "sisusb.h" |
|
#include "sisusb_init.h" |
|
|
|
/* vc_data -> sisusb conversion table */ |
|
static struct sisusb_usb_data *mysisusbs[MAX_NR_CONSOLES]; |
|
|
|
/* Forward declaration */ |
|
static const struct consw sisusb_con; |
|
|
|
static inline void |
|
sisusbcon_memsetw(u16 *s, u16 c, unsigned int count) |
|
{ |
|
memset16(s, c, count / 2); |
|
} |
|
|
|
static inline void |
|
sisusb_initialize(struct sisusb_usb_data *sisusb) |
|
{ |
|
/* Reset cursor and start address */ |
|
if (sisusb_setidxreg(sisusb, SISCR, 0x0c, 0x00)) |
|
return; |
|
if (sisusb_setidxreg(sisusb, SISCR, 0x0d, 0x00)) |
|
return; |
|
if (sisusb_setidxreg(sisusb, SISCR, 0x0e, 0x00)) |
|
return; |
|
sisusb_setidxreg(sisusb, SISCR, 0x0f, 0x00); |
|
} |
|
|
|
static inline void |
|
sisusbcon_set_start_address(struct sisusb_usb_data *sisusb, struct vc_data *c) |
|
{ |
|
sisusb->cur_start_addr = (c->vc_visible_origin - sisusb->scrbuf) / 2; |
|
|
|
sisusb_setidxreg(sisusb, SISCR, 0x0c, (sisusb->cur_start_addr >> 8)); |
|
sisusb_setidxreg(sisusb, SISCR, 0x0d, (sisusb->cur_start_addr & 0xff)); |
|
} |
|
|
|
void |
|
sisusb_set_cursor(struct sisusb_usb_data *sisusb, unsigned int location) |
|
{ |
|
if (sisusb->sisusb_cursor_loc == location) |
|
return; |
|
|
|
sisusb->sisusb_cursor_loc = location; |
|
|
|
/* Hardware bug: Text cursor appears twice or not at all |
|
* at some positions. Work around it with the cursor skew |
|
* bits. |
|
*/ |
|
|
|
if ((location & 0x0007) == 0x0007) { |
|
sisusb->bad_cursor_pos = 1; |
|
location--; |
|
if (sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0x1f, 0x20)) |
|
return; |
|
} else if (sisusb->bad_cursor_pos) { |
|
if (sisusb_setidxregand(sisusb, SISCR, 0x0b, 0x1f)) |
|
return; |
|
sisusb->bad_cursor_pos = 0; |
|
} |
|
|
|
if (sisusb_setidxreg(sisusb, SISCR, 0x0e, (location >> 8))) |
|
return; |
|
sisusb_setidxreg(sisusb, SISCR, 0x0f, (location & 0xff)); |
|
} |
|
|
|
static inline struct sisusb_usb_data * |
|
sisusb_get_sisusb(unsigned short console) |
|
{ |
|
return mysisusbs[console]; |
|
} |
|
|
|
static inline int |
|
sisusb_sisusb_valid(struct sisusb_usb_data *sisusb) |
|
{ |
|
if (!sisusb->present || !sisusb->ready || !sisusb->sisusb_dev) |
|
return 0; |
|
|
|
return 1; |
|
} |
|
|
|
static struct sisusb_usb_data * |
|
sisusb_get_sisusb_lock_and_check(unsigned short console) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
/* We can't handle console calls in non-schedulable |
|
* context due to our locks and the USB transport. |
|
* So we simply ignore them. This should only affect |
|
* some calls to printk. |
|
*/ |
|
if (in_atomic()) |
|
return NULL; |
|
|
|
sisusb = sisusb_get_sisusb(console); |
|
if (!sisusb) |
|
return NULL; |
|
|
|
mutex_lock(&sisusb->lock); |
|
|
|
if (!sisusb_sisusb_valid(sisusb) || |
|
!sisusb->havethisconsole[console]) { |
|
mutex_unlock(&sisusb->lock); |
|
return NULL; |
|
} |
|
|
|
return sisusb; |
|
} |
|
|
|
static int |
|
sisusb_is_inactive(struct vc_data *c, struct sisusb_usb_data *sisusb) |
|
{ |
|
if (sisusb->is_gfx || |
|
sisusb->textmodedestroyed || |
|
c->vc_mode != KD_TEXT) |
|
return 1; |
|
|
|
return 0; |
|
} |
|
|
|
/* con_startup console interface routine */ |
|
static const char * |
|
sisusbcon_startup(void) |
|
{ |
|
return "SISUSBCON"; |
|
} |
|
|
|
/* con_init console interface routine */ |
|
static void |
|
sisusbcon_init(struct vc_data *c, int init) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int cols, rows; |
|
|
|
/* This is called by do_take_over_console(), |
|
* ie by us/under our control. It is |
|
* only called after text mode and fonts |
|
* are set up/restored. |
|
*/ |
|
|
|
sisusb = sisusb_get_sisusb(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
mutex_lock(&sisusb->lock); |
|
|
|
if (!sisusb_sisusb_valid(sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
c->vc_can_do_color = 1; |
|
|
|
c->vc_complement_mask = 0x7700; |
|
|
|
c->vc_hi_font_mask = sisusb->current_font_512 ? 0x0800 : 0; |
|
|
|
sisusb->haveconsole = 1; |
|
|
|
sisusb->havethisconsole[c->vc_num] = 1; |
|
|
|
/* We only support 640x400 */ |
|
c->vc_scan_lines = 400; |
|
|
|
c->vc_font.height = sisusb->current_font_height; |
|
|
|
/* We only support width = 8 */ |
|
cols = 80; |
|
rows = c->vc_scan_lines / c->vc_font.height; |
|
|
|
/* Increment usage count for our sisusb. |
|
* Doing so saves us from upping/downing |
|
* the disconnect semaphore; we can't |
|
* lose our sisusb until this is undone |
|
* in con_deinit. For all other console |
|
* interface functions, it suffices to |
|
* use sisusb->lock and do a quick check |
|
* of sisusb for device disconnection. |
|
*/ |
|
kref_get(&sisusb->kref); |
|
|
|
if (!*c->vc_uni_pagedir_loc) |
|
con_set_default_unimap(c); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
if (init) { |
|
c->vc_cols = cols; |
|
c->vc_rows = rows; |
|
} else |
|
vc_resize(c, cols, rows); |
|
} |
|
|
|
/* con_deinit console interface routine */ |
|
static void |
|
sisusbcon_deinit(struct vc_data *c) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int i; |
|
|
|
/* This is called by do_take_over_console() |
|
* and others, ie not under our control. |
|
*/ |
|
|
|
sisusb = sisusb_get_sisusb(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
mutex_lock(&sisusb->lock); |
|
|
|
/* Clear ourselves in mysisusbs */ |
|
mysisusbs[c->vc_num] = NULL; |
|
|
|
sisusb->havethisconsole[c->vc_num] = 0; |
|
|
|
/* Free our font buffer if all consoles are gone */ |
|
if (sisusb->font_backup) { |
|
for(i = 0; i < MAX_NR_CONSOLES; i++) { |
|
if (sisusb->havethisconsole[c->vc_num]) |
|
break; |
|
} |
|
if (i == MAX_NR_CONSOLES) { |
|
vfree(sisusb->font_backup); |
|
sisusb->font_backup = NULL; |
|
} |
|
} |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
/* decrement the usage count on our sisusb */ |
|
kref_put(&sisusb->kref, sisusb_delete); |
|
} |
|
|
|
/* interface routine */ |
|
static u8 |
|
sisusbcon_build_attr(struct vc_data *c, u8 color, enum vc_intensity intensity, |
|
bool blink, bool underline, bool reverse, |
|
bool unused) |
|
{ |
|
u8 attr = color; |
|
|
|
if (underline) |
|
attr = (attr & 0xf0) | c->vc_ulcolor; |
|
else if (intensity == VCI_HALF_BRIGHT) |
|
attr = (attr & 0xf0) | c->vc_halfcolor; |
|
|
|
if (reverse) |
|
attr = ((attr) & 0x88) | |
|
((((attr) >> 4) | |
|
((attr) << 4)) & 0x77); |
|
|
|
if (blink) |
|
attr ^= 0x80; |
|
|
|
if (intensity == VCI_BOLD) |
|
attr ^= 0x08; |
|
|
|
return attr; |
|
} |
|
|
|
/* Interface routine */ |
|
static void |
|
sisusbcon_invert_region(struct vc_data *vc, u16 *p, int count) |
|
{ |
|
/* Invert a region. This is called with a pointer |
|
* to the console's internal screen buffer. So we |
|
* simply do the inversion there and rely on |
|
* a call to putc(s) to update the real screen. |
|
*/ |
|
|
|
while (count--) { |
|
u16 a = *p; |
|
|
|
*p++ = ((a) & 0x88ff) | |
|
(((a) & 0x7000) >> 4) | |
|
(((a) & 0x0700) << 4); |
|
} |
|
} |
|
|
|
static inline void *sisusb_vaddr(const struct sisusb_usb_data *sisusb, |
|
const struct vc_data *c, unsigned int x, unsigned int y) |
|
{ |
|
return (u16 *)c->vc_origin + y * sisusb->sisusb_num_columns + x; |
|
} |
|
|
|
static inline unsigned long sisusb_haddr(const struct sisusb_usb_data *sisusb, |
|
const struct vc_data *c, unsigned int x, unsigned int y) |
|
{ |
|
unsigned long offset = c->vc_origin - sisusb->scrbuf; |
|
|
|
/* 2 bytes per each character */ |
|
offset += 2 * (y * sisusb->sisusb_num_columns + x); |
|
|
|
return sisusb->vrambase + offset; |
|
} |
|
|
|
/* Interface routine */ |
|
static void |
|
sisusbcon_putc(struct vc_data *c, int ch, int y, int x) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y), |
|
sisusb_haddr(sisusb, c, x, y), 2); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* Interface routine */ |
|
static void |
|
sisusbcon_putcs(struct vc_data *c, const unsigned short *s, |
|
int count, int y, int x) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* Need to put the characters into the buffer ourselves, |
|
* because the vt does this AFTER calling us. |
|
*/ |
|
|
|
memcpy(sisusb_vaddr(sisusb, c, x, y), s, count * 2); |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y), |
|
sisusb_haddr(sisusb, c, x, y), count * 2); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* Interface routine */ |
|
static void |
|
sisusbcon_clear(struct vc_data *c, int y, int x, int height, int width) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
u16 eattr = c->vc_video_erase_char; |
|
int i, length, cols; |
|
u16 *dest; |
|
|
|
if (width <= 0 || height <= 0) |
|
return; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* Need to clear buffer ourselves, because the vt does |
|
* this AFTER calling us. |
|
*/ |
|
|
|
dest = sisusb_vaddr(sisusb, c, x, y); |
|
|
|
cols = sisusb->sisusb_num_columns; |
|
|
|
if (width > cols) |
|
width = cols; |
|
|
|
if (x == 0 && width >= c->vc_cols) { |
|
|
|
sisusbcon_memsetw(dest, eattr, height * cols * 2); |
|
|
|
} else { |
|
|
|
for (i = height; i > 0; i--, dest += cols) |
|
sisusbcon_memsetw(dest, eattr, width * 2); |
|
|
|
} |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
length = ((height * cols) - x - (cols - width - x)) * 2; |
|
|
|
|
|
sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, x, y), |
|
sisusb_haddr(sisusb, c, x, y), length); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* interface routine */ |
|
static int |
|
sisusbcon_switch(struct vc_data *c) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int length; |
|
|
|
/* Returnvalue 0 means we have fully restored screen, |
|
* and vt doesn't need to call do_update_region(). |
|
* Returnvalue != 0 naturally means the opposite. |
|
*/ |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return 0; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* Don't write to screen if in gfx mode */ |
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return 0; |
|
} |
|
|
|
/* That really should not happen. It would mean we are |
|
* being called while the vc is using its private buffer |
|
* as origin. |
|
*/ |
|
if (c->vc_origin == (unsigned long)c->vc_screenbuf) { |
|
mutex_unlock(&sisusb->lock); |
|
dev_dbg(&sisusb->sisusb_dev->dev, "ASSERT ORIGIN != SCREENBUF!\n"); |
|
return 0; |
|
} |
|
|
|
/* Check that we don't copy too much */ |
|
length = min((int)c->vc_screenbuf_size, |
|
(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); |
|
|
|
/* Restore the screen contents */ |
|
memcpy((u16 *)c->vc_origin, (u16 *)c->vc_screenbuf, length); |
|
|
|
sisusb_copy_memory(sisusb, (u8 *)c->vc_origin, |
|
sisusb_haddr(sisusb, c, 0, 0), length); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return 0; |
|
} |
|
|
|
/* interface routine */ |
|
static void |
|
sisusbcon_save_screen(struct vc_data *c) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int length; |
|
|
|
/* Save the current screen contents to vc's private |
|
* buffer. |
|
*/ |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
/* Check that we don't copy too much */ |
|
length = min((int)c->vc_screenbuf_size, |
|
(int)(sisusb->scrbuf + sisusb->scrbuf_size - c->vc_origin)); |
|
|
|
/* Save the screen contents to vc's private buffer */ |
|
memcpy((u16 *)c->vc_screenbuf, (u16 *)c->vc_origin, length); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* interface routine */ |
|
static void |
|
sisusbcon_set_palette(struct vc_data *c, const unsigned char *table) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int i, j; |
|
|
|
/* Return value not used by vt */ |
|
|
|
if (!con_is_visible(c)) |
|
return; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
for (i = j = 0; i < 16; i++) { |
|
if (sisusb_setreg(sisusb, SISCOLIDX, table[i])) |
|
break; |
|
if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) |
|
break; |
|
if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) |
|
break; |
|
if (sisusb_setreg(sisusb, SISCOLDATA, c->vc_palette[j++] >> 2)) |
|
break; |
|
} |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* interface routine */ |
|
static int |
|
sisusbcon_blank(struct vc_data *c, int blank, int mode_switch) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
u8 sr1, cr17, pmreg, cr63; |
|
int ret = 0; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return 0; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (mode_switch) |
|
sisusb->is_gfx = blank ? 1 : 0; |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return 0; |
|
} |
|
|
|
switch (blank) { |
|
|
|
case 1: /* Normal blanking: Clear screen */ |
|
case -1: |
|
sisusbcon_memsetw((u16 *)c->vc_origin, |
|
c->vc_video_erase_char, |
|
c->vc_screenbuf_size); |
|
sisusb_copy_memory(sisusb, (u8 *)c->vc_origin, |
|
sisusb_haddr(sisusb, c, 0, 0), |
|
c->vc_screenbuf_size); |
|
sisusb->con_blanked = 1; |
|
ret = 1; |
|
break; |
|
|
|
default: /* VESA blanking */ |
|
switch (blank) { |
|
case 0: /* Unblank */ |
|
sr1 = 0x00; |
|
cr17 = 0x80; |
|
pmreg = 0x00; |
|
cr63 = 0x00; |
|
ret = 1; |
|
sisusb->con_blanked = 0; |
|
break; |
|
case VESA_VSYNC_SUSPEND + 1: |
|
sr1 = 0x20; |
|
cr17 = 0x80; |
|
pmreg = 0x80; |
|
cr63 = 0x40; |
|
break; |
|
case VESA_HSYNC_SUSPEND + 1: |
|
sr1 = 0x20; |
|
cr17 = 0x80; |
|
pmreg = 0x40; |
|
cr63 = 0x40; |
|
break; |
|
case VESA_POWERDOWN + 1: |
|
sr1 = 0x20; |
|
cr17 = 0x00; |
|
pmreg = 0xc0; |
|
cr63 = 0x40; |
|
break; |
|
default: |
|
mutex_unlock(&sisusb->lock); |
|
return -EINVAL; |
|
} |
|
|
|
sisusb_setidxregandor(sisusb, SISSR, 0x01, ~0x20, sr1); |
|
sisusb_setidxregandor(sisusb, SISCR, 0x17, 0x7f, cr17); |
|
sisusb_setidxregandor(sisusb, SISSR, 0x1f, 0x3f, pmreg); |
|
sisusb_setidxregandor(sisusb, SISCR, 0x63, 0xbf, cr63); |
|
|
|
} |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return ret; |
|
} |
|
|
|
/* interface routine */ |
|
static void |
|
sisusbcon_scrolldelta(struct vc_data *c, int lines) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
vc_scrolldelta_helper(c, lines, sisusb->con_rolled_over, |
|
(void *)sisusb->scrbuf, sisusb->scrbuf_size); |
|
|
|
sisusbcon_set_start_address(sisusb, c); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
/* Interface routine */ |
|
static void |
|
sisusbcon_cursor(struct vc_data *c, int mode) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int from, to, baseline; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
if (c->vc_origin != c->vc_visible_origin) { |
|
c->vc_visible_origin = c->vc_origin; |
|
sisusbcon_set_start_address(sisusb, c); |
|
} |
|
|
|
if (mode == CM_ERASE) { |
|
sisusb_setidxregor(sisusb, SISCR, 0x0a, 0x20); |
|
sisusb->sisusb_cursor_size_to = -1; |
|
mutex_unlock(&sisusb->lock); |
|
return; |
|
} |
|
|
|
sisusb_set_cursor(sisusb, (c->vc_pos - sisusb->scrbuf) / 2); |
|
|
|
baseline = c->vc_font.height - (c->vc_font.height < 10 ? 1 : 2); |
|
|
|
switch (CUR_SIZE(c->vc_cursor_type)) { |
|
case CUR_BLOCK: from = 1; |
|
to = c->vc_font.height; |
|
break; |
|
case CUR_TWO_THIRDS: from = c->vc_font.height / 3; |
|
to = baseline; |
|
break; |
|
case CUR_LOWER_HALF: from = c->vc_font.height / 2; |
|
to = baseline; |
|
break; |
|
case CUR_LOWER_THIRD: from = (c->vc_font.height * 2) / 3; |
|
to = baseline; |
|
break; |
|
case CUR_NONE: from = 31; |
|
to = 30; |
|
break; |
|
default: |
|
case CUR_UNDERLINE: from = baseline - 1; |
|
to = baseline; |
|
break; |
|
} |
|
|
|
if (sisusb->sisusb_cursor_size_from != from || |
|
sisusb->sisusb_cursor_size_to != to) { |
|
|
|
sisusb_setidxreg(sisusb, SISCR, 0x0a, from); |
|
sisusb_setidxregandor(sisusb, SISCR, 0x0b, 0xe0, to); |
|
|
|
sisusb->sisusb_cursor_size_from = from; |
|
sisusb->sisusb_cursor_size_to = to; |
|
} |
|
|
|
mutex_unlock(&sisusb->lock); |
|
} |
|
|
|
static bool |
|
sisusbcon_scroll_area(struct vc_data *c, struct sisusb_usb_data *sisusb, |
|
unsigned int t, unsigned int b, enum con_scroll dir, |
|
unsigned int lines) |
|
{ |
|
int cols = sisusb->sisusb_num_columns; |
|
int length = ((b - t) * cols) * 2; |
|
u16 eattr = c->vc_video_erase_char; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* Scroll an area which does not match the |
|
* visible screen's dimensions. This needs |
|
* to be done separately, as it does not |
|
* use hardware panning. |
|
*/ |
|
|
|
switch (dir) { |
|
|
|
case SM_UP: |
|
memmove(sisusb_vaddr(sisusb, c, 0, t), |
|
sisusb_vaddr(sisusb, c, 0, t + lines), |
|
(b - t - lines) * cols * 2); |
|
sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, b - lines), |
|
eattr, lines * cols * 2); |
|
break; |
|
|
|
case SM_DOWN: |
|
memmove(sisusb_vaddr(sisusb, c, 0, t + lines), |
|
sisusb_vaddr(sisusb, c, 0, t), |
|
(b - t - lines) * cols * 2); |
|
sisusbcon_memsetw(sisusb_vaddr(sisusb, c, 0, t), eattr, |
|
lines * cols * 2); |
|
break; |
|
} |
|
|
|
sisusb_copy_memory(sisusb, sisusb_vaddr(sisusb, c, 0, t), |
|
sisusb_haddr(sisusb, c, 0, t), length); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return true; |
|
} |
|
|
|
/* Interface routine */ |
|
static bool |
|
sisusbcon_scroll(struct vc_data *c, unsigned int t, unsigned int b, |
|
enum con_scroll dir, unsigned int lines) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
u16 eattr = c->vc_video_erase_char; |
|
int copyall = 0; |
|
unsigned long oldorigin; |
|
unsigned int delta = lines * c->vc_size_row; |
|
|
|
/* Returning != 0 means we have done the scrolling successfully. |
|
* Returning 0 makes vt do the scrolling on its own. |
|
* Note that con_scroll is only called if the console is |
|
* visible. In that case, the origin should be our buffer, |
|
* not the vt's private one. |
|
*/ |
|
|
|
if (!lines) |
|
return true; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return false; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb)) { |
|
mutex_unlock(&sisusb->lock); |
|
return false; |
|
} |
|
|
|
/* Special case */ |
|
if (t || b != c->vc_rows) |
|
return sisusbcon_scroll_area(c, sisusb, t, b, dir, lines); |
|
|
|
if (c->vc_origin != c->vc_visible_origin) { |
|
c->vc_visible_origin = c->vc_origin; |
|
sisusbcon_set_start_address(sisusb, c); |
|
} |
|
|
|
/* limit amount to maximum realistic size */ |
|
if (lines > c->vc_rows) |
|
lines = c->vc_rows; |
|
|
|
oldorigin = c->vc_origin; |
|
|
|
switch (dir) { |
|
|
|
case SM_UP: |
|
|
|
if (c->vc_scr_end + delta >= |
|
sisusb->scrbuf + sisusb->scrbuf_size) { |
|
memcpy((u16 *)sisusb->scrbuf, |
|
(u16 *)(oldorigin + delta), |
|
c->vc_screenbuf_size - delta); |
|
c->vc_origin = sisusb->scrbuf; |
|
sisusb->con_rolled_over = oldorigin - sisusb->scrbuf; |
|
copyall = 1; |
|
} else |
|
c->vc_origin += delta; |
|
|
|
sisusbcon_memsetw( |
|
(u16 *)(c->vc_origin + c->vc_screenbuf_size - delta), |
|
eattr, delta); |
|
|
|
break; |
|
|
|
case SM_DOWN: |
|
|
|
if (oldorigin - delta < sisusb->scrbuf) { |
|
memmove((void *)sisusb->scrbuf + sisusb->scrbuf_size - |
|
c->vc_screenbuf_size + delta, |
|
(u16 *)oldorigin, |
|
c->vc_screenbuf_size - delta); |
|
c->vc_origin = sisusb->scrbuf + |
|
sisusb->scrbuf_size - |
|
c->vc_screenbuf_size; |
|
sisusb->con_rolled_over = 0; |
|
copyall = 1; |
|
} else |
|
c->vc_origin -= delta; |
|
|
|
c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; |
|
|
|
scr_memsetw((u16 *)(c->vc_origin), eattr, delta); |
|
|
|
break; |
|
} |
|
|
|
if (copyall) |
|
sisusb_copy_memory(sisusb, |
|
(u8 *)c->vc_origin, |
|
sisusb_haddr(sisusb, c, 0, 0), |
|
c->vc_screenbuf_size); |
|
else if (dir == SM_UP) |
|
sisusb_copy_memory(sisusb, |
|
(u8 *)c->vc_origin + c->vc_screenbuf_size - delta, |
|
sisusb_haddr(sisusb, c, 0, 0) + |
|
c->vc_screenbuf_size - delta, |
|
delta); |
|
else |
|
sisusb_copy_memory(sisusb, |
|
(u8 *)c->vc_origin, |
|
sisusb_haddr(sisusb, c, 0, 0), |
|
delta); |
|
|
|
c->vc_scr_end = c->vc_origin + c->vc_screenbuf_size; |
|
c->vc_visible_origin = c->vc_origin; |
|
|
|
sisusbcon_set_start_address(sisusb, c); |
|
|
|
c->vc_pos = c->vc_pos - oldorigin + c->vc_origin; |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return true; |
|
} |
|
|
|
/* Interface routine */ |
|
static int |
|
sisusbcon_set_origin(struct vc_data *c) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
/* Returning != 0 means we were successful. |
|
* Returning 0 will vt make to use its own |
|
* screenbuffer as the origin. |
|
*/ |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return 0; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
if (sisusb_is_inactive(c, sisusb) || sisusb->con_blanked) { |
|
mutex_unlock(&sisusb->lock); |
|
return 0; |
|
} |
|
|
|
c->vc_origin = c->vc_visible_origin = sisusb->scrbuf; |
|
|
|
sisusbcon_set_start_address(sisusb, c); |
|
|
|
sisusb->con_rolled_over = 0; |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return true; |
|
} |
|
|
|
/* Interface routine */ |
|
static int |
|
sisusbcon_resize(struct vc_data *c, unsigned int newcols, unsigned int newrows, |
|
unsigned int user) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
int fh; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return -ENODEV; |
|
|
|
fh = sisusb->current_font_height; |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
/* We are quite unflexible as regards resizing. The vt code |
|
* handles sizes where the line length isn't equal the pitch |
|
* quite badly. As regards the rows, our panning tricks only |
|
* work well if the number of rows equals the visible number |
|
* of rows. |
|
*/ |
|
|
|
if (newcols != 80 || c->vc_scan_lines / fh != newrows) |
|
return -EINVAL; |
|
|
|
return 0; |
|
} |
|
|
|
int |
|
sisusbcon_do_font_op(struct sisusb_usb_data *sisusb, int set, int slot, |
|
u8 *arg, int cmapsz, int ch512, int dorecalc, |
|
struct vc_data *c, int fh, int uplock) |
|
{ |
|
int font_select = 0x00, i, err = 0; |
|
u32 offset = 0; |
|
u8 dummy; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* |
|
* The default font is kept in slot 0. |
|
* A user font is loaded in slot 2 (256 ch) |
|
* or 2+3 (512 ch). |
|
*/ |
|
|
|
if ((slot != 0 && slot != 2) || !fh) { |
|
if (uplock) |
|
mutex_unlock(&sisusb->lock); |
|
return -EINVAL; |
|
} |
|
|
|
if (set) |
|
sisusb->font_slot = slot; |
|
|
|
/* Default font is always 256 */ |
|
if (slot == 0) |
|
ch512 = 0; |
|
else |
|
offset = 4 * cmapsz; |
|
|
|
font_select = (slot == 0) ? 0x00 : (ch512 ? 0x0e : 0x0a); |
|
|
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x04); /* Write to plane 2 */ |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x07); /* Memory mode a0-bf */ |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset */ |
|
|
|
if (err) |
|
goto font_op_error; |
|
|
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x03); /* Select plane read 2 */ |
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x00); /* Disable odd/even */ |
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x00); /* Address range a0-bf */ |
|
|
|
if (err) |
|
goto font_op_error; |
|
|
|
if (arg) { |
|
if (set) |
|
for (i = 0; i < cmapsz; i++) { |
|
err |= sisusb_writeb(sisusb, |
|
sisusb->vrambase + offset + i, |
|
arg[i]); |
|
if (err) |
|
break; |
|
} |
|
else |
|
for (i = 0; i < cmapsz; i++) { |
|
err |= sisusb_readb(sisusb, |
|
sisusb->vrambase + offset + i, |
|
&arg[i]); |
|
if (err) |
|
break; |
|
} |
|
|
|
/* |
|
* In 512-character mode, the character map is not contiguous if |
|
* we want to remain EGA compatible -- which we do |
|
*/ |
|
|
|
if (ch512) { |
|
if (set) |
|
for (i = 0; i < cmapsz; i++) { |
|
err |= sisusb_writeb(sisusb, |
|
sisusb->vrambase + offset + |
|
(2 * cmapsz) + i, |
|
arg[cmapsz + i]); |
|
if (err) |
|
break; |
|
} |
|
else |
|
for (i = 0; i < cmapsz; i++) { |
|
err |= sisusb_readb(sisusb, |
|
sisusb->vrambase + offset + |
|
(2 * cmapsz) + i, |
|
&arg[cmapsz + i]); |
|
if (err) |
|
break; |
|
} |
|
} |
|
} |
|
|
|
if (err) |
|
goto font_op_error; |
|
|
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x01); /* Reset */ |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x02, 0x03); /* Write to planes 0+1 */ |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x04, 0x03); /* Memory mode a0-bf */ |
|
if (set) |
|
sisusb_setidxreg(sisusb, SISSR, 0x03, font_select); |
|
err |= sisusb_setidxreg(sisusb, SISSR, 0x00, 0x03); /* Reset end */ |
|
|
|
if (err) |
|
goto font_op_error; |
|
|
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x04, 0x00); /* Select plane read 0 */ |
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x05, 0x10); /* Enable odd/even */ |
|
err |= sisusb_setidxreg(sisusb, SISGR, 0x06, 0x06); /* Address range b8-bf */ |
|
|
|
if (err) |
|
goto font_op_error; |
|
|
|
if ((set) && (ch512 != sisusb->current_font_512)) { |
|
|
|
/* Font is shared among all our consoles. |
|
* And so is the hi_font_mask. |
|
*/ |
|
for (i = 0; i < MAX_NR_CONSOLES; i++) { |
|
struct vc_data *d = vc_cons[i].d; |
|
if (d && d->vc_sw == &sisusb_con) |
|
d->vc_hi_font_mask = ch512 ? 0x0800 : 0; |
|
} |
|
|
|
sisusb->current_font_512 = ch512; |
|
|
|
/* color plane enable register: |
|
256-char: enable intensity bit |
|
512-char: disable intensity bit */ |
|
sisusb_getreg(sisusb, SISINPSTAT, &dummy); |
|
sisusb_setreg(sisusb, SISAR, 0x12); |
|
sisusb_setreg(sisusb, SISAR, ch512 ? 0x07 : 0x0f); |
|
|
|
sisusb_getreg(sisusb, SISINPSTAT, &dummy); |
|
sisusb_setreg(sisusb, SISAR, 0x20); |
|
sisusb_getreg(sisusb, SISINPSTAT, &dummy); |
|
} |
|
|
|
if (dorecalc) { |
|
|
|
/* |
|
* Adjust the screen to fit a font of a certain height |
|
*/ |
|
|
|
unsigned char ovr, vde, fsr; |
|
int rows = 0, maxscan = 0; |
|
|
|
if (c) { |
|
|
|
/* Number of video rows */ |
|
rows = c->vc_scan_lines / fh; |
|
/* Scan lines to actually display-1 */ |
|
maxscan = rows * fh - 1; |
|
|
|
/*printk(KERN_DEBUG "sisusb recalc rows %d maxscan %d fh %d sl %d\n", |
|
rows, maxscan, fh, c->vc_scan_lines);*/ |
|
|
|
sisusb_getidxreg(sisusb, SISCR, 0x07, &ovr); |
|
vde = maxscan & 0xff; |
|
ovr = (ovr & 0xbd) | |
|
((maxscan & 0x100) >> 7) | |
|
((maxscan & 0x200) >> 3); |
|
sisusb_setidxreg(sisusb, SISCR, 0x07, ovr); |
|
sisusb_setidxreg(sisusb, SISCR, 0x12, vde); |
|
|
|
} |
|
|
|
sisusb_getidxreg(sisusb, SISCR, 0x09, &fsr); |
|
fsr = (fsr & 0xe0) | (fh - 1); |
|
sisusb_setidxreg(sisusb, SISCR, 0x09, fsr); |
|
sisusb->current_font_height = fh; |
|
|
|
sisusb->sisusb_cursor_size_from = -1; |
|
sisusb->sisusb_cursor_size_to = -1; |
|
|
|
} |
|
|
|
if (uplock) |
|
mutex_unlock(&sisusb->lock); |
|
|
|
if (dorecalc && c) { |
|
int rows = c->vc_scan_lines / fh; |
|
|
|
/* Now adjust our consoles' size */ |
|
|
|
for (i = 0; i < MAX_NR_CONSOLES; i++) { |
|
struct vc_data *vc = vc_cons[i].d; |
|
|
|
if (vc && vc->vc_sw == &sisusb_con) { |
|
if (con_is_visible(vc)) { |
|
vc->vc_sw->con_cursor(vc, CM_DRAW); |
|
} |
|
vc->vc_font.height = fh; |
|
vc_resize(vc, 0, rows); |
|
} |
|
} |
|
} |
|
|
|
return 0; |
|
|
|
font_op_error: |
|
if (uplock) |
|
mutex_unlock(&sisusb->lock); |
|
|
|
return -EIO; |
|
} |
|
|
|
/* Interface routine */ |
|
static int |
|
sisusbcon_font_set(struct vc_data *c, struct console_font *font, |
|
unsigned int flags) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
unsigned charcount = font->charcount; |
|
|
|
if (font->width != 8 || (charcount != 256 && charcount != 512)) |
|
return -EINVAL; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return -ENODEV; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
/* Save the user-provided font into a buffer. This |
|
* is used for restoring text mode after quitting |
|
* from X and for the con_getfont routine. |
|
*/ |
|
if (sisusb->font_backup) { |
|
if (sisusb->font_backup_size < charcount) { |
|
vfree(sisusb->font_backup); |
|
sisusb->font_backup = NULL; |
|
} |
|
} |
|
|
|
if (!sisusb->font_backup) |
|
sisusb->font_backup = vmalloc(array_size(charcount, 32)); |
|
|
|
if (sisusb->font_backup) { |
|
memcpy(sisusb->font_backup, font->data, array_size(charcount, 32)); |
|
sisusb->font_backup_size = charcount; |
|
sisusb->font_backup_height = font->height; |
|
sisusb->font_backup_512 = (charcount == 512) ? 1 : 0; |
|
} |
|
|
|
/* do_font_op ups sisusb->lock */ |
|
|
|
return sisusbcon_do_font_op(sisusb, 1, 2, font->data, |
|
8192, (charcount == 512), |
|
(!(flags & KD_FONT_FLAG_DONT_RECALC)) ? 1 : 0, |
|
c, font->height, 1); |
|
} |
|
|
|
/* Interface routine */ |
|
static int |
|
sisusbcon_font_get(struct vc_data *c, struct console_font *font) |
|
{ |
|
struct sisusb_usb_data *sisusb; |
|
|
|
sisusb = sisusb_get_sisusb_lock_and_check(c->vc_num); |
|
if (!sisusb) |
|
return -ENODEV; |
|
|
|
/* sisusb->lock is down */ |
|
|
|
font->width = 8; |
|
font->height = c->vc_font.height; |
|
font->charcount = 256; |
|
|
|
if (!font->data) { |
|
mutex_unlock(&sisusb->lock); |
|
return 0; |
|
} |
|
|
|
if (!sisusb->font_backup) { |
|
mutex_unlock(&sisusb->lock); |
|
return -ENODEV; |
|
} |
|
|
|
/* Copy 256 chars only, like vgacon */ |
|
memcpy(font->data, sisusb->font_backup, 256 * 32); |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
return 0; |
|
} |
|
|
|
/* |
|
* The console `switch' structure for the sisusb console |
|
*/ |
|
|
|
static const struct consw sisusb_con = { |
|
.owner = THIS_MODULE, |
|
.con_startup = sisusbcon_startup, |
|
.con_init = sisusbcon_init, |
|
.con_deinit = sisusbcon_deinit, |
|
.con_clear = sisusbcon_clear, |
|
.con_putc = sisusbcon_putc, |
|
.con_putcs = sisusbcon_putcs, |
|
.con_cursor = sisusbcon_cursor, |
|
.con_scroll = sisusbcon_scroll, |
|
.con_switch = sisusbcon_switch, |
|
.con_blank = sisusbcon_blank, |
|
.con_font_set = sisusbcon_font_set, |
|
.con_font_get = sisusbcon_font_get, |
|
.con_set_palette = sisusbcon_set_palette, |
|
.con_scrolldelta = sisusbcon_scrolldelta, |
|
.con_build_attr = sisusbcon_build_attr, |
|
.con_invert_region = sisusbcon_invert_region, |
|
.con_set_origin = sisusbcon_set_origin, |
|
.con_save_screen = sisusbcon_save_screen, |
|
.con_resize = sisusbcon_resize, |
|
}; |
|
|
|
/* Our very own dummy console driver */ |
|
|
|
static const char *sisusbdummycon_startup(void) |
|
{ |
|
return "SISUSBVGADUMMY"; |
|
} |
|
|
|
static void sisusbdummycon_init(struct vc_data *vc, int init) |
|
{ |
|
vc->vc_can_do_color = 1; |
|
if (init) { |
|
vc->vc_cols = 80; |
|
vc->vc_rows = 25; |
|
} else |
|
vc_resize(vc, 80, 25); |
|
} |
|
|
|
static void sisusbdummycon_deinit(struct vc_data *vc) { } |
|
static void sisusbdummycon_clear(struct vc_data *vc, int sy, int sx, |
|
int height, int width) { } |
|
static void sisusbdummycon_putc(struct vc_data *vc, int c, int ypos, |
|
int xpos) { } |
|
static void sisusbdummycon_putcs(struct vc_data *vc, const unsigned short *s, |
|
int count, int ypos, int xpos) { } |
|
static void sisusbdummycon_cursor(struct vc_data *vc, int mode) { } |
|
|
|
static bool sisusbdummycon_scroll(struct vc_data *vc, unsigned int top, |
|
unsigned int bottom, enum con_scroll dir, |
|
unsigned int lines) |
|
{ |
|
return false; |
|
} |
|
|
|
static int sisusbdummycon_switch(struct vc_data *vc) |
|
{ |
|
return 0; |
|
} |
|
|
|
static int sisusbdummycon_blank(struct vc_data *vc, int blank, int mode_switch) |
|
{ |
|
return 0; |
|
} |
|
|
|
static const struct consw sisusb_dummy_con = { |
|
.owner = THIS_MODULE, |
|
.con_startup = sisusbdummycon_startup, |
|
.con_init = sisusbdummycon_init, |
|
.con_deinit = sisusbdummycon_deinit, |
|
.con_clear = sisusbdummycon_clear, |
|
.con_putc = sisusbdummycon_putc, |
|
.con_putcs = sisusbdummycon_putcs, |
|
.con_cursor = sisusbdummycon_cursor, |
|
.con_scroll = sisusbdummycon_scroll, |
|
.con_switch = sisusbdummycon_switch, |
|
.con_blank = sisusbdummycon_blank, |
|
}; |
|
|
|
int |
|
sisusb_console_init(struct sisusb_usb_data *sisusb, int first, int last) |
|
{ |
|
int i, ret; |
|
|
|
mutex_lock(&sisusb->lock); |
|
|
|
/* Erm.. that should not happen */ |
|
if (sisusb->haveconsole || !sisusb->SiS_Pr) { |
|
mutex_unlock(&sisusb->lock); |
|
return 1; |
|
} |
|
|
|
sisusb->con_first = first; |
|
sisusb->con_last = last; |
|
|
|
if (first > last || |
|
first > MAX_NR_CONSOLES || |
|
last > MAX_NR_CONSOLES) { |
|
mutex_unlock(&sisusb->lock); |
|
return 1; |
|
} |
|
|
|
/* If gfxcore not initialized or no consoles given, quit graciously */ |
|
if (!sisusb->gfxinit || first < 1 || last < 1) { |
|
mutex_unlock(&sisusb->lock); |
|
return 0; |
|
} |
|
|
|
sisusb->sisusb_cursor_loc = -1; |
|
sisusb->sisusb_cursor_size_from = -1; |
|
sisusb->sisusb_cursor_size_to = -1; |
|
|
|
/* Set up text mode (and upload default font) */ |
|
if (sisusb_reset_text_mode(sisusb, 1)) { |
|
mutex_unlock(&sisusb->lock); |
|
dev_err(&sisusb->sisusb_dev->dev, "Failed to set up text mode\n"); |
|
return 1; |
|
} |
|
|
|
/* Initialize some gfx registers */ |
|
sisusb_initialize(sisusb); |
|
|
|
for (i = first - 1; i <= last - 1; i++) { |
|
/* Save sisusb for our interface routines */ |
|
mysisusbs[i] = sisusb; |
|
} |
|
|
|
/* Initial console setup */ |
|
sisusb->sisusb_num_columns = 80; |
|
|
|
/* Use a 32K buffer (matches b8000-bffff area) */ |
|
sisusb->scrbuf_size = 32 * 1024; |
|
|
|
/* Allocate screen buffer */ |
|
if (!(sisusb->scrbuf = (unsigned long)vmalloc(sisusb->scrbuf_size))) { |
|
mutex_unlock(&sisusb->lock); |
|
dev_err(&sisusb->sisusb_dev->dev, "Failed to allocate screen buffer\n"); |
|
return 1; |
|
} |
|
|
|
mutex_unlock(&sisusb->lock); |
|
|
|
/* Now grab the desired console(s) */ |
|
console_lock(); |
|
ret = do_take_over_console(&sisusb_con, first - 1, last - 1, 0); |
|
console_unlock(); |
|
if (!ret) |
|
sisusb->haveconsole = 1; |
|
else { |
|
for (i = first - 1; i <= last - 1; i++) |
|
mysisusbs[i] = NULL; |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
void |
|
sisusb_console_exit(struct sisusb_usb_data *sisusb) |
|
{ |
|
int i; |
|
|
|
/* This is called if the device is disconnected |
|
* and while disconnect and lock semaphores |
|
* are up. This should be save because we |
|
* can't lose our sisusb any other way but by |
|
* disconnection (and hence, the disconnect |
|
* sema is for protecting all other access |
|
* functions from disconnection, not the |
|
* other way round). |
|
*/ |
|
|
|
/* Now what do we do in case of disconnection: |
|
* One alternative would be to simply call |
|
* give_up_console(). Nah, not a good idea. |
|
* give_up_console() is obviously buggy as it |
|
* only discards the consw pointer from the |
|
* driver_map, but doesn't adapt vc->vc_sw |
|
* of the affected consoles. Hence, the next |
|
* call to any of the console functions will |
|
* eventually take a trip to oops county. |
|
* Also, give_up_console for some reason |
|
* doesn't decrement our module refcount. |
|
* Instead, we switch our consoles to a private |
|
* dummy console. This, of course, keeps our |
|
* refcount up as well, but it works perfectly. |
|
*/ |
|
|
|
if (sisusb->haveconsole) { |
|
for (i = 0; i < MAX_NR_CONSOLES; i++) |
|
if (sisusb->havethisconsole[i]) { |
|
console_lock(); |
|
do_take_over_console(&sisusb_dummy_con, i, i, 0); |
|
console_unlock(); |
|
/* At this point, con_deinit for all our |
|
* consoles is executed by do_take_over_console(). |
|
*/ |
|
} |
|
sisusb->haveconsole = 0; |
|
} |
|
|
|
vfree((void *)sisusb->scrbuf); |
|
sisusb->scrbuf = 0; |
|
|
|
vfree(sisusb->font_backup); |
|
sisusb->font_backup = NULL; |
|
} |
|
|
|
void __init sisusb_init_concode(void) |
|
{ |
|
int i; |
|
|
|
for (i = 0; i < MAX_NR_CONSOLES; i++) |
|
mysisusbs[i] = NULL; |
|
}
|
|
|