mirror of
https://github.com/torvalds/linux.git
synced 2024-12-27 21:33:00 +00:00
d6efb3ac3e
Here is the large set of TTY and Serial driver patches for 5.9-rc1. Lots of bugfixes in here, thanks to syzbot fuzzing for serial and vt and console code. Other highlights include: - much needed vt/vc code cleanup from Jiri Slaby - 8250 driver fixes and additions - various serial driver updates and feature enhancements - locking cleanup for serial/console initializations - other minor cleanups All of these have been in linux-next with no reported issues. Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> -----BEGIN PGP SIGNATURE----- iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCXyv30A8cZ3JlZ0Brcm9h aC5jb20ACgkQMUfUDdst+ynW+gCgv+OqxT0jeNRAMSQcpMvP3wTBMKIAn1StfjJ4 y8uwZuQQimD49uj8XtDq =bKSv -----END PGP SIGNATURE----- Merge tag 'tty-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty Pull tty/serial updates from Greg KH: "Here is the large set of TTY and Serial driver patches for 5.9-rc1. Lots of bugfixes in here, thanks to syzbot fuzzing for serial and vt and console code. Other highlights include: - much needed vt/vc code cleanup from Jiri Slaby - 8250 driver fixes and additions - various serial driver updates and feature enhancements - locking cleanup for serial/console initializations - other minor cleanups All of these have been in linux-next with no reported issues" * tag 'tty-5.9-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty: (90 commits) MAINTAINERS: enlist Greg formally for console stuff vgacon: Fix for missing check in scrollback handling Revert "serial: 8250: Let serial core initialise spin lock" serial: 8250: Let serial core initialise spin lock tty: keyboard, do not speculate on func_table index serial: stm32: Add RS485 RTS GPIO control serial: 8250_dw: Fix common clocks usage race condition serial: 8250_dw: Pass the same rate to the clk round and set rate methods serial: 8250_dw: Simplify the ref clock rate setting procedure serial: 8250: Add 8250 port clock update method tty: serial: imx: add imx earlycon driver tty: serial: imx: enable imx serial console port as module tty/synclink: remove leftover bits of non-PCI card support tty: Use the preferred form for passing the size of a structure type tty: Fix identation issues in struct serial_struct32 tty: Avoid the use of one-element arrays serial: msm_serial: add sparse context annotation serial: pmac_zilog: add sparse context annotation newport_con: vc_color is now in state serial: imx: use hrtimers for rs485 delays ...
1518 lines
36 KiB
C
1518 lines
36 KiB
C
// 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 <thomas@winischhofer.net>
|
|
*
|
|
* Portions based on vgacon.c which are
|
|
* Created 28 Sep 1997 by Geert Uytterhoeven
|
|
* Rewritten by Martin Mares <mj@ucw.cz>, 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 int sisusbdummycon_font_set(struct vc_data *vc,
|
|
struct console_font *font,
|
|
unsigned int flags)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sisusbdummycon_font_default(struct vc_data *vc,
|
|
struct console_font *font, char *name)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sisusbdummycon_font_copy(struct vc_data *vc, int con)
|
|
{
|
|
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,
|
|
.con_font_set = sisusbdummycon_font_set,
|
|
.con_font_default = sisusbdummycon_font_default,
|
|
.con_font_copy = sisusbdummycon_font_copy,
|
|
};
|
|
|
|
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;
|
|
}
|