OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

Compare Revisions

  • This comparison shows the changes necessary to convert path
    /or1k_old/trunk/rc203soc/sw/uClinux/arch/armnommu/drivers/char
    from Rev 1765 to Rev 1782
    Reverse comparison

Rev 1765 → Rev 1782

/keyboard.c
0,0 → 1,1342
/*
* linux/arch/arm/drivers/block/keyboard.c
*
* Keyboard driver for Linux using Latin-1.
*
* Written for linux by Johan Myreen as a translation from
* the assembly version by Linus (with diacriticals added)
*
* Some additional features added by Christoph Niemann (ChN), March 1993
*
* Loadable keymaps by Risto Kankkunen, May 1993
*
* Diacriticals redone & other small changes, aeb@cwi.nl, June 1993
* Added decr/incr_console, dynamic keymaps, Unicode support,
* dynamic function/string keys, led setting, Sept 1994
* `Sticky' modifier keys, 951006.
*
* Majorly altered for use with arm Russell King (rmk@ecs.soton.ac.uk)
* The low-level keyboard driver is now separate.
* Interfaces:
* Driver supplies:
* kbd_drv_init - initialise keyboard driver proper
* kbd_drv_setleds - set leds on keyboard
* Driver uses:
* kbd_keyboardkey - process a keypress/release
* kbd_reset - reset keyboard down array
* kbd_setregs - set regs for scroll-lock debug
*/
 
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/random.h>
#include <linux/ctype.h>
 
#include <asm/bitops.h>
#include <asm/system.h>
#include <asm/irq.h>
#include <asm/segment.h>
 
#include "kbd_kern.h"
#include "diacr.h"
#include "vt_kern.h"
 
#define SIZE(x) (sizeof(x)/sizeof((x)[0]))
 
#define KBD_REPORT_ERR
#define KBD_REPORT_UNKN
 
#ifndef KBD_DEFMODE
#define KBD_DEFMODE ((1 << VC_REPEAT) | (1 << VC_META))
#endif
 
/*
* Starts with NumLock off.
*/
#ifndef KBD_DEFLEDS
#define KBD_DEFLEDS 0
#endif
 
#ifndef KBD_DEFLOCK
#define KBD_DEFLOCK 0
#endif
 
#define REPEAT_TIMEOUT HZ*300/1000
#define REPEAT_RATE HZ*30/1000
 
extern void ctrl_alt_del (void);
extern void scrollback(int);
extern void scrollfront(int);
extern int kbd_drv_init(void);
extern void kbd_drv_setleds(unsigned int leds);
extern unsigned int keymap_count;
 
/*
* these are the valid i/o ports we're allowed to change. they map all the
* video ports
*/
#define GPFIRST 0x3b4
#define GPLAST 0x3df
#define GPNUM (GPLAST - GPFIRST + 1)
 
/*
* global state includes the following, and various static variables
* in this module: shift_state, diacr, npadch, dead_key_next.
* (last_console is now a global variable)
*/
 
/* shift state counters.. */
static unsigned char k_down[NR_SHIFT] = {0, };
/* keyboard key bitmap */
#define BITS_PER_LONG (8*sizeof (unsigned long))
static unsigned long key_down[256/BITS_PER_LONG] = {0, };
static int dead_key_next;
 
/*
* In order to retrieve the shift_state (for the mouse server), either
* the variable must be global, or a new procedure must be created to
* return the value. I chose the former way.
*/
/*static*/ int shift_state;
static int npadch = -1; /* -1 or number assembled on pad */
static unsigned char diacr;
static char rep; /* Flag telling character repeat */
static int kbd_repeatkey = -1;
static int kbd_repeattimeout = REPEAT_TIMEOUT;
static int kbd_repeatrate = REPEAT_RATE;
 
static struct kbd_struct * kbd;
static struct tty_struct * tty;
 
extern void compute_shiftstate (void);
 
typedef void (*k_hand)(unsigned char value, char up_flag);
typedef void (k_handfn)(unsigned char value, char up_flag);
 
static k_handfn
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase, do_slock, do_ignore;
 
static k_hand key_handler[16] = {
do_self, do_fn, do_spec, do_pad, do_dead, do_cons, do_cur, do_shift,
do_meta, do_ascii, do_lock, do_lowercase, do_slock,
do_ignore, do_ignore, do_ignore
};
 
typedef void (*void_fnp)(void);
typedef void (void_fn)(void);
 
static void_fn do_null, enter, show_ptregs, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back, boot_it, caps_on, compose,
SAK, decr_console, incr_console, spawn_console, show_stack, bare_num;
 
static void_fnp spec_fn_table[] = {
do_null, enter, show_ptregs, show_mem,
show_state, send_intr, lastcons, caps_toggle,
num, hold, scroll_forw, scroll_back,
boot_it, caps_on, compose, SAK,
decr_console, incr_console, show_stack, bare_num
};
 
/* maximum values each key_handler can handle */
const int max_vals[] = {
255, SIZE(func_table) - 1, SIZE(spec_fn_table) - 1, NR_PAD - 1,
NR_DEAD - 1, 255, 3, NR_SHIFT - 1,
255, NR_ASCII - 1, NR_LOCK - 1, 255,
NR_LOCK - 1
};
 
const int NR_TYPES = SIZE(max_vals);
 
static void put_queue(int);
static unsigned char handle_diacr(unsigned char);
 
/* pt_regs - set by keyboard_interrupt(), used by show_ptregs() */
static struct pt_regs * pt_regs;
 
/*
* Many other routines do put_queue, but I think either
* they produce ASCII, or they produce some user-assigned
* string, and in both cases we might assume that it is
* in utf-8 already.
*/
void to_utf8(ushort c)
{
if (c < 0x80)
put_queue(c); /* 0******* */
else if (c < 0x800) {
put_queue(0xc0 | (c >> 6)); /* 110***** 10****** */
put_queue(0x80 | (c & 0x3f));
} else {
put_queue(0xe0 | (c >> 12)); /* 1110**** 10****** 10****** */
put_queue(0x80 | ((c >> 6) & 0x3f));
put_queue(0x80 | (c & 0x3f));
}
/* UTF-8 is defined for words of up to 31 bits,
but we need only 16 bits here */
}
 
/*
* As yet, we don't support setting and getting the
* key codes. I'll have to find out where this is used.
*/
int setkeycode(unsigned int scancode, unsigned int keycode)
{
return -EINVAL;
}
 
int getkeycode(unsigned int scancode)
{
return -EINVAL;
}
 
static void key_callback(unsigned long nr)
{
rep = 1;
mark_bh (KEYBOARD_BH);
}
 
static struct timer_list key_timer =
{
NULL, NULL, 0, 0, key_callback
};
 
void kbd_reset(void)
{
int i;
 
for (i = 0; i < NR_SHIFT; i++)
k_down[i] = 0;
for (i = 0; i < 256/BITS_PER_LONG; i++)
key_down[i] = 0;
shift_state = 0;
}
 
void kbd_setregs(struct pt_regs *regs)
{
pt_regs = regs;
}
 
static void kbd_processkey(unsigned int keycode, unsigned int up_flag, unsigned int autorepeat)
{
del_timer(&key_timer);
show_console();
add_keyboard_randomness (keycode|(up_flag?0x100:0));
 
tty = *vtdata.fgconsole->tty;
kbd = vtdata.fgconsole->kbd;
 
if (up_flag) {
rep = 0;
clear_bit (keycode, key_down);
/* don't report an error if key is already up - happens when a ghost key is released */
} else
rep = set_bit (keycode, key_down);
 
if (rep && !autorepeat)
return;
 
if (kbd->kbdmode == VC_RAW || kbd->kbdmode == VC_MEDIUMRAW) {
put_queue(keycode + (up_flag ? 0x80 : 0));
return;
}
 
/*
* We have to do our own auto-repeat processing...
*/
if (!up_flag) {
kbd_repeatkey = keycode;
if (vc_kbd_mode(kbd, VC_REPEAT)) {
if (rep)
key_timer.expires = jiffies + kbd_repeatrate;
else
key_timer.expires = jiffies + kbd_repeattimeout;
add_timer(&key_timer);
}
} else
kbd_repeatkey = -1;
 
/*
* Repeat a key only if the input buffers are empty or the
* characters get echoed locally. This makes key repeat usable
* with slow applications and under heavy loads.
*/
if (!rep ||
(vc_kbd_mode (kbd, VC_REPEAT) && tty &&
(L_ECHO(tty) || (tty->driver.chars_in_buffer (tty) == 0)))) {
u_short keysym;
u_char type;
 
/* the XOR below used to be an OR */
int shift_final = shift_state ^ kbd->lockstate ^ kbd->slockstate;
u_short *key_map = key_maps[shift_final];
 
if (key_map != NULL) {
keysym = key_map[keycode];
type = KTYP(keysym);
 
if (type >= 0xf0) {
type -= 0xf0;
if (type == KT_LETTER) {
type = KT_LATIN;
if (vc_kbd_led(kbd, VC_CAPSLOCK)) {
key_map = key_maps[shift_final ^ (1<<KG_SHIFT)];
if (key_map)
keysym = key_map[keycode];
}
}
(*key_handler[type])(keysym & 0xff, up_flag);
if (type != KT_SLOCK)
kbd->slockstate = 0;
} else {
/* maybe only if (kbd->kbdmode == VC_UNICODE) ? */
if (!up_flag)
to_utf8(keysym);
}
} else {
/*
* maybe beep?
* we have at least to update shift_state
* how? two almost equivalent choices follow
*/
#if 1
compute_shiftstate ();
#else
keysym = U(plain_map[keycode]);
type = KTYP(keysym);
if (type == KT_SHIFT)
(*key_handler[type]) (keysym & 0xff, up_flag);
#endif
}
}
}
 
void kbd_keyboardkey(unsigned int keycode, unsigned int up_flag)
{
kbd_processkey(keycode, up_flag, 0);
}
 
static void put_queue (int ch)
{
wake_up (&keypress_wait);
if (tty) {
tty_insert_flip_char (tty, ch, 0);
tty_schedule_flip (tty);
}
}
 
static void puts_queue(char *cp)
{
wake_up (&keypress_wait);
if (!tty)
return;
 
while (*cp) {
tty_insert_flip_char (tty, *cp, 0);
cp++;
}
tty_schedule_flip (tty);
}
 
static void show_stack (void)
{
unsigned long *p;
unsigned long *q = (unsigned long *)((int)current->kernel_stack_page + 4096);
int i;
__asm__("mov %0, sp\n\t": "=r" (p));
 
for (i = 0; p < q; p++, i++) {
if(i && !(i & 7))
printk("\n");
printk("%08lX ", *p);
}
}
 
static void applkey(int key, char mode)
{
static char buf[] = { 0x1b, 'O', 0x00, 0x00};
 
buf[1] = (mode ? 'O' : '[');
buf[2] = key;
puts_queue(buf);
}
 
static void enter(void)
{
put_queue (13);
if (vc_kbd_mode(kbd, VC_CRLF))
put_queue (10);
}
 
static void caps_toggle(void)
{
if(rep)
return;
chg_vc_kbd_led(kbd, VC_CAPSLOCK);
}
 
static void caps_on (void)
{
if (rep)
return;
set_vc_kbd_led (kbd, VC_CAPSLOCK);
}
 
static void show_ptregs (void)
{
if (pt_regs)
show_regs (pt_regs);
}
 
static void hold (void)
{
if(rep || !tty)
return;
 
/*
* Note: SCROLLOCK will be set (cleared) by stop_tty (start_tty);
* these routines are also activated by ^S/^Q.
* (And SCROLLOCK can also be set by the ioctl KDSKBLED.)
*/
if (tty->stopped)
start_tty (tty);
else
stop_tty (tty);
}
 
static void num (void)
{
if(vc_kbd_mode(kbd,VC_APPLIC))
applkey('P', 1);
else
bare_num ();
}
 
/*
* Bind this to Shift-NumLock if you work in application keypad mode
* but want to be able to change the NumLock flag.
* Bind this to NumLock if you prefer that the NumLock key always
* changes the NumLock flag.
*/
static void bare_num (void)
{
if (!rep)
chg_vc_kbd_led (kbd, VC_NUMLOCK);
}
 
static void lastcons (void)
{
/* switch to the last used console, ChN */
set_console(last_console);
}
 
static void decr_console (void)
{
int i;
int fgnum = vtdata.fgconsole->num - 1;
 
for (i = fgnum - 1; i != fgnum; i--) {
if (i == -1)
i = MAX_NR_CONSOLES - 1;
if (vt_allocated (vt_con_data + i))
break;
}
set_console(vt_con_data + i);
}
 
static void incr_console (void)
{
int i;
int fgnum = vtdata.fgconsole->num - 1;
 
for (i = fgnum + 1; i != fgnum; i++) {
if (i == MAX_NR_CONSOLES)
i = 0;
if (vt_allocated (vt_con_data + i))
break;
}
set_console(vt_con_data + i);
}
 
static void send_intr (void)
{
if (!tty)
return;
tty_insert_flip_char (tty, 0, TTY_BREAK);
tty_schedule_flip (tty);
}
 
static void scroll_forw (void)
{
scrollfront(0);
}
 
static void scroll_back (void)
{
scrollback(0);
}
 
static void boot_it (void)
{
ctrl_alt_del();
}
 
static void compose (void)
{
dead_key_next = 1;
}
 
int spawnpid, spawnsig;
 
static void spawn_console (void)
{
if (spawnpid)
if (kill_proc (spawnpid, spawnsig, 1))
spawnpid = 0;
}
 
static void SAK (void)
{
do_SAK(tty);
#if 0
/*
* Need to fix SAK handling to fix up RAW/MEDIUM_RAW and
* vt_cons modes before we can enable RAW/MEDIUM_RAW SAK
* handling.
*
* We should do this some day --- the whole point of a secure
* attention key is that it should be guaranteed to always
* work.
*/
vt_reset (vtdata.fgconsole);
do_unblank_screen (); /* not in interrupt routine? */
#endif
}
 
static void do_ignore (unsigned char value, char up_flag)
{
}
 
static void do_null (void)
{
compute_shiftstate ();
}
 
static void do_spec (unsigned char value,char up_flag)
{
if (up_flag)
return;
if (value >= SIZE(spec_fn_table))
return;
spec_fn_table[value] ();
}
 
static void do_lowercase (unsigned char value, char up_flag)
{
printk(KERN_ERR "keyboard.c: do_lowercase was called - impossible\n");
}
 
static void do_self(unsigned char value, char up_flag)
{
if (up_flag)
return; /* no action, if this is a key release */
 
if (diacr)
value = handle_diacr (value);
 
if (dead_key_next) {
dead_key_next = 0;
diacr = value;
return;
}
 
put_queue (value);
}
 
#define A_GRAVE '`'
#define A_ACUTE '\''
#define A_CFLEX '^'
#define A_TILDE '~'
#define A_DIAER '"'
#define A_CEDIL ','
static unsigned char ret_diacr[] =
{A_GRAVE, A_ACUTE, A_CFLEX, A_TILDE, A_DIAER, A_CEDIL };
 
/* If a dead key pressed twice, output a character corresponding to it, */
/* otherwise just remember the dead key. */
 
static void do_dead(unsigned char value, char up_flag)
{
if (up_flag)
return;
 
value = ret_diacr[value];
if (diacr == value) { /* pressed twice */
diacr = 0;
put_queue (value);
return;
}
diacr = value;
}
 
 
/* If space is pressed, return the character corresponding the pending */
/* dead key, otherwise try to combine the two. */
 
static unsigned char handle_diacr(unsigned char ch)
{
int d = diacr;
int i;
 
diacr = 0;
if (ch == ' ')
return d;
 
for (i = 0; i < accent_table_size; i++) {
if (accent_table[i].diacr == d && accent_table[i].base == ch)
return accent_table[i].result;
}
 
put_queue (d);
return ch;
}
 
static void do_cons(unsigned char value, char up_flag)
{
if (up_flag)
return;
set_console(vt_con_data + value);
}
 
static void do_fn(unsigned char value, char up_flag)
{
if (up_flag)
return;
if (value < SIZE(func_table)) {
if (func_table[value])
puts_queue (func_table[value]);
} else
printk (KERN_ERR "do_fn called with value=%d\n",value);
}
 
static void do_pad(unsigned char value, char up_flag)
{
static const char *pad_chars = "0123456789+-*/\015,.?#";
static const char *app_map = "pqrstuvwxylmRQMnn?S";
 
if (up_flag)
return; /* no action, if this is a key release */
 
/* kludge... shift forces cursor/number keys */
if (vc_kbd_mode (kbd, VC_APPLIC) && !k_down[KG_SHIFT]) {
applkey (app_map[value], 1);
return;
}
 
if (!vc_kbd_led (kbd,VC_NUMLOCK))
switch (value) {
case KVAL(K_PCOMMA):
case KVAL(K_PDOT):
do_fn (KVAL(K_REMOVE), 0);
return;
case KVAL(K_P0):
do_fn (KVAL(K_INSERT), 0);
return;
case KVAL(K_P1):
do_fn (KVAL(K_SELECT), 0);
return;
case KVAL(K_P2):
do_cur(KVAL(K_DOWN), 0);
return;
case KVAL(K_P3):
do_fn (KVAL(K_PGDN), 0);
return;
case KVAL(K_P4):
do_cur(KVAL(K_LEFT), 0);
return;
case KVAL(K_P6):
do_cur(KVAL(K_RIGHT), 0);
return;
case KVAL(K_P7):
do_fn (KVAL(K_FIND), 0);
return;
case KVAL(K_P8):
do_cur(KVAL(K_UP), 0);
return;
case KVAL(K_P9):
do_fn (KVAL(K_PGUP), 0);
return;
case KVAL(K_P5):
applkey('G',vc_kbd_mode(kbd, VC_APPLIC));
return;
}
 
put_queue (pad_chars[value]);
if (value == KVAL(K_PENTER) && vc_kbd_mode (kbd, VC_CRLF))
put_queue (10);
}
 
static void do_cur(unsigned char value, char up_flag)
{
static const char *cur_chars = "BDCA";
if (up_flag)
return;
 
applkey (cur_chars[value], vc_kbd_mode(kbd, VC_CKMODE));
}
 
static void do_shift(unsigned char value, char up_flag)
{
int old_state = shift_state;
 
if (rep)
return;
 
/* Mimic typewriter: a CapsShift key acts like Shift but undoes CapsLock */
if (value == KVAL(K_CAPSSHIFT)) {
value = KVAL(K_SHIFT);
if (!up_flag)
clr_vc_kbd_led (kbd, VC_CAPSLOCK);
}
 
if(up_flag) {
/* handle the case that two shift or control
keys are depressed simultaneously */
if(k_down[value])
k_down[value]--;
} else
k_down[value]++;
 
if(k_down[value])
shift_state |= (1 << value);
else
shift_state &= ~ (1 << value);
 
/* kludge */
if(up_flag && shift_state != old_state && npadch != -1) {
if(kbd->kbdmode == VC_UNICODE)
to_utf8(npadch & 0xffff);
else
put_queue(npadch & 0xff);
npadch = -1;
}
}
 
/*
* Called after returning from RAW mode or when changing consoles -
* recompute k_down[] and shift_state from key_down[]
* Maybe called when keymap is undefined so that shift key release is seen
*/
void compute_shiftstate(void)
{
int i, j, k, sym, val;
 
shift_state = 0;
for (i = 0; i < SIZE(k_down); i++)
k_down[i] = 0;
 
for (i = 0; i < SIZE(key_down); i++)
if (key_down[i]) { /* skip this word if not a single bit on */
k = i * BITS_PER_LONG;
for (j = 0; j < BITS_PER_LONG; j++, k++)
if (test_bit (k, key_down)) {
sym = U(plain_map[k]);
if (KTYP(sym) == KT_SHIFT) {
val = KVAL (sym);
if (val == KVAL(K_CAPSSHIFT))
val = KVAL(K_SHIFT);
k_down[val] ++;
shift_state |= (1 << val);
}
}
}
}
 
static void do_meta(unsigned char value, char up_flag)
{
if(up_flag)
return;
 
if(vc_kbd_mode(kbd, VC_META)) {
put_queue ('\033');
put_queue (value);
} else
put_queue (value | 0x80);
}
 
static void do_ascii(unsigned char value, char up_flag)
{
int base;
 
if (up_flag)
return;
 
if (value < 10) /* decimal input of code, while Alt depressed */
base = 10;
else { /* hexadecimal input of code, while AltGr depressed */
value -= 10;
base = 16;
}
 
if(npadch == -1)
npadch = value;
else
npadch = npadch * base + value;
}
 
static void do_lock (unsigned char value, char up_flag)
{
if (up_flag || rep)
return;
chg_vc_kbd_lock (kbd, value);
}
 
static void do_slock (unsigned char value, char up_flag)
{
if (up_flag || rep)
return;
chg_vc_kbd_slock (kbd, value);
}
 
/*
* The leds display either
* (i) The status of NumLock, CapsLock and ScrollLock.
* (ii) Whatever pattern of lights people wait to show using KDSETLED.
* (iii) Specified bits of specified words in kernel memory.
*/
 
static unsigned char ledstate = 0xff; /* undefined */
static unsigned char ledioctl;
 
unsigned char getledstate(void)
{
return ledstate;
}
 
void setledstate(struct kbd_struct *kbd, unsigned int led)
{
if(!(led & ~7)) {
ledioctl = led;
kbd->ledmode = LED_SHOW_IOCTL;
} else
kbd->ledmode = LED_SHOW_FLAGS;
set_leds();
}
 
static struct ledptr {
unsigned int *addr;
unsigned int mask;
unsigned char valid:1;
} ledptrs[3];
 
void register_leds(int console, unsigned int led,
unsigned int *addr, unsigned int mask)
{
struct kbd_struct *kbd = vt_con_data[console].kbd;
if(led < 3) {
ledptrs[led].addr = addr;
ledptrs[led].mask = mask;
ledptrs[led].valid= 1;
kbd->ledmode = LED_SHOW_MEM;
} else
kbd->ledmode = LED_SHOW_FLAGS;
}
 
static inline unsigned char getleds(void)
{
struct kbd_struct *kbd = vtdata.fgconsole->kbd;
unsigned char leds;
 
if(kbd->ledmode == LED_SHOW_IOCTL)
return ledioctl;
leds = kbd->ledflagstate;
if (kbd->ledmode == LED_SHOW_MEM) {
if (ledptrs[0].valid) {
if (*ledptrs[0].addr & ledptrs[0].mask)
leds |= 1;
else
leds &= ~1;
}
if (ledptrs[1].valid) {
if (*ledptrs[1].addr & ledptrs[1].mask)
leds |= 2;
else
leds &= ~2;
}
if (ledptrs[2].valid) {
if(*ledptrs[2].addr & ledptrs[2].mask)
leds |= 4;
else
leds &= ~4;
}
}
return leds;
}
 
/*
* This routine is the bottom half of the keyboard interrupt
* routine, and runs with all interrupts enabled. It does
* console changing, led setting and copy_to_cooked, which can
* take a reasonably long time.
*
* Aside from timing (which isn't really that important for
* keyboard interrupts as they happen often), using the software
* interrupt routines for this thing allows us to easily mask
* this when we don't want any of the above to happen. Not yet
* used, but this allows for easy and efficient race-condition
* prevention later on.
*/
static void kbd_bh(void)
{
unsigned char leds = getleds();
 
tty = *vtdata.fgconsole->tty;
kbd = vtdata.fgconsole->kbd;
 
if (rep) {
/*
* This prevents the kbd_key routine from being called
* twice, once by this BH, and once by the interrupt
* routine. Maybe we should put the key press in a
* buffer or variable, and then call the BH...
*/
disable_irq (IRQ_KEYBOARDRX);
if (kbd_repeatkey != -1)
kbd_processkey (kbd_repeatkey, 0, 1);
enable_irq (IRQ_KEYBOARDRX);
rep = 0;
}
 
if (leds != ledstate) {
ledstate = leds;
kbd_drv_setleds(leds);
}
}
 
/*
* Initialise a kbd struct
*/
int kbd_struct_init (struct vt *vt, int init)
{
vt->kbd->ledflagstate = vt->kbd->default_ledflagstate = KBD_DEFLEDS;
vt->kbd->ledmode = LED_SHOW_FLAGS;
vt->kbd->lockstate = KBD_DEFLOCK;
vt->kbd->slockstate = 0;
vt->kbd->modeflags = KBD_DEFMODE;
vt->kbd->kbdmode = VC_XLATE;
return 0;
}
 
int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg)
{
asmlinkage int sys_ioperm(unsigned long from, unsigned long num, int on);
int i;
 
switch (cmd) {
case KDGKBTYPE:
/*
* This is naive.
*/
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned char));
if (!i)
put_user (KB_101, (char *)arg);
return i;
 
case KDADDIO:
case KDDELIO:
/*
* KDADDIO and KDDELIO may be able to add ports beyond what
* we reject here, but to be safe...
*/
if (arg < GPFIRST || arg > GPLAST)
return -EINVAL;
return sys_ioperm (arg, 1, (cmd == KDADDIO)) ? -ENXIO : 0;
 
case KDENABIO:
case KDDISABIO:
return sys_ioperm (GPFIRST, GPLAST, (cmd == KDENABIO)) ? -ENXIO : 0;
 
case KDMAPDISP:
case KDUNMAPDISP:
/*
* These work like a combination of mmap and KDENABIO.
* this could easily be finished.
*/
return -EINVAL;
 
case KDSKBMODE:
switch (arg) {
case K_RAW:
vt->kbd->kbdmode = VC_RAW;
break;
case K_MEDIUMRAW:
vt->kbd->kbdmode = VC_MEDIUMRAW;
break;
case K_XLATE:
vt->kbd->kbdmode = VC_XLATE;
compute_shiftstate ();
break;
case K_UNICODE:
vt->kbd->kbdmode = VC_UNICODE;
compute_shiftstate ();
break;
default:
return -EINVAL;
}
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer (*vt->tty);
return 0;
 
case KDGKBMODE:
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
if (!i) {
int ucval;
ucval = ((vt->kbd->kbdmode == VC_RAW) ? K_RAW :
(vt->kbd->kbdmode == VC_MEDIUMRAW) ? K_MEDIUMRAW :
(vt->kbd->kbdmode == VC_UNICODE) ? K_UNICODE : K_XLATE);
put_user (ucval, (int *)arg);
}
return i;
 
case KDSKBMETA:
/*
* this could be folded into KDSKBMODE, but for compatibility
* reasons, it is not so easy to fold kDGKBMETA into KDGKBMODE.
*/
switch (arg) {
case K_METABIT:
clr_vc_kbd_mode (vt->kbd, VC_META);
break;
case K_ESCPREFIX:
set_vc_kbd_mode (vt->kbd, VC_META);
break;
default:
return -EINVAL;
}
return 0;
 
case KDGKBMETA:
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
if (!i) {
int ucval;
ucval = (vc_kbd_mode (vt->kbd, VC_META) ? K_ESCPREFIX : K_METABIT);
put_user (ucval, (int *)arg);
}
return i;
 
case KDGETKEYCODE: {
struct kbkeycode *const a = (struct kbkeycode *)arg;
 
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbkeycode));
if (!i) {
unsigned int sc;
 
sc = get_user (&a->scancode);
i = getkeycode (sc);
if (i > 0)
put_user (i, &a->keycode);
i = 0;
}
return i;
}
 
case KDSETKEYCODE: {
struct kbkeycode *const a = (struct kbkeycode *)arg;
 
i = verify_area (VERIFY_READ, a, sizeof (struct kbkeycode));
if (!i) {
unsigned int sc, kc;
sc = get_user (&a->scancode);
kc = get_user (&a->keycode);
i = setkeycode (sc, kc);
}
return i;
}
 
case KDGKBENT: {
struct kbentry *const a = (struct kbentry *)arg;
 
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
if (!i) {
ushort *keymap, val;
u_char s;
i = get_user (&a->kb_index);
if (i >= NR_KEYS)
return -EINVAL;
s = get_user (&a->kb_table);
if (s >= MAX_NR_KEYMAPS)
return -EINVAL;
keymap = key_maps[s];
if (keymap) {
val = U(keymap[i]);
if (vt->kbd->kbdmode != VC_UNICODE && KTYP(val) >= NR_TYPES)
val = K_HOLE;
} else
val = (i ? K_HOLE : K_NOSUCHMAP);
put_user (val, &a->kb_value);
i = 0;
}
return i;
}
 
case KDSKBENT: {
struct kbentry *const a = (struct kbentry *)arg;
 
i = verify_area (VERIFY_WRITE, a, sizeof (struct kbentry));
if (!i) {
ushort *key_map;
u_char s;
u_short v, ov;
 
if ((i = get_user(&a->kb_index)) >= NR_KEYS)
return -EINVAL;
if ((s = get_user(&a->kb_table)) >= MAX_NR_KEYMAPS)
return -EINVAL;
v = get_user(&a->kb_value);
if (!i && v == K_NOSUCHMAP) {
/* disallocate map */
key_map = key_maps[s];
if (s && key_map) {
key_maps[s] = 0;
if (key_map[0] == U(K_ALLOCATED)) {
kfree_s(key_map, sizeof(plain_map));
keymap_count--;
}
}
return 0;
}
 
if (KTYP(v) < NR_TYPES) {
if (KVAL(v) > max_vals[KTYP(v)])
return -EINVAL;
} else
if (kbd->kbdmode != VC_UNICODE)
return -EINVAL;
 
/* assignment to entry 0 only tests validity of args */
if (!i)
return 0;
 
if (!(key_map = key_maps[s])) {
int j;
if (keymap_count >= MAX_NR_OF_USER_KEYMAPS && !suser())
return -EPERM;
 
key_map = (ushort *) kmalloc(sizeof(plain_map),
GFP_KERNEL);
if (!key_map)
return -ENOMEM;
key_maps[s] = key_map;
key_map[0] = U(K_ALLOCATED);
for (j = 1; j < NR_KEYS; j++)
key_map[j] = U(K_HOLE);
keymap_count++;
}
ov = U(key_map[i]);
if (v == ov)
return 0; /* nothing to do */
/*
* Only the Superuser can set or unset the Secure
* Attention Key.
*/
if (((ov == K_SAK) || (v == K_SAK)) && !suser())
return -EPERM;
key_map[i] = U(v);
if (!s && (KTYP(ov) == KT_SHIFT || KTYP(v) == KT_SHIFT))
compute_shiftstate();
return 0;
}
return i;
}
 
case KDGKBSENT: {
struct kbsentry *a = (struct kbsentry *)arg;
char *p;
u_char *q;
int sz;
 
i = verify_area(VERIFY_WRITE, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC || i < 0)
return -EINVAL;
sz = sizeof(a->kb_string) - 1; /* sz should have been
a struct member */
q = a->kb_string;
p = func_table[i];
if(p)
for ( ; *p && sz; p++, sz--)
put_user(*p, q++);
put_user('\0', q);
return ((p && *p) ? -EOVERFLOW : 0);
}
 
case KDSKBSENT: {
struct kbsentry * const a = (struct kbsentry *)arg;
int delta;
char *first_free, *fj, *fnw;
int j, k, sz;
u_char *p;
char *q;
 
i = verify_area(VERIFY_READ, (void *)a, sizeof(struct kbsentry));
if (i)
return i;
if ((i = get_user(&a->kb_func)) >= MAX_NR_FUNC)
return -EINVAL;
q = func_table[i];
 
first_free = funcbufptr + (funcbufsize - funcbufleft);
for (j = i+1; j < MAX_NR_FUNC && !func_table[j]; j++) ;
if (j < MAX_NR_FUNC)
fj = func_table[j];
else
fj = first_free;
 
delta = (q ? -strlen(q) : 1);
sz = sizeof(a->kb_string); /* sz should have been
a struct member */
for (p = a->kb_string; get_user(p) && sz; p++,sz--)
delta++;
if (!sz)
return -EOVERFLOW;
if (delta <= funcbufleft) { /* it fits in current buf */
if (j < MAX_NR_FUNC) {
memmove(fj + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
if (func_table[k])
func_table[k] += delta;
}
if (!q)
func_table[i] = fj;
funcbufleft -= delta;
} else { /* allocate a larger buffer */
sz = 256;
while (sz < funcbufsize - funcbufleft + delta)
sz <<= 1;
fnw = (char *) kmalloc(sz, GFP_KERNEL);
if(!fnw)
return -ENOMEM;
 
if (!q)
func_table[i] = fj;
if (fj > funcbufptr)
memmove(fnw, funcbufptr, fj - funcbufptr);
for (k = 0; k < j; k++)
if (func_table[k])
func_table[k] = fnw + (func_table[k] - funcbufptr);
 
if (first_free > fj) {
memmove(fnw + (fj - funcbufptr) + delta, fj, first_free - fj);
for (k = j; k < MAX_NR_FUNC; k++)
if (func_table[k])
func_table[k] = fnw + (func_table[k] - funcbufptr) + delta;
}
if (funcbufptr != func_buf)
kfree_s(funcbufptr, funcbufsize);
funcbufptr = fnw;
funcbufleft = funcbufleft - delta + sz - funcbufsize;
funcbufsize = sz;
}
for (p = a->kb_string, q = func_table[i]; ; p++, q++)
if (!(*q = get_user(p)))
break;
return 0;
}
 
case KDGKBDIACR: {
struct kbdiacrs *a = (struct kbdiacrs *)arg;
 
i = verify_area(VERIFY_WRITE, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
put_user(accent_table_size, &a->kb_cnt);
memcpy_tofs(a->kbdiacr, accent_table,
accent_table_size*sizeof(struct kbdiacr));
return 0;
}
 
case KDSKBDIACR: {
struct kbdiacrs *a = (struct kbdiacrs *)arg;
unsigned int ct;
 
i = verify_area(VERIFY_READ, (void *) a, sizeof(struct kbdiacrs));
if (i)
return i;
ct = get_user(&a->kb_cnt);
if (ct >= MAX_DIACR)
return -EINVAL;
accent_table_size = ct;
memcpy_fromfs(accent_table, a->kbdiacr, ct*sizeof(struct kbdiacr));
return 0;
}
 
/* the ioctls below read/set the flags usually shown in the leds */
/* don't use them - they will go away without warning */
case KDGKBLED:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
return i;
put_user(vt->kbd->ledflagstate |
(vt->kbd->default_ledflagstate << 4), (char *) arg);
return 0;
 
case KDSKBLED:
if (arg & ~0x77)
return -EINVAL;
vt->kbd->ledflagstate = (arg & 7);
vt->kbd->default_ledflagstate = ((arg >> 4) & 7);
set_leds ();
return 0;
 
/* the ioctls below only set the lights, not the functions */
/* for those, see KDGKBLED and KDSKBLED above */
case KDGETLED:
i = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned char));
if (i)
return i;
put_user(getledstate(), (char *) arg);
return 0;
 
case KDSETLED:
setledstate(kbd, arg);
return 0;
 
/*
* A process can indicate its willingness to accept signals
* generated by pressing an appropriate key combination.
* Thus, one can have a daemon that e.g. spawns a new console
* upon a keypess and then changes to it.
* Probably init should be changed to do this (and have a
* field ks (`keyboard signal') in inittab describing the
* desired acion), so that the number of background daemons
* does not increase.
*/
case KDSIGACCEPT: {
if (arg < 1 || arg > NSIG || arg == SIGKILL)
return -EINVAL;
spawnpid = current->pid;
spawnsig = arg;
return 0;
}
default:
return -ENOIOCTLCMD;
}
}
 
int kbd_init (void)
{
int ret;
init_bh(KEYBOARD_BH, kbd_bh);
ret = kbd_drv_init();
mark_bh(KEYBOARD_BH);
return ret;
}
/serial6850.c
0,0 → 1,23
/*
* Dummy serial functions
*
* Copyright (C) 1995, 1996 Russell King
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/serial.h>
 
int rs_init (void)
{
return 0;
}
 
int register_serial (struct serial_struct *dev)
{
return -1;
}
 
void unregister_serial (int line)
{
}
/console.c
0,0 → 1,2138
/*
* linux/arch/arm/drivers/char/console.c
*
* Modifications (C) 1995, 1996 Russell King
*/
 
/*
* This module exports the console io functions:
*
* 'int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)'
* 'unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)'
* 'void vcd_disallocate (struct vt *vt)'
* 'int vcd_resize (unsigned long lines, unsigned long cols)'
* 'void vcd_blankscreen (int nopowersave)'
* 'void vcd_unblankscreen (void)'
* 'void vcd_savestate (const struct vt *vt, int blanked)'
* 'void vcd_restorestate (const struct vt *vt)'
* 'void vcd_setup_graphics (const struct vt *vt)'
* 'int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)'
* 'int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)'
*
*
* 'unsigned long con_init(unsigned long)'
* S 'int con_open(struct tty_struct *tty,struct file *filp)'
* S 'void con_write(struct tty_struct *tty)'
* S 'void console_print(const char *b)'
*
* 'void unblank_screen(void)'
* 'void scrollback(int lines)' *
* 'void scrollfront(int lines)' *
* 'int do_screendump(int arg)'
*
* 'int con_get_font(char *)'
* 'int con_set_font(char *)'
* 'int con_get_trans(char *)'
* 'int con_set_trans(char *)'
*
* 'int mouse_reporting(void)'
*/
 
#define BLANK 0x0020
 
/* A bitmap for codes <32. A bit of 1 indicates that the code
* corresponding to that bit number invokes a special action
* (such as cursor movement) and should not be displayed as a
* glyph unless the disp_ctrl mode is explicitly enabled.
*/
#define CTRL_ACTION 0x0d00ff81
#define CTRL_ALWAYS 0x0800f501 /* Cannot be overriden by disp_ctrl */
 
/*
* Here is the default bell parameters: 750HZ, 1/8th of a second
*/
#define DEFAULT_BELL_PITCH 750
#define DEFAULT_BELL_DURATION (HZ/8)
 
/*
* NOTE!!! We sometimes disable and enable interrupts for a short while
* (to put a word in video IO), but this will work even for keyboard
* interrupts. We know interrupts aren't enabled when getting a keyboard
* interrupt, as we use trap-gates. Hopefully all is well.
*/
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/kd.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/mm.h>
 
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/irq.h>
#include <asm/hardware.h>
 
#define DEBUG
 
#include "kbd_kern.h"
#include "consolemap.h"
#include "vt_kern.h"
#include "selection.h"
 
#define set_kbd(x) set_vc_kbd_mode (vt->kbd, x)
#define clr_kbd(x) clr_vc_kbd_mode (vt->kbd, x)
#define is_kbd(x) vc_kbd_mode (vt->kbd, x)
 
#define decarm VC_REPEAT
#define decckm VC_CKMODE
#define kbdapplic VC_APPLIC
#define lnm VC_CRLF
 
/*
* this is what the terminal answers to a ESC-Z or csi0c query.
*/
#define VT100ID "\033[?1;2c"
#define VT102ID "\033[?6c"
 
extern int setup_arm_irq(int, struct irqaction *);
 
/*
* routines to load custom translation table, EGA/VGA font and
* VGA colour palette from console.c
*/
extern int con_set_trans_old(unsigned char * table);
extern int con_get_trans_old(unsigned char * table);
extern int con_set_trans_new(unsigned short * table);
extern int con_get_trans_new(unsigned short * table);
extern void con_clear_unimap(struct unimapinit *ui);
extern int con_set_unimap(ushort ct, struct unipair *list);
extern int con_get_unimap(ushort ct, ushort *uct, struct unipair *list);
extern void con_set_default_unimap(void);
extern int con_set_font(char * fontmap);
extern int con_get_font(char * fontmap);
 
 
/* ARM Extensions */
#if defined(HAS_VIDC)
 
extern void memc_write (int reg, int val);
extern void vidc_write (int reg, int val);
static int __r;
#define palette_setpixel(p) __r = p
#define palette_write(v) \
{ \
int rr = __r++, red, green, blue, vv = (v); \
red = (vv >> 4) & 0x00f; \
green = (vv >> 8) & 0x0f0; \
blue = (vv >> 12) & 0xf00; \
outl((rr<<26)|red|green|blue, IO_VIDC_BASE); \
}
 
#define MAX_PIX 16
 
#elif defined(HAS_VIDC20)
#define palette_setpixel(p) outl(0x10000000|((p) & 255), IO_VIDC_BASE)
#define palette_write(v) outl(0x00000000|((v) & 0x00ffffff), IO_VIDC_BASE)
#define MAX_PIX 256
#endif
 
/*
* from ll_wr_char.S
*/
int bytes_per_char_h, bytes_per_char_v, video_size_row;
extern void ll_write_char(unsigned long ps, unsigned long chinfo);
extern unsigned long con_charconvtable[256];
extern unsigned long map_screen_mem (unsigned long vid_base, unsigned long kmem, int remap);
#include <linux/ctype.h>
 
extern struct console_driver screen_driver, buffer_driver;
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
static void gotoxy (struct con_struct *vcd, int new_x, int new_y);
static inline void set_cursor (const struct vt * const vt);
extern void reset_vc(const struct vt * const vt);
extern void register_console(void (*proc)(const char *));
extern void compute_shiftstate(void);
extern void con_reset_palette (const struct vt * const vt);
extern void con_set_palette (const struct vt * const vt);
 
static int printable; /* Is console ready for printing? */
 
#define console_charmask 0xff
#ifndef console_charmask
static unsigned short console_charmask = 0x0ff;
#endif
 
#include "font.h"
 
static const unsigned long palette_1[MAX_PIX] = {
0x00000000, /* Black */
0x00ffffff, /* White */
0x00000000 /* Black */
};
 
static const unsigned long palette_4[MAX_PIX] = {
0x00000000, /* Black */
0x000000cc, /* Red */
0x0000cc00, /* Green */
0x0000cccc, /* Yellow */
0x00cc0000, /* Blue */
0x00cc00cc, /* Magenta */
0x00cccc00, /* Cyan */
0x00cccccc, /* White */
0x00000000,
0x000000ff,
0x0000ff00,
0x0000ffff,
0x00ff0000,
0x00ff00ff,
0x00ffff00,
0x00ffffff
};
 
#if defined(HAS_VIDC)
static const unsigned long palette_8[MAX_PIX] = {
0x00000000,
0x00111111,
0x00222222,
0x00333333,
0x00440000,
0x00551111,
0x00662222,
0x00773333,
0x00000044,
0x00111155,
0x00222266,
0x00333377,
0x00440044,
0x00551155,
0x00662266,
0x00773377
};
#elif defined(HAS_VIDC20)
#define palette_8 palette_4
#endif
 
static const unsigned long *default_palette_entries = palette_4;
 
static const unsigned char color_1[] = {0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1 };
static const unsigned char color_4[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
#if defined(HAS_VIDC)
static const unsigned char color_8[] = {0x00, 0x18, 0x60, 0x78, 0x84, 0x9C, 0xE4, 0xFC,
0x00, 0x1B, 0x63, 0x7B, 0x87, 0x9F, 0xE7, 0xFF};
#elif defined(HAS_VIDC20)
static const unsigned char color_8[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14,15 };
#endif
static const unsigned char *color_table = color_4;
 
/*===================================================================================*/
 
#ifdef CONFIG_SERIAL_ECHO
#include "serialecho.c"
#endif
 
/*
* functions to handle /dev/fb
*/
int con_fb_read(char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_write(const char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_mmap(unsigned long vma_start, unsigned long vma_offset,
unsigned long vma_end, pgprot_t prot)
{
if (vma_offset > vtdata.screen.memsize ||
vma_end - vma_start + vma_offset > vtdata.screen.memsize)
return -EINVAL;
return remap_page_range(vma_start, SCREEN2_BASE + vma_offset,
vma_end - vma_start, prot);
}
 
#ifdef DEBUG
static int vcd_validate (struct con_struct *vcd, const char *msg)
{
unsigned long old_scrorigin, old_scrpos, old_buforigin, old_bufpos, w = 0;
 
old_scrorigin = vcd->screen.origin;
old_scrpos = vcd->screen.pos;
old_buforigin = vtdata.buffer.origin;
old_bufpos = vcd->buffer.pos;
if (vcd->screen.origin < SCREEN1_BASE || vcd->screen.origin > SCREEN1_END) {
vcd->screen.origin = SCREEN2_BASE;
w = 1;
}
if (vcd->screen.pos < SCREEN1_BASE || vcd->screen.pos > SCREEN2_END) {
vcd->screen.pos = SCREEN2_BASE;
w = 1;
}
if (w)
printk ("*** CONSOLE ERROR: %s: (%p): origin = %08lX pos = %08lX ***\n",
msg, vcd, old_scrorigin, old_scrpos);
return w;
}
#endif
 
void no_scroll(char *str, int *ints)
{
}
 
static unsigned long __origin; /* Offset of currently displayed screen */
 
static void __set_origin(unsigned long offset)
{
unsigned long flags;
 
clear_selection ();
 
offset = offset - vtdata.screen.memstart;
if (offset >= vtdata.screen.memsize)
offset -= vtdata.screen.memsize;
 
save_flags_cli (flags);
__origin = offset;
video_set_dma(0, vtdata.screen.memsize, offset);
restore_flags(flags);
}
 
void scrollback(int lines)
{
}
 
void scrollfront(int lines)
{
}
 
void scroll_set_origin (unsigned long offset)
{
__set_origin(offset);
}
 
static void set_origin (const struct vt * const vt)
{
if (vtdata.fgconsole != vt || vt->vtd->vc_mode == KD_GRAPHICS)
return;
__set_origin(vt->vcd->screen.origin);
}
 
/* -------------------------------------------------------------------------------
* Cursor
* ------------------------------------------------------------------------------- */
 
static char cursor_on = 0;
static unsigned long cp;
 
static void put_cursor(char on_off,unsigned long newcp)
{
static char con;
unsigned long cp_p = cp;
int c = vtdata.screen.bytespercharh == 8 ? color_table[15] : 0x11 * color_table[15];
int i;
 
if (vtdata.fgconsole->vtd->vc_mode == KD_GRAPHICS)
return;
 
cp = newcp;
 
if (con != on_off) {
if (cp_p != -1)
for (i = 0; i < vtdata.screen.bytespercharh; i++)
((unsigned char*)cp_p)[i]^=c;
con = on_off;
}
}
 
static void vsync_irq(int irq, void *dev_id, struct pt_regs *regs)
{
static char cursor_flash = 16;
 
if (!--cursor_flash) {
cursor_flash = 16;
cursor_on = cursor_on ? 0 : 1;
if (vtdata.fgconsole->vcd->screen.cursoron > 0)
put_cursor(cursor_on,cp);
}
}
 
static void hide_cursor(void)
{
put_cursor (0, -1);
}
 
static void set_cursor (const struct vt * const vt)
{
unsigned long flags;
 
if (vt != vtdata.fgconsole || vt->vtd->vc_mode == KD_GRAPHICS)
return;
 
save_flags_cli (flags);
if (vt->vcd->deccm) {
#ifdef DEBUG
vcd_validate (vt->vcd, "set_cursor");
#endif
cp = vt->vcd->screen.pos + vtdata.numcolumns *
vtdata.screen.bytespercharh * (vtdata.screen.bytespercharv - 1);
} else
hide_cursor();
restore_flags(flags);
}
 
static void vcd_removecursors (const struct vt *vt)
{
unsigned long flags;
 
save_flags_cli (flags);
 
if (--vt->vcd->screen.cursoron == 0 && vtdata.fgconsole == vt)
put_cursor (0, cp);
 
restore_flags (flags);
}
 
static void vcd_restorecursors(const struct vt *vt)
{
unsigned long flags;
 
save_flags_cli (flags);
 
if (++vt->vcd->screen.cursoron == 1 && cursor_on && vtdata.fgconsole == vt)
put_cursor (1, cp);
 
restore_flags (flags);
}
 
/* -----------------------------------------------------------------------------------------
* VC stuff
* ----------------------------------------------------------------------------------------- */
/*
* gotoxy() must verify all boundaries, because the arguments
* might also be negative. If a given position is out of
* bounds, the cursor is placed at the nearest margin.
*/
static void gotoxy (struct con_struct *vcd, int new_x, int new_y)
{
int min_y, max_y;
 
if (new_x < 0)
vcd->curstate.x = 0;
else
if (new_x >= vtdata.numcolumns)
vcd->curstate.x = vtdata.numcolumns - 1;
else
vcd->curstate.x = new_x;
 
if (vcd->decom) {
new_y += vcd->top;
min_y = vcd->top;
max_y = vcd->bottom;
} else {
min_y = 0;
max_y = vtdata.numrows;
}
 
if (new_y < min_y)
vcd->curstate.y = min_y;
else if (new_y >= max_y)
vcd->curstate.y = max_y - 1;
else
vcd->curstate.y = new_y;
 
vcd->driver.gotoxy (vcd);
vcd->need_wrap = 0;
#ifdef DEBUG
vcd_validate (vcd, "gotoxy");
#endif
}
 
/* for absolute user moves, when decom is set */
static void gotoxay (struct con_struct *vcd, int new_x, int new_y)
{
gotoxy(vcd, new_x, vcd->decom ? (vcd->top+new_y) : new_y);
}
 
static void update_attr (const struct vt * const vt)
{
unsigned int backcol, forecol;
 
backcol = color_table[vt->vcd->curstate.backcol];
if (vt->vcd->curstate.flags & FLG_BOLD)
forecol = color_table[vt->vcd->curstate.forecol + 8];
else
forecol = color_table[vt->vcd->curstate.forecol];
vt->vcd->combined_state = (vt->vcd->curstate.flags << 24) |
(backcol << 16) |
(forecol << 8);
switch (vtdata.screen.bitsperpix) {
case 1:
vt->vcd->cached_backcolwrd = 0;
break;
default:
case 4:
vt->vcd->cached_backcolwrd = 0x11111111 * backcol;
break;
case 8:
vt->vcd->cached_backcolwrd = 0x01010101 * backcol;
break;
}
}
 
static void default_attr (const struct vt * const vt)
{
vt->vcd->curstate.flags &= ~(FLG_INVERSE|FLG_FLASH|FLG_UNDERLINE|FLG_ITALIC|FLG_BOLD);
vt->vcd->curstate.forecol = vt->vcd->def_forecol;
vt->vcd->curstate.backcol = vt->vcd->def_backcol;
}
 
static int csi_m (const struct vt * const vt)
{
struct con_struct *vcd = vt->vcd;
int i, ret = 0;
 
for (i = 0; i <= vcd->npar; i++) {
switch (vcd->par[i]) {
case 0:
default_attr (vt);
break;
case 1:
vcd->curstate.flags |= FLG_BOLD;
break; /* Bold */
case 2:
vcd->curstate.flags &= ~FLG_BOLD;
break; /* Feint */
case 3:
vcd->curstate.flags |= FLG_ITALIC;
break; /* Italic */
case 4:
vcd->curstate.flags |= FLG_UNDERLINE;
break; /* Underline */
case 5:
case 6:
vcd->curstate.flags |= FLG_FLASH;
break; /* Flash */
case 7:
vcd->curstate.flags |= FLG_INVERSE;
break; /* Inverse chars */
 
case 10:
/* ANSI X3.64-1979 (SCO-ish?)
* Select primary font, don't display
* control chars if defined, don't set
* bit 8 on output.
*/
vcd->translate = set_translate(vcd->curstate.flags & FLG_CHRSET
? vcd->curstate.G1_charset
: vcd->curstate.G0_charset);
vcd->disp_ctrl = 0;
vcd->toggle_meta = 0;
ret = 1;
break;
case 11:
/* ANSI X3.64-1979 (SCO-ish?)
* Select first alternate font, let's
* chars < 32 be displayed as ROM chars.
*/
vcd->translate = set_translate(IBMPC_MAP);
vcd->disp_ctrl = 1;
vcd->toggle_meta = 0;
ret = 1;
break;
case 12:
/* ANSI X3.64-1979 (SCO-ish?)
* Select second alternate font, toggle
* high bit before displaying as ROM char.
*/
vcd->translate = set_translate(IBMPC_MAP);
vcd->disp_ctrl = 1;
vcd->toggle_meta = 1;
ret = 1;
break;
case 21:
case 22:
vcd->curstate.flags &= ~FLG_BOLD;
break;
case 24:
vcd->curstate.flags &= ~FLG_UNDERLINE;
break;
case 25:
vcd->curstate.flags &= ~FLG_FLASH;
break;
case 27:
vcd->curstate.flags &= ~FLG_INVERSE;
break;
case 30:
case 31:
case 32:
case 33:
case 34:
case 35:
case 36:
case 37:
vcd->curstate.forecol = vcd->par[i]-30;
break; /* Foreground colour */
case 38:
vcd->curstate.forecol = vcd->def_forecol;
vcd->curstate.flags &= ~FLG_UNDERLINE;
break;
case 39:
vcd->curstate.forecol = vcd->def_forecol;
vcd->curstate.flags &= ~FLG_UNDERLINE;
break; /* Default foreground colour */
case 40:
case 41:
case 42:
case 43:
case 44:
case 45:
case 46:
case 47:
vcd->curstate.backcol = vcd->par[i]-40;
break; /* Background colour */
case 49:
vcd->curstate.backcol = vcd->def_backcol;
break; /* Default background colour */
}
}
update_attr (vt);
return ret;
}
 
static void respond_string (char *p, struct tty_struct *tty)
{
while (*p)
tty_insert_flip_char (tty, *p++, 0);
tty_schedule_flip (tty);
}
 
static void cursor_report (const struct vt *vt)
{
char buf[40];
 
sprintf (buf, "\033[%d;%dR", vt->vcd->curstate.y + (vt->vcd->decom ? vt->vcd->top + 1 : 1),
vt->vcd->curstate.x + 1);
respond_string (buf, *vt->tty);
}
 
static void status_report (struct tty_struct *tty)
{
respond_string ("\033[0n", tty);
}
 
static void respond_ID (struct tty_struct *tty)
{
respond_string(VT102ID, tty);
}
 
void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry)
{
char buf[8];
 
sprintf (buf,"\033[M%c%c%c", (char)(' ' + butt), (char)('!' + mrx),
(char)('!'+mry));
respond_string(buf, tty);
}
 
/* invoked by ioctl(TIOCLINUX) */
int mouse_reporting (void)
{
return vtdata.fgconsole->vcd->report_mouse;
}
 
static inline unsigned long screenpos (const struct vt * const vt, int offset)
{
int hx, hy;
 
hx = offset % vtdata.numcolumns;
hy = offset / vtdata.numcolumns;
return vt->vcd->screen.origin + hy * vtdata.screen.sizerow + hx * vtdata.screen.bytespercharh;
}
 
void invert_screen (const struct vt * const vt, unsigned int offset, unsigned int count)
{
struct con_struct *vcd = vt->vcd;
unsigned long *buffer = vcd->driver.buffer_pos(vcd, offset);
unsigned long p, pp;
int i;
 
for (i = 0; i <= count; i++)
buffer[i] ^= FLG_INVERSE << 24;
 
if (vt == vtdata.fgconsole) {
int hx, hy, hex, hey;
 
hx = offset % vtdata.numcolumns;
hy = offset / vtdata.numcolumns;
hex = (offset + count) % vtdata.numcolumns;
hey = (offset + count) / vtdata.numcolumns;
 
p = vcd->screen.origin + hy * vtdata.screen.sizerow;
pp = p + hx * vtdata.screen.bytespercharh;
for (;hy <= hey; hy ++) {
for (; hx < ((hy == hey) ? hex + 1 : vtdata.numcolumns); hx ++) {
ll_write_char (pp, *buffer++);
pp += vtdata.screen.bytespercharh;
}
hx = 0;
pp = p += vtdata.screen.sizerow;
}
}
}
 
void complement_pos (const struct vt * const vt, unsigned int offset)
{
static unsigned long old = 0;
static unsigned long p = 0;
unsigned long complement;
 
switch (vtdata.screen.bitsperpix) {
case 4:
complement = 0x00070700;
break;
case 8:
#ifndef HAS_VIDC20
complement = 0x00fcfc00;
#else
complement = 0x00070700;
#endif
break;
default:
complement = 0;
}
 
if (p)
ll_write_char (p, old);
if ((int)offset == -1)
p = 0;
else {
old = *vt->vcd->driver.buffer_pos(vt->vcd, offset);
p = screenpos (vt, offset);
ll_write_char (p, old ^ complement);
}
}
 
unsigned long screen_word (const struct vt * const vt, unsigned int offset)
{
return *vt->vcd->driver.buffer_pos (vt->vcd, offset);
}
 
int scrw2glyph (unsigned long scr_word)
{
return scr_word & 255;
}
 
unsigned long *screen_pos (const struct vt * const vt, unsigned int offset)
{
return vt->vcd->driver.buffer_pos (vt->vcd, offset);
}
 
void getconsxy (const struct vt * const vt, char *p)
{
p[0] = vt->vcd->curstate.x;
p[1] = vt->vcd->curstate.y;
}
 
void putconsxy (const struct vt * const vt, char *p)
{
gotoxy (vt->vcd, p[0], p[1]);
set_cursor (vt);
}
 
static void csi_J (const struct vt * const vt, int vpar);
 
static void set_mode (const struct vt * const vt, int on_off)
{
struct con_struct *vcd = vt->vcd;
int i;
 
for (i = 0; i <= vcd->npar; i++)
if (vcd->ques)
switch(vcd->par[i]) { /* DEC private modes set/reset */
case 1: /* Cursor keys send ^[Ox/^[[x */
if (on_off)
set_kbd(decckm);
else
clr_kbd(decckm);
break;
case 3: /* 80/132 mode switch unimplemented */
vcd->deccolm = on_off;
csi_J (vt, 2);
gotoxy (vcd, 0, 0);
break;
case 5: /* Inverted screen on/off */
if (vcd->decscnm != on_off) {
vcd->decscnm = on_off;
invert_screen (vt, 0, vtdata.buffer.totsize);
update_attr (vt);
}
break;
case 6: /* Origin relative/absolute */
vcd->decom = on_off;
gotoxay (vcd, 0, 0);
break;
case 7: /* Autowrap on/off */
vcd->decawm = on_off;
break;
case 8: /* Autorepeat on/off */
if (on_off)
set_kbd(decarm);
else
clr_kbd(decarm);
break;
case 9:
vcd->report_mouse = on_off ? 1 : 0;
break;
case 25: /* Cursor on/off */
vcd->deccm = on_off;
set_cursor (vt);
break;
case 1000:
vcd->report_mouse = on_off ? 2 : 0;
break;
}
else
switch(vcd->par[i]) { /* ANSI modes set/reset */
case 3: /* Monitor (display ctrls) */
vcd->disp_ctrl = on_off;
break;
case 4: /* Insert mode on/off */
vcd->decim = on_off;
break;
case 20: /* Lf, Enter = CrLf/Lf */
if (on_off)
set_kbd(lnm);
else
clr_kbd(lnm);
break;
}
}
 
static void setterm_command (const struct vt * const vt)
{
struct con_struct *vcd = vt->vcd;
switch (vt->vcd->par[0]) {
case 1: /* Set colour for underline mode (implemented as an underline) */
case 2: /* set colour for half intensity mode (unimplemented) */
break;
case 8:
vcd->def_forecol = vcd->curstate.forecol;
vcd->def_backcol = vcd->curstate.backcol;
break;
case 9:
vtdata.screen.blankinterval =
((vcd->par[1] < 60) ? vcd->par[1] : 60) * 60 * HZ;
vt_pokeblankedconsole ();
break;
case 10: /* set bell frequency in Hz */
if (vcd->npar >= 1)
vcd->bell_pitch = vcd->par[1];
else
vcd->bell_pitch = DEFAULT_BELL_PITCH;
break;
case 11: /* set bell duration in msec */
if (vcd->npar >= 1)
vcd->bell_duration = (vcd->par[1] < 2000) ?
vcd->par[1]*HZ/1000 : 0;
else
vcd->bell_duration = DEFAULT_BELL_DURATION;
break;
case 12: { /* bring specified console to the front */
int arg = vcd->par[1];
if (arg >= 1 && vt_allocated(vt_con_data + (arg - 1) ))
vt_updatescreen(vt_con_data + (arg - 1));
break;
}
case 13: /* unblank the screen */
vt_do_unblankscreen ();
break;
case 14: /* set vesa powerdown interval */
#if todo
vesa_off_interval = ((vcd->par[1] < 60) ? vcd->par[1] : 60) * 60 * HZ;
#endif
break;
}
}
 
static void csi_J (const struct vt * const vt, int vpar)
{
unsigned char endx, endy;
unsigned char startx, starty;
 
switch (vpar) {
case 0: /* erase from cursor to bottom of screen (including char at (x, y) */
startx = vt->vcd->curstate.x;
starty = vt->vcd->curstate.y;
endx = vtdata.numcolumns - 1;
endy = vtdata.numrows - 1;
break;
case 1: /* erase from top of screen to cursor (including char at (x, y) */
startx = 0;
starty = 0;
endx = vt->vcd->curstate.x;
endy = vt->vcd->curstate.y;
break;
case 2: /* erase entire screen */
startx = 0;
starty = 0;
endx = vtdata.numcolumns - 1;
endy = vtdata.numrows - 1;
#if TODO
origin = video_mem_base;
set_origin (currcons);
gotoxy (currcons, x, y);
#endif
break;
default:
return;
}
vt->vcd->driver.erase (vt->vcd, startx, starty, endx, endy);
/*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
}
 
static void csi_K (const struct vt * const vt, int vpar)
{
unsigned char endx;
unsigned char startx;
 
switch(vpar) {
case 0: /* erase from cursor to end of line */
startx = vt->vcd->curstate.x;
endx = vtdata.numcolumns - 1;
break;
case 1: /* erase from beginning of line to cursor */
startx = 0;
endx = vt->vcd->curstate.x;
break;
case 2: /* erase entire line */
startx = 0;
endx = vtdata.numcolumns - 1;
break;
default:
return;
}
vt->vcd->driver.erase(vt->vcd, startx, vt->vcd->curstate.y, endx, vt->vcd->curstate.y);
/*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
}
 
static void csi_X (const struct vt * const vt, int vpar) /* erase the following vpar positions */
{ /* not vt100? */
unsigned char countx, county;
unsigned char startx, starty;
 
if (!vpar)
vpar++;
 
startx = vt->vcd->curstate.x;
starty = vt->vcd->curstate.y;
countx = 0;
county = 0;
#if TODO
vt->vcd->driver.erase(vt->vcd,startx,starty,countx,county);
#endif
/*vt->vcd->need_wrap = 0; why? We don't move the cursor... */
}
 
static void csi_at (const struct vt * const vt, int nr)
{
if (nr > vtdata.numcolumns - vt->vcd->curstate.x)
nr = vtdata.numcolumns - vt->vcd->curstate.x;
else if (!nr)
nr = 1;
vt->vcd->driver.insert_char(vt->vcd, nr);
}
 
static void csi_L (const struct vt * const vt, int nr)
{
if (nr > vtdata.numrows - vt->vcd->curstate.y)
nr = vtdata.numrows - vt->vcd->curstate.y;
else if (!nr)
nr = 1;
vt->vcd->driver.scroll_down (vt->vcd, vt->vcd->curstate.y, vt->vcd->bottom, nr);
/* vt->vcd->need_wrap = 0; why? We haven't moved the cursor. */
}
 
static void csi_P (const struct vt * const vt, int nr)
{
if (nr > vtdata.numcolumns)
nr = vtdata.numcolumns;
else if (!nr)
nr = 1;
vt->vcd->driver.delete_char(vt->vcd, nr);
}
 
static void csi_M (const struct vt * const vt, int nr)
{
if (nr > vtdata.numrows)
nr = vtdata.numrows;
else if (!nr)
nr = 1;
vt->vcd->driver.scroll_up (vt->vcd, vt->vcd->curstate.y, vt->vcd->bottom, nr);
/* vt->vcd->need_wrap = 0; why? we haven't moved the cursor. */
}
 
enum { ESnormal, ESesc, ESsquare, ESgetpars, ESgotpars, ESfunckey,
EShash, ESsetG0, ESsetG1, ESpercent, ESignore, ESnonstd,
ESpalette };
 
static void reset_terminal (const struct vt * const vt, int initialising)
{
struct con_struct *vcd;
 
vcd = vt->vcd;
 
vcd->top = 0;
vcd->bottom = vtdata.numrows;
vcd->state = ESnormal;
vcd->ques = 0;
vcd->translate = set_translate (LAT1_MAP);
vcd->curstate.G0_charset = LAT1_MAP;
vcd->curstate.G1_charset = GRAF_MAP;
vcd->curstate.flags = 0;
vcd->need_wrap = 0;
vcd->report_mouse = 0;
vcd->utf = 0;
vcd->utf_count = 0;
vcd->disp_ctrl = 0;
vcd->toggle_meta = 0;
vcd->decscnm = 0;
vcd->decom = 0;
vcd->decawm = 1;
vcd->deccm = 1;
vcd->decim = 0;
vcd->curstate.forecol = 7;
vcd->curstate.backcol = 0;
vcd->def_forecol = 7;
vcd->def_backcol = 0;
vcd->tab_stop[0] = 0x01010100;
vcd->tab_stop[1] =
vcd->tab_stop[2] =
vcd->tab_stop[3] =
vcd->tab_stop[4] = 0x01010101;
 
set_kbd (decarm);
clr_kbd (decckm);
clr_kbd(kbdapplic);
clr_kbd(lnm);
 
vt->kbd->lockstate = 0;
vt->kbd->ledmode = LED_SHOW_FLAGS;
vt->kbd->ledflagstate = vt->kbd->default_ledflagstate;
if (!initialising)
set_leds ();
 
if (vcd->screen.palette_entries) {
kfree (vcd->screen.palette_entries);
vcd->screen.palette_entries = 0;
}
 
gotoxy (vcd, 0, 0);
vcd->savedstate = vcd->curstate;
update_attr (vt);
 
if (!initialising)
csi_J (vt, 2);
}
 
 
static inline void update_palette(const struct vt * const vt)
{
const unsigned long *palette_entries;
int i;
 
if (vt->vcd->screen.palette_entries || vt->vtd->vc_mode != KD_GRAPHICS)
palette_entries = default_palette_entries;
else
palette_entries = vt->vcd->screen.palette_entries;
 
palette_setpixel(0);
for (i = MAX_PIX; i > 3; i -= 4) {
palette_write(*palette_entries++);
palette_write(*palette_entries++);
palette_write(*palette_entries++);
palette_write(*palette_entries++);
}
for (; i > 0; i--)
palette_write(*palette_entries++);
}
 
void update_scrmem (const struct vt * const vt, int start, int length)
{
unsigned long p, pp, sx, sy, ex, ey;
unsigned long *buffer;
 
length += start;
sy = start / vtdata.numcolumns;
sx = start % vtdata.numcolumns;
ey = length / vtdata.numcolumns;
ex = length % vtdata.numcolumns;
 
if (ey > vtdata.numrows)
ey = vtdata.numrows;
 
p = vt->vcd->screen.origin + sy * vtdata.screen.sizerow;
buffer = vt->vcd->buffer.buffer + start;
 
if (ey > sy) {
for (; sy < ey; sy++) {
pp = p + sx * vtdata.screen.bytespercharh;
for (; sx < vtdata.numcolumns; sx++) {
ll_write_char (pp, *buffer++);
pp += vtdata.screen.bytespercharh;
}
p += vtdata.screen.sizerow;
sx = 0;
}
}
 
if (ey == sy && ex) {
for (; sx < ex; sx++) {
ll_write_char (p, *buffer++);
p += vtdata.screen.bytespercharh;
}
}
}
 
void set_scrmem (const struct vt * const vt, long offset)
{
unsigned long p, pp, my, by;
unsigned long *buffer;
 
p = vt->vcd->screen.origin;
buffer = vt->vcd->buffer.buffer;
 
by = vtdata.screen.sizerow;
for (my = vtdata.numrows; my > 0; my--) {
int mx, bx = vtdata.screen.bytespercharh;
pp = p;
mx = vtdata.numcolumns;
while (mx > 8) {
mx -= 8;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
ll_write_char (pp, *buffer++); pp += bx;
}
while (mx > 0) {
mx -= 1;
ll_write_char (pp, *buffer++); pp += bx;
}
p += by;
}
pp = vt->vcd->screen.origin + vtdata.screen.memsize;
memsetl((unsigned long *)p, vt->vcd->cached_backcolwrd, pp - p);
update_palette(vt);
}
 
/*
* PIO_FONT support
*/
int con_set_font (char *arg)
{
return -EINVAL;
}
 
int con_get_font (char *arg)
{
return -EINVAL;
}
 
void con_reset_palette (const struct vt * const vt)
{
}
 
void con_set_palette (const struct vt * const vt)
{
update_palette(vt);
}
 
/* == arm specific console code ============================================================== */
 
int do_screendump(int arg)
{
char *buf = (char *)arg;
int l;
if (!suser())
return -EPERM;
l = verify_area (VERIFY_WRITE, buf, 2);
if (l)
return l;
return -ENOSYS;
}
 
void vcd_disallocate (struct vt *vt)
{
if (vt->vcd && vt->vcd->buffer.kmalloced) {
vfree (vt->vcd->buffer.buffer);
vt->vcd->buffer.buffer = NULL;
vt->vcd->buffer.kmalloced = 0;
}
}
 
int vcd_resize(unsigned long lines, unsigned long cols)
{/* TODO */
return -ENOMEM;
}
 
void vcd_blankscreen(int nopowersave)
{
unsigned int pix;
 
/* DISABLE VIDEO */
palette_setpixel(0);
for (pix = 0; pix < MAX_PIX; pix++)
palette_write(0);
}
 
void vcd_unblankscreen (void)
{
update_palette(vtdata.fgconsole);
}
 
static unsigned long old_origin;
 
void vcd_savestate (const struct vt *vt, int blanked)
{
struct con_struct *vcd = vt->vcd;
clear_selection ();
vcd_removecursors (vt);
old_origin = vt->vcd->screen.origin;
 
if (blanked)
vtdata.blanked = NULL;
 
memcpy (vcd->buffer.buffer, vtdata.buffer.buffer + vtdata.buffer.origin,
vtdata.buffer.totsize << 2);
vcd->buffer.pos -= vtdata.buffer.origin;
vcd->driver = buffer_driver;
#ifdef DEBUG
vcd_validate (vt->vcd, "vcd_savestate");
#endif
}
 
void vcd_restorestate (const struct vt *vt)
{
struct con_struct *vcd = vt->vcd;
int text_mode;
/*
* Reset the origin on this VT to be the same as the previous.
* This way we don't get any jumps when we change VT's.
*/
text_mode = vt->vtd->vc_mode == KD_TEXT ? 1 : 0;
memcpy (vtdata.buffer.buffer, vcd->buffer.buffer, vtdata.buffer.totsize << 2);
vtdata.buffer.origin = 0;
if (text_mode) {
vt->vcd->screen.origin = old_origin;
set_scrmem (vt, 0);
} else
vt->vcd->screen.origin = SCREEN2_BASE;
vcd->driver = screen_driver;
gotoxy(vt->vcd, vt->vcd->curstate.x, vt->vcd->curstate.y);
#ifdef DEBUG
vcd_validate (vt->vcd, "vcd_restorestate");
#endif
set_origin(vt);
set_cursor(vt);
if (text_mode)
vcd_restorecursors (vt);
set_leds ();
}
 
void vcd_setup_graphics (const struct vt *vt)
{
clear_selection ();
vcd_removecursors (vt);
__set_origin (vtdata.screen.memstart);
}
 
/*===============================================================================================*/
 
static int vcd_write_utf (struct con_struct *vcd, int c)
{
/* Combine UTF-8 into Unicode */
/* Incomplete characters silently ignored */
if (c < 0x80) {
vcd->utf_count = 0;
return c;
}
 
if (vcd->utf_count > 0 && (c & 0xc0) == 0x80) {
vcd->utf_char = (vcd->utf_char << 6) | (c & 0x3f);
if (--vcd->utf_count == 0)
return vcd->utf_char;
else
return -1;
} else {
unsigned int count, chr;
if ((c & 0xe0) == 0xc0) {
count = 1;
chr = (c & 0x1f);
} else if ((c & 0xf0) == 0xe0) {
count = 2;
chr = (c & 0x0f);
} else if ((c & 0xf8) == 0xf0) {
count = 3;
chr = (c & 0x07);
} else if ((c & 0xfc) == 0xf8) {
count = 4;
chr = (c & 0x03);
} else if ((c & 0xfe) == 0xfc) {
count = 5;
chr = (c & 0x01);
} else {
count = 0;
chr = 0;
}
vcd->utf_count = count;
vcd->utf_char = chr;
return -1;
}
}
 
static int vcd_write_ctrl (const struct vt *vt, unsigned int c)
{
struct con_struct *vcd = vt->vcd;
/*
* Control characters can be used in the _middle_
* of an escape sequence.
*/
switch (c) {
case 0:
return 0;
case 7:
if (vcd->bell_duration)
vt_mksound (vcd->bell_pitch, 72, vcd->bell_duration);
return 0;
case 8:
if (vcd->need_wrap)
vcd->need_wrap = 0;
else if (vcd->curstate.x) {
vcd->curstate.x --;
if (vtdata.fgconsole == vt)
vcd->screen.pos -= vtdata.screen.bytespercharh;
vcd->buffer.pos -= 1;
vcd->need_wrap = 0;
}
return 0;
case 9:
if (vtdata.fgconsole == vt)
vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos -= vcd->curstate.x;
while (vcd->curstate.x < vtdata.numcolumns - 1) {
vcd->curstate.x++;
if (vcd->tab_stop[vcd->curstate.x >> 5] & (1 << (vcd->curstate.x & 31)))
break;
}
if (vtdata.fgconsole == vt)
vcd->screen.pos += vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos += vcd->curstate.x;
return 0;
case 10:
case 11:
case 12:
if (vcd->curstate.y + 1 == vcd->bottom)
vcd->driver.scroll_up (vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y < vtdata.numrows - 1) {
vcd->curstate.y ++;
if (vtdata.fgconsole == vt)
vcd->screen.pos += vtdata.screen.sizerow;
vcd->buffer.pos += vtdata.buffer.sizerow;
}
vcd->need_wrap = 0;
if (!is_kbd(lnm))
return 0;
case 13:
if (vtdata.fgconsole == vt)
vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos -= vcd->curstate.x;
vcd->curstate.x = vcd->need_wrap = 0;
return 0;
case 14:
vcd->curstate.flags |= FLG_CHRSET;
vcd->disp_ctrl = 1;
vcd->translate = set_translate(vcd->curstate.G1_charset);
return 1;
case 15:
vcd->curstate.flags &= ~FLG_CHRSET;
vcd->disp_ctrl = 0;
vcd->translate = set_translate(vcd->curstate.G0_charset);
return 1;
case 24:
case 26:
vcd->state = ESnormal;
return 0;
case 27:
vcd->state = ESesc;
return 0;
case 127:
/* ignored */
return 0;
case 128+27:
vcd->state = ESsquare;
return 0;
}
 
switch(vcd->state) {
case ESesc:
vcd->state = ESnormal;
switch (c) {
case '[':
vcd->state = ESsquare;
break;
case ']':
vcd->state = ESnonstd;
break;
case '%':
vcd->state = ESpercent;
break;
case 'E':
if (vtdata.fgconsole == vt)
vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos -= vcd->curstate.x;
vcd->curstate.x = vcd->need_wrap = 0;
case 'D':
if (vcd->curstate.y + 1 == vcd->bottom)
vcd->driver.scroll_up (vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y < vtdata.numrows - 1) {
vcd->curstate.y ++;
if (vtdata.fgconsole == vt)
vcd->screen.pos += vtdata.screen.sizerow;
vcd->buffer.pos += vtdata.buffer.sizerow;
}
/* vcd->need_wrap = 0; why should we reset this when the x position is the same? */
break;
case 'M':
if (vcd->curstate.y == vcd->top)
vcd->driver.scroll_down (vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y > 0) {
vcd->curstate.y --;
if (vtdata.fgconsole == vt)
vcd->screen.pos -= vtdata.screen.sizerow;
vcd->buffer.pos -= vtdata.buffer.sizerow;
}
/* vcd->need_wrap = 0; why should we reset this when the x position is the same? */
break;
case 'H':
vcd->tab_stop[vcd->curstate.x >> 5] |= (1 << (vcd->curstate.x & 31));
break;
case 'Z':
respond_ID (*vt->tty);
break;
case '7':
vcd->savedstate = vcd->curstate;
break;
case '8':
vcd->curstate = vcd->savedstate;
gotoxy(vt->vcd, vt->vcd->curstate.x, vt->vcd->curstate.y);
update_attr (vt);
return 1;
case '(':
vcd->state = ESsetG0;
break;
case ')':
vcd->state = ESsetG1;
break;
case '#':
vcd->state = EShash;
break;
case 'c':
reset_terminal (vt, 0);
break;
case '>': /* Numeric keypad */
clr_kbd (kbdapplic);
break;
case '=': /* Appl. keypad */
set_kbd (kbdapplic);
break;
}
return 0;
case ESnonstd:
vcd->state = ESnormal;
switch (c) {
case 'P': /* palette escape sequence */
for (vcd->npar = 0; vcd->npar < NPAR; vcd->npar++)
vcd->par[vcd->npar] = 0;
vcd->npar = 0 ;
vcd->state = ESpalette;
return 0;
case 'R': /* reset palette */
con_reset_palette (vt);
default:
return 0;
}
case ESpalette:
if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) {
vcd->par[vcd->npar++] = (c > '9' ? (c & 0xDF) - 'A' + 10 : c - '0');
if (vcd->npar == 7) {
#if TODO
int i = par[0]*3, j = 1;
palette[i] = 16*par[j++];
palette[i++] += par[j++];
palette[i] = 16*par[j++];
palette[i++] += par[j++];
palette[i] = 16*par[j++];
palette[i++] += par[j];
#endif
con_set_palette (vt);
vcd->state = ESnormal;
}
} else
vcd->state = ESnormal;
return 0;
case ESsquare:
for (vcd->npar = 0; vcd->npar < NPAR; vcd->npar++)
vcd->par[vcd->npar] = 0;
vcd->npar = 0;
vcd->state = ESgetpars;
if (c == '[') { /* Function key */
vcd->state = ESfunckey;
return 0;
}
 
vcd->ques = (c == '?');
if (vcd->ques)
return 0;
case ESgetpars:
if (c == ';' && vcd->npar < NPAR - 1) {
vcd->npar ++;
return 0;
} else
if (c >= '0' && c <= '9') {
vcd->par[vcd->npar] = vcd->par[vcd->npar] * 10 + c - '0';
return 0;
} else
vcd->state = ESgotpars;
case ESgotpars:
vcd->state=ESnormal;
switch (c) {
case 'h':
set_mode(vt, 1);
return 0;
case 'l':
set_mode(vt, 0);
return 0;
case 'n':
if (!vcd->ques) {
if (vcd->par[0] == 5)
status_report (*vt->tty);
else
if (vcd->par[0] == 6)
cursor_report (vt);
}
return 0;
}
if (vcd->ques) {
vcd->ques = 0;
return 0;
}
switch(c) {
case 'G':
case '`':
if (vcd->par[0])
vcd->par[0]--;
gotoxy (vcd, vcd->par[0], vcd->curstate.y);
return 0;
case 'A':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, vcd->curstate.x, vcd->curstate.y - vcd->par[0]);
return 0;
case 'B':
case 'e':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, vcd->curstate.x, vcd->curstate.y + vcd->par[0]);
return 0;
case 'C':
case 'a':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, vcd->curstate.x + vcd->par[0], vcd->curstate.y);
return 0;
case 'D':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, vcd->curstate.x - vcd->par[0], vcd->curstate.y);
return 0;
case 'E':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, 0, vcd->curstate.y + vcd->par[0]);
return 0;
case 'F':
if (!vcd->par[0])
vcd->par[0]++;
gotoxy (vcd, 0, vcd->curstate.y - vcd->par[0]);
return 0;
case 'd':
if (vcd->par[0])
vcd->par[0]--;
gotoxay (vcd, vcd->curstate.x, vcd->par[0]);
return 0;
case 'H':
case 'f':
if (vcd->par[0])
vcd->par[0]--;
if (vcd->par[1])
vcd->par[1]--;
gotoxay (vcd, vcd->par[1], vcd->par[0]);
return 0;
case 'J':
csi_J (vt, vcd->par[0]);
return 0;
case 'K':
csi_K (vt, vcd->par[0]);
return 0;
case 'L':
csi_L (vt, vcd->par[0]);
return 0;
case 'M':
csi_M (vt, vcd->par[0]);
return 0;
case 'P':
csi_P (vt, vcd->par[0]);
return 0;
case 'c':
if (!vcd->par[0])
respond_ID(*vt->tty);
return 0;
case 'g':
if (!vcd->par[0])
vcd->tab_stop[vcd->curstate.x >> 5] &= ~(1 << (vcd->curstate.x & 31));
else
if (vcd->par[0] == 3) {
vcd->tab_stop[0] =
vcd->tab_stop[1] =
vcd->tab_stop[2] =
vcd->tab_stop[3] =
vcd->tab_stop[4] = 0;
}
return 0;
case 'm':
return csi_m (vt);
case 'q': /* DECLL - but only 3 leds */
/* map 0,1,2,3 to 0,1,2,4 */
if (vcd->par[0] < 4)
setledstate(vt->kbd, (vcd->par[0] < 3 ? vcd->par[0] : 4));
return 0;
case 'r':
if (!vcd->par[0])
vcd->par[0]++;
if (!vcd->par[1])
vcd->par[1] = vtdata.numrows;
if (vcd->par[0] < vcd->par[1] && vcd->par[1] <= vtdata.numrows) {
vcd->top = vcd->par[0] - 1;
vcd->bottom = vcd->par[1];
gotoxay (vcd, 0, 0);
}
return 0;
case 's':
vcd->savedstate = vcd->curstate;
return 0;
case 'u':
vcd->curstate = vcd->savedstate;
update_attr (vt);
return 1;
case 'X':
csi_X (vt, vcd->par[0]);
return 0;
case '@':
csi_at (vt, vcd->par[0]);
return 0;
case ']': /* setterm functions */
setterm_command (vt);
return 0;
}
return 0;
case ESpercent:
vcd->state = ESnormal;
switch (c) {
case '@': /* defined in ISO 2022 */
vcd->utf = 0;
return 0;
case 'G': /* prelim official escape code */
case '8': /* retained for compatibility */
vcd->utf = 1;
return 0;
}
return 0;
case ESfunckey:
vcd->state = ESnormal;
return 0;
case EShash:
vcd->state = ESnormal;
if (c == '8') {
/* DEC screen alignment test. kludge :-) */
}
return 0;
case ESsetG0:
vcd->state = ESnormal;
if (c == '0')
vcd->curstate.G0_charset = GRAF_MAP;
else
if (c == 'B')
vcd->curstate.G0_charset = LAT1_MAP;
else
if (c == 'U')
vcd->curstate.G0_charset = IBMPC_MAP;
else
if (c == 'K')
vcd->curstate.G0_charset = USER_MAP;
if ((vcd->curstate.flags & FLG_CHRSET) == 0) {
vcd->translate = set_translate(vcd->curstate.G0_charset);
return 1;
}
return 0;
case ESsetG1:
vcd->state = ESnormal;
if (c == '0')
vcd->curstate.G1_charset = GRAF_MAP;
else
if (c == 'B')
vcd->curstate.G1_charset = LAT1_MAP;
else
if (c == 'U')
vcd->curstate.G1_charset = IBMPC_MAP;
else
if (c == 'K')
vcd->curstate.G1_charset = USER_MAP;
if (vcd->curstate.flags & FLG_CHRSET) {
vcd->translate = set_translate(vcd->curstate.G1_charset);
return 1;
}
return 0;
default:
vcd->state = ESnormal;
}
return 0;
}
 
static inline void vcd_write_char (struct con_struct *vcd, unsigned int c)
{
if (c & ~console_charmask)
return;
vcd->driver.write_char (vcd, vcd->combined_state | (c & 255));
}
 
int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)
{
int strt_count = count;
unsigned short *cached_trans;
unsigned int cached_ctrls;
register struct con_struct *vcd;
 
if (from_user && get_fs () == KERNEL_DS)
from_user = 0;
 
vcd = vt->vcd;
 
#ifdef DEBUG
vcd_validate (vcd, "vcd_write entry");
#endif
 
vcd_removecursors (vt);
if (vt == vtdata.select.vt)
clear_selection();
 
disable_bh (CONSOLE_BH);
recache:
cached_ctrls = vcd->disp_ctrl ? CTRL_ALWAYS : CTRL_ACTION;
cached_trans = vcd->translate + (vcd->toggle_meta ? 0x80 : 0);
 
while (!(*vt->tty)->stopped && count) {
int tc, c;
enable_bh(CONSOLE_BH);
__asm__("teq %3, #0
ldreqb %0, [%1], #1
ldrnebt %0, [%1], #1" : "=r" (c), "=&r" (buf) : "1" (buf), "r" (from_user));
disable_bh(CONSOLE_BH);
count --;
 
if (vcd->utf) {
if ((tc = vcd_write_utf (vcd, c)) < 0)
continue;
c = tc;
} else
tc = cached_trans[c];
 
if (vcd->state == ESnormal && tc && (c != 127 || vcd->disp_ctrl) && (c != 128+27)) {
if (c >= 32 || (!vcd->utf && !(cached_ctrls & (1 << c)))) { /* ok */
tc = conv_uni_to_pc (tc);
if (tc == -4)
/* If we got -4 (not found) then see if we have
defined a replacement character (U+FFFD) */
tc = conv_uni_to_pc (0xfffd);
else if (tc == -3)
/* Bad hash table -- hope for the best */
tc = c;
 
vcd_write_char (vcd, tc);
continue;
}
}
 
if (vcd_write_ctrl (vt, c))
goto recache;
else
continue;
}
if (vt->vtd->vc_mode != KD_GRAPHICS)
set_cursor (vt);
vcd_restorecursors (vt);
enable_bh(CONSOLE_BH);
#ifdef DEBUG
vcd_validate (vcd, "vcd_write exit");
#endif
return strt_count - count;
}
 
int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)
{
int i;
switch (cmd) {
case VT_GETPALETTE: {
unsigned long pix, num, *ptr;
const unsigned long *entries;
 
ptr = (unsigned long *)arg;
 
if ((i = verify_area (VERIFY_READ, ptr, 12)) != 0)
return i;
 
pix = get_user (ptr);
num = get_user (ptr + 1);
if (!num)
return 0;
if (pix > 255 || pix + num > 256)
return -EINVAL;
 
ptr = (unsigned long *) get_user (ptr + 2);
if ((i = verify_area (VERIFY_WRITE, ptr, num * sizeof (unsigned long))) != 0)
return i;
 
if (vt->vcd->screen.palette_entries)
entries = vt->vcd->screen.palette_entries + pix;
else
entries = default_palette_entries + pix;
memcpy_tofs(ptr, entries, num * sizeof (unsigned long));
return 0;
}
case VT_SETPALETTE: {
unsigned long pix, num, *ptr, *sval;
 
ptr = (unsigned long *)arg;
 
if ((i = verify_area (VERIFY_READ, ptr, 12)) != 0)
return i;
 
pix = get_user (ptr);
num = get_user (ptr + 1);
if (!num)
return 0;
if (pix > 255 || pix + num > 256)
return -EINVAL;
 
ptr = (unsigned long *) get_user (ptr + 2);
if ((i = verify_area (VERIFY_READ, ptr, num * sizeof (unsigned long))) != 0)
return i;
 
if (!vt->vcd->screen.palette_entries) {
void *entries;
entries = kmalloc (sizeof (unsigned long) * 256, GFP_KERNEL);
if (!vt->vcd->screen.palette_entries) {
if (!entries)
return -ENOMEM;
memcpy (entries, default_palette_entries, 256 * sizeof (unsigned long));
vt->vcd->screen.palette_entries = entries;
}
}
sval = vt->vcd->screen.palette_entries + pix;
memcpy_fromfs (sval, ptr, num * sizeof (unsigned long));
palette_setpixel(pix);
for (i = num; i > 3; i -= 4) {
palette_write (*sval++);
palette_write (*sval++);
palette_write (*sval++);
palette_write (*sval++);
}
if (i & 2) {
palette_write (*sval++);
palette_write (*sval++);
}
if (i & 1)
palette_write (*sval++);
return 0;
}
 
case PIO_FONT:
if (vt->vtd->vc_mode != KD_TEXT)
return -EINVAL;
return con_set_font((char *)arg);
/* con_set_font() defined in console.c */
 
case GIO_FONT:
if (vt->vtd->vc_mode != KD_TEXT)
return -EINVAL;
return con_get_font((char *)arg);
/* con_get_font() defined in console.c */
 
case PIO_SCRNMAP:
return con_set_trans_old ((char *)arg);
 
case GIO_SCRNMAP:
return con_get_trans_old((char *)arg);
 
case PIO_UNISCRNMAP:
return con_set_trans_new((short *)arg);
 
case GIO_UNISCRNMAP:
return con_get_trans_new((short *)arg);
 
case PIO_UNIMAPCLR: {
struct unimapinit ui;
 
i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapinit));
if (i)
return i;
memcpy_fromfs(&ui, (void *)arg, sizeof(struct unimapinit));
con_clear_unimap(&ui);
return 0;
}
 
case PIO_UNIMAP: {
i = verify_area(VERIFY_READ, (void *)arg, sizeof(struct unimapdesc));
if (i == 0) {
struct unimapdesc *ud;
u_short ct;
struct unipair *list;
 
ud = (struct unimapdesc *) arg;
ct = get_fs_word(&ud->entry_ct);
list = (struct unipair *) get_fs_long(&ud->entries);
 
i = verify_area(VERIFY_READ, (void *) list,
ct*sizeof(struct unipair));
if (!i)
return con_set_unimap(ct, list);
}
return i;
}
 
case GIO_UNIMAP: {
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct unimapdesc));
if (i == 0) {
struct unimapdesc *ud;
u_short ct;
struct unipair *list;
 
ud = (struct unimapdesc *) arg;
ct = get_fs_word(&ud->entry_ct);
list = (struct unipair *) get_fs_long(&ud->entries);
if (ct)
i = verify_area(VERIFY_WRITE, (void *) list,
ct*sizeof(struct unipair));
if (!i)
return con_get_unimap(ct, &(ud->entry_ct), list);
}
return i;
}
 
default:
return -ENOIOCTLCMD;
}
}
 
void console_print(const char *b)
{
static int printing = 0;
struct vt *vt = vtdata.fgconsole;
struct con_struct * const vcd = vt->vcd;
unsigned char c;
 
#ifdef DEBUG
vcd_validate (vcd, "console_print entry");
#endif
 
if (!printable || printing || vt->vtd->vc_mode == KD_GRAPHICS)
return; /* console not yet initialized */
printing = 1;
 
if (!vt_allocated(vtdata.fgconsole)) {
/* impossible */
printk ("console_print: tty %d not allocated ??\n", vtdata.fgconsole->num);
printing = 0;
return;
}
 
#ifdef CONFIG_SERIAL_ECHO
serial_echo_print (b);
#endif
vcd_removecursors (vt);
while ((c = *b++) != 0)
screen_driver.put_char(vcd, vcd->combined_state | (c & 255));
set_cursor (vt);
vcd_restorecursors (vt);
 
vt_pokeblankedconsole ();
printing = 0;
#ifdef DEBUG
vcd_validate (vt->vcd, "console_print exit");
#endif
}
 
/*===============================================================================================*/
 
int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)
{
struct con_struct *vcd = vt->vcd;
memset (vcd, 0, sizeof (*vcd));
vcd->screen.origin = vtdata.screen.memstart;
vcd->screen.cursoron = (kmem ? 1 : 0);
vcd->screen.palette_entries = NULL;
vcd->driver = buffer_driver;
if (kmallocok) {
vcd->buffer.buffer = vmalloc (vtdata.buffer.totsize * sizeof (unsigned long));
if (!vt->vcd->buffer.buffer)
return -ENOMEM;
vcd->buffer.kmalloced = 1;
} else {
vcd->buffer.buffer = (unsigned long *) *kmem;
*kmem += vtdata.buffer.totsize * sizeof (unsigned long);
}
 
vt->vtd->paste_wait = NULL;
reset_terminal (vt, (kmem ? 1 : 0));
return 0;
}
 
/*
* We allow this irq to be shared
*/
static struct irqaction vsyncirq = { vsync_irq, SA_SHIRQ, 0, "vsync", NULL, NULL };
unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)
{
int colours, i;
 
switch (bytes_per_char_h) {
default:
case 1:
default_palette_entries = palette_1;
vtdata.screen.bytespercharh = 1;
vtdata.screen.bitsperpix = 1;
color_table = color_1;
colours = 1;
break;
case 4:
default_palette_entries = palette_4;
vtdata.screen.bytespercharh = 4;
vtdata.screen.bitsperpix = 4;
color_table = color_4;
colours = 16;
for (i = 0; i < 256; i++)
con_charconvtable[i] =
(i & 128 ? 1 << 0 : 0) |
(i & 64 ? 1 << 4 : 0) |
(i & 32 ? 1 << 8 : 0) |
(i & 16 ? 1 << 12 : 0) |
(i & 8 ? 1 << 16 : 0) |
(i & 4 ? 1 << 20 : 0) |
(i & 2 ? 1 << 24 : 0) |
(i & 1 ? 1 << 28 : 0);
break;
case 8:
default_palette_entries = palette_8;
vtdata.screen.bytespercharh = 8;
vtdata.screen.bitsperpix = 8;
color_table = color_8;
colours = 256;
for (i = 0; i < 16; i++)
con_charconvtable[i] =
(i & 8 ? 1 << 0 : 0) |
(i & 4 ? 1 << 8 : 0) |
(i & 2 ? 1 << 16 : 0) |
(i & 1 ? 1 << 24 : 0);
break;
}
video_size_row = vtdata.numcolumns * vtdata.screen.bytespercharh;
vtdata.screen.bytespercharv = bytes_per_char_v;
vtdata.screen.sizerow = video_size_row * vtdata.screen.bytespercharv;
vtdata.screen.totsize = vtdata.screen.sizerow * vtdata.numrows;
 
vtdata.screen.memsize = ((vtdata.screen.totsize - 1) | (PAGE_SIZE - 1)) + 1;
vtdata.screen.memend = SCREEN1_END;
vtdata.screen.memstart = vtdata.screen.memend - vtdata.screen.memsize;
vtdata.buffer.buffer = (unsigned long *)kmem;
vtdata.buffer.sizerow = vtdata.numcolumns;
vtdata.buffer.totsize = vtdata.numcolumns * vtdata.numrows;
vtdata.buffer.lastorigin = vtdata.buffer.totsize * 2;
kmem = (unsigned long)(vtdata.buffer.buffer + vtdata.buffer.totsize * 3);
memzero (vtdata.buffer.buffer, vtdata.buffer.totsize * 3 << 2);
 
kmem = map_screen_mem (vtdata.screen.memstart, kmem, 1);
 
palette_setpixel(0);
for (i = 0; i < MAX_PIX; i++)
palette_write(default_palette_entries[i]);
 
vcd_init (vt, 0, &kmem);
vt->vcd->driver = screen_driver;
 
gotoxy (vt->vcd, ORIG_X, ORIG_Y);
set_origin (vt);
csi_J (vt, 0);
printable = 1;
 
#ifdef CONFIG_SERIAL_ECHO
serial_echo_init (SERIAL_ECHO_PORT);
#endif
printk ("Console: %s %s %dx%dx%d, %d virtual console%s (max %d)\n",
colours != 1 ? "colour" : "mono",
"A-series",
vtdata.numcolumns, vtdata.numrows, colours,
MIN_NR_CONSOLES,
(MIN_NR_CONSOLES == 1) ? "":"s",
MAX_NR_CONSOLES);
 
register_console (console_print);
 
if (setup_arm_irq(IRQ_VSYNCPULSE, &vsyncirq))
panic ("Unable to get VSYNC irq for console\n");
 
return kmem;
}
 
/*
* Report the current status of the vc. This is exported to modules (ARub)
*/
int con_get_info(int *mode, int *shift, int *col, int *row,
struct tty_struct **tty)
{
extern int shift_state;
 
if (mode) *mode = vtdata.fgconsole->vtd->vc_mode;
if (shift) *shift = shift_state;
if (col) *col = vtdata.numcolumns;
if (row) *row = vtdata.numrows;
if (tty) *tty = *vtdata.fgconsole->tty;
return vtdata.fgconsole->num;
}
/serial-dualsp.c
0,0 → 1,18
/*
* linux/arch/arm/drivers/char/serial-dualsp.c
*
* Copyright (c) 1996 Russell King.
*
* Changelog:
* 30-07-1996 RMK Created
*/
#define MY_CARD_LIST { MANU_SERPORT, PROD_SERPORT_DSPORT }
#define MY_NUMPORTS 2
#define MY_BAUD_BASE (3686400 / 16)
#define MY_INIT dualsp_serial_init
#define MY_BASE_ADDRESS(ec) \
ecard_address (ec, ECARD_IOC, ECARD_SLOW) + (0x2000 >> 2)
#define MY_PORT_ADDRESS(port,cardaddress) \
((cardaddress) + (port) * 8)
#include "serial-card.c"
 
/defkeymap.c
0,0 → 1,358
/*
* linux/arch/arm/drivers/char/defkeymap.c
*
* Copyright (C) 1995, 1996 Russell King
*/
 
#include <linux/types.h>
#include <linux/keyboard.h>
#include <linux/kd.h>
 
/* Normal (maps 1:1 with no processing) */
#define KTn 0xF0
/* Function keys */
#define KTf 0xF1
/* Special (Performs special house-keeping funcs) */
#define KTs 0xF2
#define KIGNORE K(KTs, 0) /* Ignore */
#define KENTER K(KTs, 1) /* Enter */
#define KREGS K(KTs, 2) /* Regs */
#define KMEM K(KTs, 3) /* Mem */
#define KSTAT K(KTs, 4) /* State */
#define KINTR K(KTs, 5) /* Intr */
#define Ksl 6 /* Last console */
#define KCAPSLK K(KTs, 7) /* Caps lock */
#define KNUMLK K(KTs, 8) /* Num-lock */
#define KSCRLLK K(KTs, 9) /* Scroll-lock */
#define KSCRLFOR K(KTs,10) /* Scroll forward */
#define KSCRLBAK K(KTs,11) /* Scroll back */
#define KREBOOT K(KTs,12) /* Reboot */
#define KCAPSON K(KTs,13) /* Caps on */
#define KCOMPOSE K(KTs,14) /* Compose */
#define KSAK K(KTs,15) /* SAK */
#define CONS_DEC K(KTs,16) /* Dec console */
#define CONS_INC K(KTs,17) /* Incr console */
#define KFLOPPY K(KTs,18) /* Floppy */
/* Key pad (0-9 = digits, 10=+, 11=-, 12=*, 13=/, 14=enter, 16=., 17=# */
#define KTp 0xF3
#define KPAD_0 K(KTp, 0 )
#define KPAD_1 K(KTp, 1 )
#define KPAD_2 K(KTp, 2 )
#define KPAD_3 K(KTp, 3 )
#define KPAD_4 K(KTp, 4 )
#define KPAD_5 K(KTp, 5 )
#define KPAD_6 K(KTp, 6 )
#define KPAD_7 K(KTp, 7 )
#define KPAD_8 K(KTp, 8 )
#define KPAD_9 K(KTp, 9 )
#define KPAD_PL K(KTp,10 )
#define KPAD_MI K(KTp,11 )
#define KPAD_ML K(KTp,12 )
#define KPAD_DV K(KTp,13 )
#define KPAD_EN K(KTp,14 )
#define KPAD_DT K(KTp,16 )
#define KPAD_HS K(KTp,18 )
/* Console switching */
#define KCn 0xF5
/* Cursor */
#define KTc 0xF6
#define Kcd 0 /* Cursor down */
#define Kcl 1 /* Cursor left */
#define Kcr 2 /* Cursor right */
#define Kcu 3 /* Cursor up */
/* Shift/alt modifiers etc */
#define KMd 0xF7
#define KSHIFT K(KMd, 0 )
#define KALTGR K(KMd, 1 )
#define KCTRL K(KMd, 2 )
#define KALT K(KMd, 3 )
/* Meta */
#define KMt 0xF8
#define KAs 0xF9
#define KPADA_0 K(KAs, 0 )
#define KPADA_1 K(KAs, 1 )
#define KPADA_2 K(KAs, 2 )
#define KPADA_3 K(KAs, 3 )
#define KPADA_4 K(KAs, 4 )
#define KPADA_5 K(KAs, 5 )
#define KPADA_6 K(KAs, 6 )
#define KPADA_7 K(KAs, 7 )
#define KPADA_8 K(KAs, 8 )
#define KPADA_9 K(KAs, 9 )
#define KPADB_0 K(KAs,10 )
#define KPADB_1 K(KAs,11 )
#define KPADB_2 K(KAs,12 )
#define KPADB_3 K(KAs,13 )
#define KPADB_4 K(KAs,14 )
#define KPADB_5 K(KAs,15 )
#define KPADB_6 K(KAs,16 )
#define KPADB_7 K(KAs,17 )
#define KPADB_8 K(KAs,18 )
#define KPADB_9 K(KAs,19 )
/* Locking keys */
#define KLk 0xFA
/* Letters */
#define KTl 0xFB
 
u_short plain_map[NR_KEYS]=
{
K(KTn, 27),K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSCRLLK ,KINTR ,
K(KTn,'`'),K(KTn,'1'),K(KTn,'2'),K(KTn,'3' ),K(KTn,'4'),K(KTn,'5'),K(KTn,'6' ),K(KTn,'7'),
 
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'q'),
K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
K(KTl,'p'),K(KTn,'['),K(KTn,']'),K(KTn,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'a'),K(KTl,'s'),K(KTl,'d' ),K(KTl,'f'),
K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),K(KTn,';'),K(KTn,'\''),KENTER ,
KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
K(KTl,'c'),K(KTl,'v'),K(KTl,'b'),K(KTl,'n' ),K(KTl,'m'),K(KTn,','),K(KTn,'.' ),K(KTn,'/'),
KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short shift_map[NR_KEYS]=
{
K(KTn, 27),K(KTf, 10),K(KTf, 11),K(KTf, 12 ),K(KTf, 13),K(KTf, 14),K(KTf, 15 ),K(KTf, 16),
K(KTf, 17),K(KTf, 18),K(KTf, 19),K(KTf, 20 ),K(KTf, 21),KIGNORE ,KMEM ,KINTR ,
K(KTn,'~'),K(KTn,'!'),K(KTn,'@'),K(KTn,'#' ),K(KTn,'$'),K(KTn,'%'),K(KTn,'^' ),K(KTn,'&'),
 
K(KTf,20 ),KSCRLBAK ,KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KTn, 9 ),K(KTl,'Q'),
K(KTl,'W'),K(KTl,'E'),K(KTl,'R'),K(KTl,'T' ),K(KTl,'Y'),K(KTl,'U'),K(KTl,'I' ),K(KTl,'O'),
K(KTl,'P'),K(KTn,'{'),K(KTn,'}'),K(KTn,'|' ),K(KTf,22 ),K(KTf,23 ),KSCRLFOR ,KPAD_7 ,
KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTl,'A'),K(KTl,'S'),K(KTl,'D' ),K(KTl,'F'),
K(KTl,'G'),K(KTl,'H'),K(KTl,'J'),K(KTl,'K' ),K(KTl,'L'),K(KTn,':'),K(KTn,'"' ),KENTER ,
KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'Z' ),K(KTl,'X'),
K(KTl,'C'),K(KTl,'V'),K(KTl,'B'),K(KTl,'N' ),K(KTl,'M'),K(KTn,'<'),K(KTn,'>' ),K(KTn,'?'),
KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn,' '),
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short altgr_map[NR_KEYS]=
{
KIGNORE ,K(KCn,12 ),K(KCn,13 ),K(KCn,14 ),K(KCn,15 ),K(KCn,16 ),K(KCn,17 ),K(KCn, 18),
K(KCn, 19),K(KCn,20 ),K(KCn,21 ),K(KCn,22 ),K(KCn,23 ),KIGNORE ,KREGS ,KINTR ,
KIGNORE ,KIGNORE ,K(KTn,'@'),KIGNORE ,K(KTn,'$'),KIGNORE ,KIGNORE ,K(KTn,'{'),
K(KTn,'['),K(KTn,']'),K(KTn,'}'),K(KTn,'\\'),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTl,'q'),
K(KTl,'w'),K(KTl,'e'),K(KTl,'r'),K(KTl,'t' ),K(KTl,'y'),K(KTl,'u'),K(KTl,'i' ),K(KTl,'o'),
K(KTl,'p'),KIGNORE ,K(KTn,'~'),KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADB_7 ,
KPADB_8 ,KPADB_9 ,KPAD_MI ,KCTRL ,K(KAs,20 ),K(KTl,'s'),K(KAs,23 ),K(KAs,25 ),
K(KTl,'g'),K(KTl,'h'),K(KTl,'j'),K(KTl,'k' ),K(KTl,'l'),KIGNORE ,KIGNORE ,KENTER ,
KPADB_4 ,KPADB_5 ,KPADB_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTl,'z' ),K(KTl,'x'),
K(KAs,22 ),K(KTl,'v'),K(KTl,21 ),K(KTl,'n' ),K(KTl,'m'),KIGNORE ,KIGNORE ,KIGNORE ,
KSHIFT ,K(KTc,Kcu),KPADB_1 ,KPADB_2 ,KPADB_3 ,KCAPSLK ,KALT ,KIGNORE ,
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPADB_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short ctrl_map[NR_KEYS]=
{
KIGNORE ,K(KTf, 0),K(KTf, 1),K(KTf, 2 ),K(KTf, 3),K(KTf, 4),K(KTf, 5 ),K(KTf, 6),
K(KTf, 7),K(KTf, 8),K(KTf, 9),K(KTf, 10 ),K(KTf, 11),KIGNORE ,KSTAT ,KINTR ,
KIGNORE ,K(KTn, 1 ),K(KTn, 2 ),K(KTn, 3 ),K(KTn, 4 ),K(KTn, 5 ),K(KTn, 6 ),K(KTn, 7 ),
K(KTn, 8 ),K(KTn, 9 ),K(KTn, 0 ),K(KTn,31 ),KIGNORE ,KIGNORE ,K(KTn, 8 ),K(KTf,21 ),
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
K(KTn,16 ),K(KTn,27 ),K(KTn,29 ),K(KTn,28 ),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KCOMPOSE ,K(KTn,127),
KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short shift_ctrl_map[NR_KEYS]=
{
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KFLOPPY ,KINTR ,
KIGNORE ,KIGNORE ,K(KTn, 0 ),KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,K(KTn,31 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KTn,17 ),
K(KTn,23 ),K(KTn, 5 ),K(KTn,18 ),K(KTn,20 ),K(KTn,25 ),K(KTn,21 ),K(KTn, 9 ),K(KTn,15 ),
K(KTn,16 ),KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KTn, 1 ),K(KTn,19 ),K(KTn, 4 ),K(KTn, 6 ),
K(KTn, 7 ),K(KTn, 8 ),K(KTn,10 ),K(KTn,11 ),K(KTn,12 ),KIGNORE ,K(KTn, 7 ),KENTER ,
KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KTn,26 ),K(KTn,24 ),
K(KTn, 3 ),K(KTn,22 ),K(KTn, 2 ),K(KTn,14 ),K(KTn,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,K(KTn, 0 ),
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short alt_map[NR_KEYS]=
{
K(KMt,27 ),K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KSCRLLK ,KINTR ,
K(KMt,'`'),K(KMt,'1'),K(KMt,'2'),K(KMt,'3' ),K(KMt,'4'),K(KMt,'5'),K(KMt,'6' ),K(KMt,'7'),
 
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,K(KMt, 9 ),K(KMt,'q'),
K(KMt,'w'),K(KMt,'e'),K(KMt,'r'),K(KMt,'t' ),K(KMt,'y'),K(KMt,'u'),K(KMt,'i' ),K(KMt,'o'),
K(KMt,'p'),K(KMt,'['),K(KMt,']'),K(KMt,'\\'),K(KTf,22 ),K(KTf,23 ),K(KTf,25 ),KPADA_7 ,
KPADA_8 ,KPADA_9 ,KPAD_MI ,KCTRL ,K(KMt,'a'),K(KMt,'s'),K(KMt,'d' ),K(KMt,'f'),
K(KMt,'g'),K(KMt,'h'),K(KMt,'j'),K(KMt,'k' ),K(KMt,'l'),K(KMt,';'),K(KMt,'\''),K(KMt,13 ),
KPADA_4 ,KPADA_5 ,KPADA_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,'z' ),K(KMt,'x'),
K(KMt,'c'),K(KMt,'v'),K(KMt,'b'),K(KMt,'n' ),K(KMt,'m'),K(KMt,','),K(KMt,'.' ),KIGNORE ,
KSHIFT ,K(KTc,Kcu),KPADA_1 ,KPADA_2 ,KPADA_3 ,KCAPSLK ,KALT ,K(KMt,' '),
KALTGR ,KCTRL ,CONS_DEC ,K(KTc,Kcd ),CONS_INC ,KPADA_0 ,KPAD_DT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
u_short ctrl_alt_map[NR_KEYS]=
{
KIGNORE ,K(KCn, 0 ),K(KCn, 1 ),K(KCn, 2 ),K(KCn, 3 ),K(KCn, 4 ),K(KCn, 5 ),K(KCn, 6 ),
K(KCn, 7 ),K(KCn, 8 ),K(KCn, 9 ),K(KCn,10 ),K(KCn,11 ),KIGNORE ,KIGNORE ,KINTR ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,K(KTf,21 ),
K(KTf,20 ),K(KTf,24 ),KNUMLK ,KPAD_DV ,KPAD_ML ,KPAD_HS ,KIGNORE ,K(KMt,17 ),
K(KMt,23 ),K(KMt, 5 ),K(KMt,18 ),K(KMt,20 ),K(KMt,25 ),K(KMt,21 ),K(KMt, 9 ),K(KMt,15 ),
K(KMt,16 ),KIGNORE ,KIGNORE ,KIGNORE ,KREBOOT ,K(KTf,23 ),K(KTf,25 ),KPAD_7 ,
KPAD_8 ,KPAD_9 ,KPAD_MI ,KCTRL ,K(KMt, 1 ),K(KMt,19 ),K(KMt, 4 ),K(KMt, 6 ),
K(KMt, 7 ),K(KMt, 8 ),K(KMt,10 ),K(KMt,11 ),K(KMt,12 ),KIGNORE ,KIGNORE ,KENTER ,
KPAD_4 ,KPAD_5 ,KPAD_6 ,KPAD_PL ,KSHIFT ,KIGNORE ,K(KMt,26 ),K(KMt,24 ),
K(KMt, 3 ),K(KMt,22 ),K(KMt, 2 ),K(KMt,14 ),K(KMt,13 ),KIGNORE ,KIGNORE ,KIGNORE ,
KSHIFT ,K(KTc,Kcu),KPAD_1 ,KPAD_2 ,KPAD_3 ,KCAPSLK ,KALT ,KIGNORE ,
KALTGR ,KCTRL ,K(KTc,Kcl),K(KTc,Kcd ),K(KTc,Kcr),KPAD_0 ,KREBOOT ,KPAD_EN ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,KIGNORE ,
};
 
ushort *key_maps[MAX_NR_KEYMAPS] = {
plain_map, shift_map, altgr_map, 0,
ctrl_map, shift_ctrl_map, 0, 0,
alt_map, 0, 0, 0,
ctrl_alt_map, 0
};
 
unsigned int keymap_count = 7;
 
/*
* Philosophy: most people do not define more strings, but they who do
* often want quite a lot of string space. So, we statically allocate
* the default and allocate dynamically in chunks of 512 bytes.
*/
 
char func_buf[] = {
'\033', '[', '[', 'A', 0,
'\033', '[', '[', 'B', 0,
'\033', '[', '[', 'C', 0,
'\033', '[', '[', 'D', 0,
'\033', '[', '[', 'E', 0,
'\033', '[', '1', '7', '~', 0,
'\033', '[', '1', '8', '~', 0,
'\033', '[', '1', '9', '~', 0,
'\033', '[', '2', '0', '~', 0,
'\033', '[', '2', '1', '~', 0,
'\033', '[', '2', '3', '~', 0,
'\033', '[', '2', '4', '~', 0,
'\033', '[', '2', '5', '~', 0,
'\033', '[', '2', '6', '~', 0,
'\033', '[', '2', '8', '~', 0,
'\033', '[', '2', '9', '~', 0,
'\033', '[', '3', '1', '~', 0,
'\033', '[', '3', '2', '~', 0,
'\033', '[', '3', '3', '~', 0,
'\033', '[', '3', '4', '~', 0,
'\033', '[', '1', '~', 0,
'\033', '[', '2', '~', 0,
'\033', '[', '3', '~', 0,
'\033', '[', '4', '~', 0,
'\033', '[', '5', '~', 0,
'\033', '[', '6', '~', 0,
'\033', '[', 'M', 0,
'\033', '[', 'P', 0,
};
 
char *funcbufptr = func_buf;
int funcbufsize = sizeof(func_buf);
int funcbufleft = 0; /* space left */
 
char *func_table[MAX_NR_FUNC] = {
func_buf + 0,
func_buf + 5,
func_buf + 10,
func_buf + 15,
func_buf + 20,
func_buf + 25,
func_buf + 31,
func_buf + 37,
func_buf + 43,
func_buf + 49,
func_buf + 55,
func_buf + 61,
func_buf + 67,
func_buf + 73,
func_buf + 79,
func_buf + 85,
func_buf + 91,
func_buf + 97,
func_buf + 103,
func_buf + 109,
func_buf + 115,
func_buf + 120,
func_buf + 125,
func_buf + 130,
func_buf + 135,
func_buf + 140,
func_buf + 145,
0,
0,
func_buf + 149,
0,
};
 
struct kbdiacr accent_table[MAX_DIACR] = {
{'`', 'A', '\300'}, {'`', 'a', '\340'},
{'\'', 'A', '\301'}, {'\'', 'a', '\341'},
{'^', 'A', '\302'}, {'^', 'a', '\342'},
{'~', 'A', '\303'}, {'~', 'a', '\343'},
{'"', 'A', '\304'}, {'"', 'a', '\344'},
{'O', 'A', '\305'}, {'o', 'a', '\345'},
{'0', 'A', '\305'}, {'0', 'a', '\345'},
{'A', 'A', '\305'}, {'a', 'a', '\345'},
{'A', 'E', '\306'}, {'a', 'e', '\346'},
{',', 'C', '\307'}, {',', 'c', '\347'},
{'`', 'E', '\310'}, {'`', 'e', '\350'},
{'\'', 'E', '\311'}, {'\'', 'e', '\351'},
{'^', 'E', '\312'}, {'^', 'e', '\352'},
{'"', 'E', '\313'}, {'"', 'e', '\353'},
{'`', 'I', '\314'}, {'`', 'i', '\354'},
{'\'', 'I', '\315'}, {'\'', 'i', '\355'},
{'^', 'I', '\316'}, {'^', 'i', '\356'},
{'"', 'I', '\317'}, {'"', 'i', '\357'},
{'-', 'D', '\320'}, {'-', 'd', '\360'},
{'~', 'N', '\321'}, {'~', 'n', '\361'},
{'`', 'O', '\322'}, {'`', 'o', '\362'},
{'\'', 'O', '\323'}, {'\'', 'o', '\363'},
{'^', 'O', '\324'}, {'^', 'o', '\364'},
{'~', 'O', '\325'}, {'~', 'o', '\365'},
{'"', 'O', '\326'}, {'"', 'o', '\366'},
{'/', 'O', '\330'}, {'/', 'o', '\370'},
{'`', 'U', '\331'}, {'`', 'u', '\371'},
{'\'', 'U', '\332'}, {'\'', 'u', '\372'},
{'^', 'U', '\333'}, {'^', 'u', '\373'},
{'"', 'U', '\334'}, {'"', 'u', '\374'},
{'\'', 'Y', '\335'}, {'\'', 'y', '\375'},
{'T', 'H', '\336'}, {'t', 'h', '\376'},
{'s', 's', '\337'}, {'"', 'y', '\377'},
{'s', 'z', '\337'}, {'i', 'j', '\377'},
};
 
unsigned int accent_table_size = 68;
/font.h
0,0 → 1,276
/*
* Font definition, with PC graphics
*/
const unsigned char cmap_80[][8]= {
/* 00 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ^@ */
/* 01 */ {0x7e, 0x81, 0xa5, 0x81, 0xbd, 0x99, 0x81, 0x7e}, /* ^A */
/* 02 */ {0x7e, 0xff, 0xbd, 0xff, 0xc3, 0xe7, 0xff, 0x7e}, /* ^B */
/* 03 */ {0x3c, 0xfe, 0xfe, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* ^C */
/* 04 */ {0x10, 0x38, 0x7c, 0xfe, 0x7c, 0x38, 0x10, 0x00}, /* ^D */
/* 05 */ {0x00, 0x18, 0x3c, 0xe7, 0xe7, 0x3c, 0x18, 0x00}, /* ^E */
/* 06 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 07 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 08 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 09 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0A */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0B */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0C */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0D */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0E */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 0F */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 
/* 10 */ {0x00, 0x60, 0x78, 0x7e, 0x7e, 0x78, 0x60, 0x00}, /* |> */
/* 11 */ {0x00, 0x06, 0x1e, 0x7e, 0x7e, 0x1e, 0x06, 0x00}, /* <| */
/* 12 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 13 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 14 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 15 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 16 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 17 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 18 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 19 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 1A */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 1B */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 1C */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 1D */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 1E */ {0x00, 0x18, 0x18, 0x3c, 0x3c, 0x7e, 0x7e, 0x00}, /* /\ */
/* 1F */ {0x00, 0x7e, 0x7e, 0x3c, 0x3c, 0x18, 0x18, 0x00}, /* \/ */
 
/* 20 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* */
/* 21 */ {0x18, 0x3c, 0x3c, 0x18, 0x18, 0x00, 0x18, 0x00}, /* ! */
/* 22 */ {0x6C, 0x6C, 0x6C, 0x00, 0x00, 0x00, 0x00, 0x00}, /* " */
/* 23 */ {0x36, 0x36, 0x7F, 0x36, 0x7F, 0x36, 0x36, 0x00}, /* # */
/* 24 */ {0x0C, 0x3F, 0x68, 0x3E, 0x0B, 0x7E, 0x18, 0x00}, /* $ */
/* 25 */ {0x60, 0x66, 0x0C, 0x18, 0x30, 0x66, 0x06, 0x00}, /* % */
/* 26 */ {0x38, 0x6C, 0x6C, 0x38, 0x6D, 0x66, 0x3B, 0x00}, /* & */
/* 27 */ {0x18, 0x18, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ' */
/* 28 */ {0x0C, 0x18, 0x30, 0x30, 0x30, 0x18, 0x0C, 0x00}, /* ( */
/* 29 */ {0x30, 0x18, 0x0C, 0x0C, 0x0C, 0x18, 0x30, 0x00}, /* ) */
/* 2A */ {0x00, 0x18, 0x7E, 0x3C, 0x7E, 0x18, 0x00, 0x00}, /* * */
/* 2B */ {0x00, 0x18, 0x18, 0x7E, 0x18, 0x18, 0x00, 0x00}, /* + */
/* 2C */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x30}, /* , */
/* 2D */ {0x00, 0x00, 0x00, 0x7E, 0x00, 0x00, 0x00, 0x00}, /* - */
/* 2E */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x18, 0x18, 0x00}, /* . */
/* 2F */ {0x00, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x00, 0x00}, /* / */
 
/* 30 */ {0x3C, 0x66, 0x6E, 0x7E, 0x76, 0x66, 0x3C, 0x00}, /* 0 */
/* 31 */ {0x18, 0x38, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00}, /* 1 */
/* 32 */ {0x3C, 0x66, 0x06, 0x0C, 0x18, 0x30, 0x7E, 0x00}, /* 2 */
/* 33 */ {0x3C, 0x66, 0x06, 0x1C, 0x06, 0x66, 0x3C, 0x00}, /* 3 */
/* 34 */ {0x0C, 0x1C, 0x3C, 0x6C, 0x7E, 0x0C, 0x0C, 0x00}, /* 4 */
/* 35 */ {0x7E, 0x60, 0x7C, 0x06, 0x06, 0x66, 0x3C, 0x00}, /* 5 */
/* 36 */ {0x1C, 0x30, 0x60, 0x7C, 0x66, 0x66, 0x3C, 0x00}, /* 6 */
/* 37 */ {0x7E, 0x06, 0x0C, 0x18, 0x30, 0x30, 0x30, 0x00}, /* 7 */
/* 38 */ {0x3C, 0x66, 0x66, 0x3C, 0x66, 0x66, 0x3C, 0x00}, /* 8 */
/* 39 */ {0x3C, 0x66, 0x66, 0x3E, 0x06, 0x0C, 0x38, 0x00}, /* 9 */
/* 3A */ {0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x00}, /* : */
/* 3B */ {0x00, 0x00, 0x18, 0x18, 0x00, 0x18, 0x18, 0x30}, /* ; */
/* 3C */ {0x0C, 0x18, 0x30, 0x60, 0x30, 0x18, 0x0C, 0x00}, /* < */
/* 3D */ {0x00, 0x00, 0x7E, 0x00, 0x7E, 0x00, 0x00, 0x00}, /* = */
/* 3E */ {0x30, 0x18, 0x0C, 0x06, 0x0C, 0x18, 0x30, 0x00}, /* > */
/* 3F */ {0x3C, 0x66, 0x0C, 0x18, 0x18, 0x00, 0x18, 0x00}, /* ? */
 
/* 40 */ {0x3C, 0x66, 0x6E, 0x6A, 0x6E, 0x60, 0x3C, 0x00}, /* @ */
/* 41 */ {0x3C, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* A */
/* 42 */ {0x7C, 0x66, 0x66, 0x7C, 0x66, 0x66, 0x7C, 0x00}, /* B */
/* 43 */ {0x3C, 0x66, 0x60, 0x60, 0x60, 0x66, 0x3C, 0x00}, /* C */
/* 44 */ {0x78, 0x6C, 0x66, 0x66, 0x66, 0x6C, 0x78, 0x00}, /* D */
/* 45 */ {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x7E, 0x00}, /* E */
/* 46 */ {0x7E, 0x60, 0x60, 0x7C, 0x60, 0x60, 0x60, 0x00}, /* F */
/* 47 */ {0x3C, 0x66, 0x60, 0x6E, 0x66, 0x66, 0x3C, 0x00}, /* G */
/* 48 */ {0x66, 0x66, 0x66, 0x7E, 0x66, 0x66, 0x66, 0x00}, /* H */
/* 49 */ {0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x7E, 0x00}, /* I */
/* 4A */ {0x3E, 0x0C, 0x0C, 0x0C, 0x0C, 0x6C, 0x38, 0x00}, /* J */
/* 4B */ {0x66, 0x6C, 0x78, 0x70, 0x78, 0x6C, 0x66, 0x00}, /* K */
/* 4C */ {0x60, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7E, 0x00}, /* L */
/* 4D */ {0x63, 0x77, 0x7F, 0x6B, 0x6B, 0x63, 0x63, 0x00}, /* M */
/* 4E */ {0x66, 0x66, 0x76, 0x7E, 0x6E, 0x66, 0x66, 0x00}, /* N */
/* 4F */ {0x3C, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* O */
 
/* 50 */ {0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60, 0x60, 0x00}, /* P */
/* 51 */ {0x3C, 0x66, 0x66, 0x66, 0x6A, 0x6C, 0x36, 0x00}, /* Q */
/* 52 */ {0x7C, 0x66, 0x66, 0x7C, 0x6C, 0x66, 0x66, 0x00}, /* R */
/* 53 */ {0x3C, 0x66, 0x60, 0x3C, 0x06, 0x66, 0x3C, 0x00}, /* S */
/* 54 */ {0x7E, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* T */
/* 55 */ {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* U */
/* 56 */ {0x66, 0x66, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* V */
/* 57 */ {0x63, 0x63, 0x6B, 0x6B, 0x7F, 0x77, 0x63, 0x00}, /* W */
/* 58 */ {0x66, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x66, 0x00}, /* X */
/* 59 */ {0x66, 0x66, 0x66, 0x3C, 0x18, 0x18, 0x18, 0x00}, /* Y */
/* 5A */ {0x7E, 0x06, 0x0C, 0x18, 0x30, 0x60, 0x7E, 0x00}, /* Z */
/* 5B */ {0x7C, 0x60, 0x60, 0x60, 0x60, 0x60, 0x7C, 0x00}, /* [ */
/* 5C */ {0x00, 0x60, 0x30, 0x18, 0x0C, 0x06, 0x00, 0x00}, /* \ */
/* 5D */ {0x3E, 0x06, 0x06, 0x06, 0x06, 0x06, 0x3E, 0x00}, /* ] */
/* 5E */ {0x3C, 0x66, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ^ */
/* 5F */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFF}, /* _ */
 
/* 60 */ {0x30, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ` */
/* 61 */ {0x00, 0x00, 0x3C, 0x06, 0x3E, 0x66, 0x3E, 0x00}, /* a */
/* 62 */ {0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x7C, 0x00}, /* b */
/* 63 */ {0x00, 0x00, 0x3C, 0x66, 0x60, 0x66, 0x3C, 0x00}, /* c */
/* 64 */ {0x06, 0x06, 0x3E, 0x66, 0x66, 0x66, 0x3E, 0x00}, /* d */
/* 65 */ {0x00, 0x00, 0x3C, 0x66, 0x7E, 0x60, 0x3C, 0x00}, /* e */
/* 66 */ {0x1C, 0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x00}, /* f */
/* 67 */ {0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x3C}, /* g */
/* 68 */ {0x60, 0x60, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00}, /* h */
/* 69 */ {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* i */
/* 6A */ {0x18, 0x00, 0x38, 0x18, 0x18, 0x18, 0x18, 0x70}, /* j */
/* 6B */ {0x60, 0x60, 0x66, 0x6C, 0x78, 0x6C, 0x66, 0x00}, /* k */
/* 6C */ {0x38, 0x18, 0x18, 0x18, 0x18, 0x18, 0x3C, 0x00}, /* l */
/* 6D */ {0x00, 0x00, 0x36, 0x7F, 0x6B, 0x6B, 0x63, 0x00}, /* m */
/* 6E */ {0x00, 0x00, 0x7C, 0x66, 0x66, 0x66, 0x66, 0x00}, /* n */
/* 6F */ {0x00, 0x00, 0x3C, 0x66, 0x66, 0x66, 0x3C, 0x00}, /* o */
 
/* 70 */ {0x00, 0x00, 0x7C, 0x66, 0x66, 0x7C, 0x60, 0x60}, /* p */
/* 71 */ {0x00, 0x00, 0x3E, 0x66, 0x66, 0x3E, 0x06, 0x07}, /* q */
/* 72 */ {0x00, 0x00, 0x6C, 0x76, 0x60, 0x60, 0x60, 0x00}, /* r */
/* 73 */ {0x00, 0x00, 0x3E, 0x60, 0x3C, 0x06, 0x7C, 0x00}, /* s */
/* 74 */ {0x30, 0x30, 0x7C, 0x30, 0x30, 0x30, 0x1C, 0x00}, /* t */
/* 75 */ {0x00, 0x00, 0x66, 0x66, 0x66, 0x66, 0x3E, 0x00}, /* u */
/* 76 */ {0x00, 0x00, 0x66, 0x66, 0x66, 0x3C, 0x18, 0x00}, /* v */
/* 77 */ {0x00, 0x00, 0x63, 0x6B, 0x6B, 0x7F, 0x36, 0x00}, /* w */
/* 78 */ {0x00, 0x00, 0x66, 0x3C, 0x18, 0x3C, 0x66, 0x00}, /* x */
/* 79 */ {0x00, 0x00, 0x66, 0x66, 0x66, 0x3E, 0x06, 0x3C}, /* y */
/* 7A */ {0x00, 0x00, 0x7E, 0x0C, 0x18, 0x30, 0x7E, 0x00}, /* z */
/* 7B */ {0x0C, 0x18, 0x18, 0x70, 0x18, 0x18, 0x0C, 0x00}, /* { */
/* 7C */ {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x00}, /* | */
/* 7D */ {0x30, 0x18, 0x18, 0x0E, 0x18, 0x18, 0x30, 0x00}, /* } */
/* 7E */ {0x31, 0x6B, 0x46, 0x00, 0x00, 0x00, 0x00, 0x00}, /* ~ */
/* 7F */ {0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF}, /*  */
 
/* 80 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 81 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 82 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 83 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 84 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 85 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 86 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 87 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 88 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 89 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8A */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8B */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8C */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8D */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8E */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 8F */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 
/* 90 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 91 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 92 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 93 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 94 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 95 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 96 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 97 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 98 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 99 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9A */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9B */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9C */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9D */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9E */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* 9F */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 
/* A0 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A1 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A2 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A3 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A4 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A5 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A6 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A7 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A8 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* A9 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AA */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AB */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AC */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AD */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AE */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* AF */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 
/* B0 */ {0x22, 0x88, 0x22, 0x88, 0x22, 0x88, 0x22, 0x88},
/* B1 */ {0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa, 0x55, 0xaa},
/* B2 */ {0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77, 0xdd, 0x77},
/* B3 */ {0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18},
/* B4 */ {0x18, 0x18, 0x18, 0xf8, 0x18, 0x18, 0x18, 0x18},
/* B5 */ {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18},
/* B6 */ {0x66, 0x66, 0x66, 0xe6, 0x66, 0x66, 0x66, 0x66},
/* B7 */ {0x00, 0x00, 0x00, 0xfe, 0x66, 0x66, 0x66, 0x66},
/* B8 */ {0x00, 0x00, 0xf8, 0x18, 0xf8, 0x18, 0x18, 0x18},
/* B9 */ {0x66, 0x66, 0xe6, 0x06, 0xe6, 0x66, 0x66, 0x66},
/* BA */ {0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66, 0x66},
/* BB */ {0x00, 0x00, 0xfe, 0x06, 0xe6, 0x66, 0x66, 0x66},
/* BC */ {0x66, 0x66, 0xe6, 0x06, 0xfe, 0x00, 0x00, 0x00},
/* BD */ {0x66, 0x66, 0x66, 0xfe, 0x00, 0x00, 0x00, 0x00},
/* BE */ {0x18, 0x18, 0xf8, 0x18, 0xf8, 0x00, 0x00, 0x00},
/* BF */ {0x00, 0x00, 0x00, 0xf8, 0x18, 0x18, 0x18, 0x18},
 
/* C0 */ {0x18, 0x18, 0x18, 0x1f, 0x00, 0x00, 0x00, 0x00},
/* C1 */ {0x18, 0x18, 0x18, 0xff, 0x00, 0x00, 0x00, 0x00},
/* C2 */ {0x00, 0x00, 0x00, 0xff, 0x18, 0x18, 0x18, 0x18},
/* C3 */ {0x18, 0x18, 0x18, 0x1f, 0x18, 0x18, 0x18, 0x18},
/* C4 */ {0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0x00, 0x00},
/* C5 */ {0x18, 0x18, 0x18, 0xff, 0x18, 0x18, 0x18, 0x18},
/* C6 */ {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18},
/* C7 */ {0x66, 0x66, 0x66, 0x67, 0x66, 0x66, 0x66, 0x66},
/* C8 */ {0x66, 0x66, 0x67, 0x60, 0x7f, 0x00, 0x00, 0x00},
/* C9 */ {0x00, 0x00, 0x7f, 0x60, 0x67, 0x66, 0x66, 0x66},
/* CA */ {0x66, 0x66, 0xe7, 0x00, 0xff, 0x00, 0x00, 0x00},
/* CB */ {0x00, 0x00, 0xff, 0x00, 0xe7, 0x66, 0x66, 0x66},
/* CC */ {0x66, 0x66, 0x67, 0x60, 0x67, 0x66, 0x66, 0x66},
/* CD */ {0x00, 0x00, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00},
/* CE */ {0x66, 0x66, 0xe7, 0x00, 0xe7, 0x66, 0x66, 0x66},
/* CF */ {0x18, 0x18, 0xff, 0x00, 0xff, 0x00, 0x00, 0x00},
 
/* D0 */ {0x66, 0x66, 0x66, 0xff, 0x00, 0x00, 0x00, 0x00},
/* D1 */ {0x00, 0x00, 0xff, 0x00, 0xff, 0x18, 0x18, 0x18},
/* D2 */ {0x00, 0x00, 0x00, 0xff, 0x66, 0x66, 0x66, 0x66},
/* D3 */ {0x66, 0x66, 0x66, 0x7f, 0x00, 0x00, 0x00, 0x00},
/* D4 */ {0x18, 0x18, 0x1f, 0x18, 0x1f, 0x00, 0x00, 0x00},
/* D5 */ {0x00, 0x00, 0x1f, 0x18, 0x1f, 0x18, 0x18, 0x18},
/* D6 */ {0x00, 0x00, 0x00, 0x7f, 0x66, 0x66, 0x66, 0x66},
/* D7 */ {0x66, 0x66, 0x66, 0xff, 0x66, 0x66, 0x66, 0x66},
/* D8 */ {0x18, 0x18, 0xff, 0x18, 0xff, 0x18, 0x18, 0x18},
/* D9 */ {0x18, 0x18, 0x18, 0xf8, 0x00, 0x00, 0x00, 0x00},
/* DA */ {0x00, 0x00, 0x00, 0x1f, 0x18, 0x18, 0x18, 0x18},
/* DB */ {0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff},
/* DC */ {0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff},
/* DD */ {0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0, 0xf0},
/* DE */ {0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f, 0x0f},
/* DF */ {0xff, 0xff, 0xff, 0xff, 0x00, 0x00, 0x00, 0x00},
 
/* E0 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E1 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E2 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E3 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E4 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E5 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E6 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E7 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E8 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* E9 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* EA */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* EB */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* EC */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* ED */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* EE */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* EF */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
 
/* F0 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F1 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F2 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F3 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F4 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F5 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F6 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F7 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F8 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* F9 */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FA */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FB */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FC */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FD */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FE */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
/* FF */ {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
};
/serialecho.c
0,0 → 1,124
/*
* linux/arch/arm/drivers/char/serialecho.c
*
* Serial echoing for kernel console messages.
*/
#if defined (CONFIG_ARCH_A5K) || defined (CONFIG_ARCH_RPC) || defined(CONFIG_ARCH_EBSA110)
 
#include <asm/io.h>
 
#include <linux/serial_reg.h>
 
extern int serial_echo_init (int base);
extern int serial_echo_print (const char *s);
 
/*
* this defines the address for the port to which printk echoing is done
* when CONFIG_SERIAL_ECHO is defined
*/
#ifndef SERIAL_ECHO_PORT
#define SERIAL_ECHO_PORT 0x3f8 /* internal serial port */
#endif
 
#ifndef SERIAL_ECHO_DIVISOR
#define SERIAL_ECHO_DIVISOR 12 /* 9600 baud */
#endif
 
static int serial_echo_port = 0;
 
#define serial_echo_outb(v,a) outb((v),(a)+serial_echo_port)
#define serial_echo_inb(a) inb((a)+serial_echo_port)
 
#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE)
 
/* wait for the transmitter & holding register to empty */
#define WAIT_FOR_XMITR \
do { \
lsr = serial_echo_inb (UART_LSR); \
} while ((lsr & BOTH_EMPTY) != BOTH_EMPTY)
 
/*
* These two functions abstract the actual communications with the
* debug port. This is so we can change the underlying communications
* mechanism without modifying the rest of the code.
*/
int serial_echo_print (const char *s)
{
int lsr, ier;
int i;
 
if (!serial_echo_port)
return 0;
 
/*
* First save the IER then disable interrupts
*/
ier = serial_echo_inb (UART_IER);
serial_echo_outb (0x00, UART_IER);
 
/*
* Now do each character
*/
for (i = 0; *s; i++, s++) {
WAIT_FOR_XMITR;
 
/* send the character out. */
serial_echo_outb (*s, UART_TX);
 
/* if a LF, also do CR... */
if (*s == 10) {
WAIT_FOR_XMITR;
serial_echo_outb (13, UART_TX);
}
}
 
/*
* Finally, wait for transmitter & holding register to empty
* and restore the IER.
*/
WAIT_FOR_XMITR;
serial_echo_outb (ier, UART_IER);
 
return 0;
}
 
int serial_echo_init (int base)
{
int comstat, hi, lo;
 
if (base != 0x3f8 && base != 0x2f8) {
serial_echo_port = 0;
return 0;
} else
serial_echo_port = base;
 
/*
* Read the Divisor Latch
*/
comstat = serial_echo_inb (UART_LCR);
serial_echo_outb (comstat | UART_LCR_DLAB, UART_LCR);
hi = serial_echo_inb (UART_DLM);
lo = serial_echo_inb (UART_DLL);
serial_echo_outb (comstat, UART_LCR);
 
/*
* now do a hardwired init
*/
serial_echo_outb (0x03, UART_LCR); /* No parity, 8 bits, 1 stop */
serial_echo_outb (0x83, UART_LCR); /* Access divisor latch */
serial_echo_outb (SERIAL_ECHO_DIVISOR >> 8, UART_DLM);
serial_echo_outb (SERIAL_ECHO_DIVISOR & 0xff, UART_DLL);
serial_echo_outb (0x03, UART_LCR); /* Done with divisor */
 
/* Prior to disabling interrupts, read the LSR and RBR
* registers
*/
comstat = serial_echo_inb (UART_LSR); /* COM? LSR */
comstat = serial_echo_inb (UART_RX); /* COM? RBR */
serial_echo_outb (0x00, UART_IER); /* Disable all interrupts */
 
return 0;
}
 
#endif
 
/consoledriver.c
0,0 → 1,475
/*
* linux/arch/arm/drivers/char/consoledriver.c
*
* Low-level console routines
*/
#include <linux/string.h>
 
#include <asm/hardware.h>
#include <asm/io.h>
 
#include "vt_kern.h"
 
#define palette_setpixel(p) outl(0x10000000|((p) & 255), IO_VIDC_BASE)
#define palette_write(v) outl(0x00000000|((v) & 0x00ffffff), IO_VIDC_BASE)
/*
* Don't allow underline through to erasing/scrolling routines
*/
#define FLG_MASK (~(FLG_UNDERLINE << 24))
 
extern void scroll_set_origin (unsigned long offset);
extern void ll_write_char(unsigned long ps, unsigned long chinfo);
 
static void vcd_buffer_gotoxy(struct con_struct *vcd)
{
vcd->screen.pos = vcd->screen.origin;
vcd->buffer.pos = vcd->curstate.y * vtdata.buffer.sizerow +
vcd->curstate.x;
}
 
static void vcd_buffer_scrollup(struct con_struct *vcd, unsigned int t, unsigned int b, unsigned int l)
{
unsigned int old_top, new_top, old_end, new_end;
 
if (b > vtdata.numrows || t >= b || l == 0)
return;
 
l *= vtdata.buffer.sizerow;
old_top = t * vtdata.buffer.sizerow;
new_top = old_top + l;
new_end = b * vtdata.buffer.sizerow;
old_end = new_end - l;
 
if (new_top < new_end) /* we have something to move */
memmove (vcd->buffer.buffer + old_top, vcd->buffer.buffer + new_top,
(new_end - new_top) << 2);
 
if (old_end < new_end) {
if (old_end < old_top)
old_end = old_top;
memsetl (vcd->buffer.buffer + old_end, (vcd->combined_state & FLG_MASK) | 32,
(new_end - old_end) << 2);
}
}
 
static void vcd_buffer_scrolldown(struct con_struct *vcd, unsigned int t, unsigned int b, unsigned int l)
{
unsigned int old_top, new_top, old_end, new_end;
 
if (b > vtdata.numrows || t >= b || l == 0)
return;
 
l *= vtdata.buffer.sizerow;
new_top = t * vtdata.buffer.sizerow;
old_top = new_top + l;
old_end = b * vtdata.buffer.sizerow;
new_end = old_end - l;
 
if (new_top < new_end) /* we have something to move */
memmove (vcd->buffer.buffer + old_top, vcd->buffer.buffer + new_top,
(new_end - new_top) << 2);
 
if (new_top < old_top) { /* we have something to clear */
if (old_top > old_end)
old_top = old_end;
memsetl (vcd->buffer.buffer + new_top, (vcd->combined_state & FLG_MASK) | 32,
(old_top - new_top) << 2);
}
}
 
static unsigned long *vcd_buffer_buffer_pos(struct con_struct *vcd, unsigned int offset)
{
return vcd->buffer.buffer + offset;
}
 
static void vcd_buffer_delete_char(struct con_struct *vcd, unsigned int n)
{
unsigned long *buffer;
unsigned int len;
 
buffer = vcd->buffer.buffer + vcd->buffer.pos;
 
if (n >= vtdata.numcolumns - vcd->curstate.x) {
n = vtdata.numcolumns - vcd->curstate.x;
len = 0;
} else {
len = vtdata.numcolumns - vcd->curstate.x - n;
memmove (buffer, buffer + n, len << 2);
}
memsetl (buffer + len, (vcd->combined_state & FLG_MASK) | 32, n << 2);
}
 
static void vcd_buffer_insert_char(struct con_struct *vcd, unsigned int n)
{
unsigned long *buffer;
 
buffer = vcd->buffer.buffer + vcd->buffer.pos;
 
if (n >= vtdata.numcolumns - vcd->curstate.x)
n = vtdata.numcolumns - vcd->curstate.x;
else
memmove (buffer + n, buffer, (vtdata.numcolumns - vcd->curstate.x - n) << 2);
memsetl (buffer, (vcd->combined_state & FLG_MASK) | 32, n << 2);
}
 
static void vcd_buffer_write_char(struct con_struct *vcd, unsigned long character)
{
if (vcd->need_wrap) {
vcd->buffer.pos -= vcd->curstate.x;
vcd->need_wrap = vcd->curstate.x = 0;
if (vcd->curstate.y + 1 == vcd->bottom)
vcd_buffer_scrollup(vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y + 1 < vtdata.numrows) {
vcd->curstate.y += 1;
vcd->buffer.pos += vtdata.buffer.sizerow;
}
}
if (vcd->decim)
vcd_buffer_insert_char(vcd, 1);
vcd->buffer.buffer[vcd->buffer.pos] = character;
 
if (vcd->curstate.x + 1 == vtdata.numcolumns)
vcd->need_wrap = vcd->decawm;
else {
vcd->curstate.x ++;
vcd->buffer.pos ++;
}
}
 
static void vcd_buffer_erase(struct con_struct *vcd, unsigned char sx, unsigned char sy,
unsigned char ex, unsigned char ey)
{
unsigned int start = sy * vtdata.numcolumns + sx;
unsigned int end = ey * vtdata.numcolumns + ex + 1;
 
memsetl (vcd->buffer.buffer + start, (vcd->combined_state & FLG_MASK) | 32, (end - start) << 2);
}
 
struct console_driver buffer_driver = {
vcd_buffer_gotoxy,
vcd_buffer_scrollup,
vcd_buffer_scrolldown,
vcd_buffer_buffer_pos,
vcd_buffer_delete_char,
vcd_buffer_insert_char,
NULL,
vcd_buffer_write_char,
vcd_buffer_erase
};
 
 
static void vcd_screen_gotoxy(struct con_struct *vcd)
{
vcd->screen.pos = vcd->screen.origin +
vcd->curstate.y * vtdata.screen.sizerow +
vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos = vtdata.buffer.origin +
vcd->curstate.y * vtdata.buffer.sizerow +
vcd->curstate.x;
}
 
static void vcd_screen_scrollup(struct con_struct *vcd, unsigned int t, unsigned int b, unsigned int l)
{
unsigned int hardware;
 
if (b > vtdata.numrows || t >= b || l == 0)
return;
 
hardware = (b == vtdata.numrows && t == 0);
 
if (hardware) {
vcd->buffer.pos -= vtdata.buffer.origin;
vtdata.buffer.origin += l * vtdata.buffer.sizerow;
if (vtdata.buffer.origin > vtdata.buffer.lastorigin) {
memmove (vtdata.buffer.buffer,
vtdata.buffer.buffer + vtdata.buffer.origin,
(vtdata.buffer.totsize - vtdata.buffer.sizerow) << 2);
vtdata.buffer.origin = 0;
}
vcd->buffer.pos += vtdata.buffer.origin;
memsetl (vtdata.buffer.buffer + vtdata.buffer.origin +
(vtdata.numrows - l) * vtdata.buffer.sizerow,
(vcd->combined_state & FLG_MASK) | 32,
l * vtdata.buffer.sizerow << 2);
 
vcd->screen.pos -= vcd->screen.origin;
vcd->screen.origin += l * vtdata.screen.sizerow;
if (vcd->screen.origin >= vtdata.screen.memend)
vcd->screen.origin = vcd->screen.origin -
vtdata.screen.memend + vtdata.screen.memstart;
vcd->screen.pos += vcd->screen.origin;
memsetl ((void *)(vcd->screen.origin + vtdata.screen.sizerow * (vtdata.numrows - l)),
vcd->cached_backcolwrd, l * vtdata.screen.sizerow);
scroll_set_origin (vcd->screen.origin);
} else {
unsigned int old_top, new_top, old_end, new_end;
unsigned char *origin = (unsigned char *)vcd->screen.origin;
 
old_top = t;
new_top = t + l;
new_end = b;
old_end = b - l;
 
if (new_top < new_end) { /* we have something to move */
memmove (vtdata.buffer.buffer + vtdata.buffer.origin + old_top * vtdata.buffer.sizerow,
vtdata.buffer.buffer + vtdata.buffer.origin + new_top * vtdata.buffer.sizerow,
(new_end - new_top) * vtdata.buffer.sizerow << 2);
memmove (origin + old_top * vtdata.screen.sizerow,
origin + new_top * vtdata.screen.sizerow,
(new_end - new_top) * vtdata.screen.sizerow);
}
if (old_end < new_end) {
if (old_end < old_top)
old_end = old_top;
memsetl (vtdata.buffer.buffer + vtdata.buffer.origin + old_end * vtdata.buffer.sizerow,
(vcd->combined_state & FLG_MASK) | 32,
(new_end - old_end) * vtdata.buffer.sizerow << 2);
memsetl ((unsigned long *)(origin + old_end * vtdata.screen.sizerow),
vcd->cached_backcolwrd,
(new_end - old_end) * vtdata.screen.sizerow);
}
}
}
 
static void vcd_screen_scrolldown(struct con_struct *vcd, unsigned int t, unsigned int b, unsigned int l)
{
unsigned int hardware;
if (b > vtdata.numrows || t >= b || l == 0)
return;
 
hardware = (b == vtdata.numrows && t == 0);
 
if (hardware) {
vcd->buffer.pos -= vtdata.buffer.origin;
vtdata.buffer.origin -= vtdata.buffer.sizerow;
if ((int)vtdata.buffer.origin < 0) {
memmove (vtdata.buffer.buffer + vtdata.buffer.lastorigin + l * vtdata.buffer.sizerow,
vtdata.buffer.buffer,
(vtdata.buffer.totsize - l * vtdata.buffer.sizerow) << 2);
vtdata.buffer.origin = vtdata.buffer.lastorigin;
}
vcd->buffer.pos += vtdata.buffer.origin;
memsetl (vtdata.buffer.buffer + vtdata.buffer.origin, (vcd->combined_state & FLG_MASK) | 32,
vtdata.buffer.sizerow << 2);
 
vcd->screen.pos -= vcd->screen.origin;
vcd->screen.origin -= l * vtdata.screen.sizerow;
if (vcd->screen.origin < vtdata.screen.memstart)
vcd->screen.origin = vcd->screen.origin - vtdata.screen.memstart + vtdata.screen.memend;
vcd->screen.pos += vcd->screen.origin;
memsetl ((void *)vcd->screen.origin, vcd->cached_backcolwrd, l * vtdata.screen.sizerow);
scroll_set_origin (vcd->screen.origin);
} else {
unsigned int old_top, new_top, old_end, new_end;
unsigned char *origin = (unsigned char *)vcd->screen.origin;
 
new_top = t;
old_top = t + l;
old_end = b;
new_end = b - l;
 
if (new_top < new_end) { /* we have something to move */
memmove (vtdata.buffer.buffer + vtdata.buffer.origin + old_top * vtdata.buffer.sizerow,
vtdata.buffer.buffer + vtdata.buffer.origin + new_top * vtdata.buffer.sizerow,
(new_end - new_top) * vtdata.buffer.sizerow << 2);
memmove (origin + old_top * vtdata.screen.sizerow,
origin + new_top * vtdata.screen.sizerow,
(new_end - new_top) * vtdata.screen.sizerow);
}
if (new_top < old_top) { /* we have something to clear */
if (old_top > old_end)
old_top = old_end;
memsetl (vtdata.buffer.buffer + vtdata.buffer.origin + new_top * vtdata.buffer.sizerow,
(vcd->combined_state & FLG_MASK) | 32,
(old_top - new_top) * vtdata.buffer.sizerow << 2);
memsetl ((unsigned long *)(origin + new_top * vtdata.screen.sizerow),
vcd->cached_backcolwrd,
(old_top - new_top) * vtdata.screen.sizerow);
}
}
}
 
static unsigned long *vcd_screen_buffer_pos(struct con_struct *vcd, unsigned int offset)
{
return vtdata.buffer.buffer + vtdata.buffer.origin + offset;
}
 
static void vcd_screen_delete_char (struct con_struct *vcd, unsigned int n)
{
unsigned int row, clear, movelen, setlen, bpr;
unsigned char *to, *from;
unsigned long *buffer;
 
buffer = vtdata.buffer.buffer + vcd->buffer.pos;
 
if (n >= vtdata.numcolumns - vcd->curstate.x) {
n = vtdata.numcolumns - vcd->curstate.x;
movelen = 0;
} else {
movelen = vtdata.numcolumns - vcd->curstate.x - n;
memmove (buffer, buffer + n, movelen << 2);
}
memsetl (buffer + movelen, (vcd->combined_state & FLG_MASK) | 32, n << 2);
 
clear = vcd->cached_backcolwrd & 255;
to = (unsigned char *)vcd->screen.pos;
setlen = n * vtdata.screen.bytespercharh;
from = to + setlen;
bpr = vtdata.numcolumns * vtdata.screen.bytespercharh;
movelen = bpr - setlen - vcd->curstate.x * vtdata.screen.bytespercharh;
 
for (row = vtdata.screen.bytespercharv; row > 0; row--, to += bpr, from += bpr) {
memmove (to, from, movelen);
memset (to + movelen, clear, setlen);
}
}
 
static void vcd_screen_insert_char (struct con_struct *vcd, unsigned int n)
{
unsigned int row, clear, movelen, setlen, bpr;
unsigned char *to, *from;
unsigned long *buffer;
 
buffer = vtdata.buffer.buffer + vcd->buffer.pos;
 
if (n >= vtdata.numcolumns - vcd->curstate.x)
n = vtdata.numcolumns - vcd->curstate.x;
else
memmove (buffer + n, buffer, (vtdata.numcolumns - vcd->curstate.x - n) << 2);
memsetl (buffer, (vcd->combined_state & FLG_MASK) | 32, n << 2);
 
clear = vcd->cached_backcolwrd & 255;
from = (unsigned char *)vcd->screen.pos;
setlen = n * vtdata.screen.bytespercharh;
to = from + setlen;
bpr = vtdata.numcolumns * vtdata.screen.bytespercharh;
movelen = bpr - setlen - vcd->curstate.x * vtdata.screen.bytespercharh;
 
for (row = vtdata.screen.bytespercharv; row > 0; row--, to += bpr, from += bpr) {
memmove (to, from, movelen);
memset (from, clear, setlen);
}
}
 
static void vcd_screen_put_char(struct con_struct *vcd, unsigned long character)
{
char c = character & 255;
 
if (c == 8) {
if (!vcd->need_wrap) {
vcd->screen.pos -= vtdata.screen.bytespercharh;
vcd->buffer.pos --;
vcd->curstate.x --;
} else
vcd->need_wrap = 0;
return;
}
if (c == 10 || c == 13 || vcd->need_wrap) {
vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos -= vcd->curstate.x;
vcd->need_wrap = vcd->curstate.x = 0;
if (vcd->curstate.y + 1 == vcd->bottom)
vcd_screen_scrollup (vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y < vtdata.numrows - 1) {
vcd->curstate.y += 1;
vcd->buffer.pos += vtdata.buffer.sizerow;
vcd->screen.pos += vtdata.screen.sizerow;
}
if (c == 10 || c == 13)
return;
}
ll_write_char (vcd->screen.pos, character);
vtdata.buffer.buffer[vcd->buffer.pos] = character;
 
if (vcd->curstate.x + 1 == vtdata.numcolumns)
vcd->need_wrap = 1;
else {
vcd->curstate.x++;
vcd->screen.pos += vtdata.screen.bytespercharh;
vcd->buffer.pos ++;
}
}
 
static void vcd_screen_write_char(struct con_struct *vcd, unsigned long character)
{
if (vcd->need_wrap) {
vcd->screen.pos -= vcd->curstate.x * vtdata.screen.bytespercharh;
vcd->buffer.pos -= vcd->curstate.x;
vcd->need_wrap = vcd->curstate.x = 0;
if (vcd->curstate.y + 1 == vcd->bottom)
vcd_screen_scrollup(vcd, vcd->top, vcd->bottom, 1);
else if (vcd->curstate.y + 1 < vtdata.numrows) {
vcd->curstate.y += 1;
vcd->screen.pos += vtdata.screen.sizerow;
vcd->buffer.pos += vtdata.buffer.sizerow;
}
}
if (vcd->decim)
vcd_screen_insert_char(vcd, 1);
vtdata.buffer.buffer[vcd->buffer.pos] = character;
ll_write_char (vcd->screen.pos, character);
 
if (vcd->curstate.x + 1 == vtdata.numcolumns)
vcd->need_wrap = vcd->decawm;
else {
vcd->curstate.x ++;
vcd->screen.pos += vtdata.screen.bytespercharh;
vcd->buffer.pos ++;
}
}
 
static void vcd_screen_erase (struct con_struct *vcd, unsigned char sx, unsigned char sy,
unsigned char ex, unsigned char ey)
{
unsigned int start = sy * vtdata.numcolumns + sx;
unsigned int end = ey * vtdata.numcolumns + ex + 1;
unsigned char *origin;
 
memsetl (vtdata.buffer.buffer + vtdata.buffer.origin + start,
(vcd->combined_state & FLG_MASK) | 32, (end - start) << 2);
if (sx) { /* erase to end of line */
unsigned int c, i, increment;
 
origin = (unsigned char *)vcd->screen.origin +
sy * vtdata.screen.sizerow +
sx * vtdata.screen.bytespercharh;
increment = vtdata.numcolumns * vtdata.screen.bytespercharh;
if (sy == ey)
c = (ex - sx + 1) * vtdata.screen.bytespercharh;
else
c = (vtdata.numcolumns - sx) * vtdata.screen.bytespercharh;
 
for (i = vtdata.screen.bytespercharv; i; i--, origin += increment)
memset (origin, vcd->cached_backcolwrd, c);
sy ++;
}
 
if (sy < ey) {
origin = (unsigned char *)vcd->screen.origin + sy * vtdata.screen.sizerow;
memset (origin, vcd->cached_backcolwrd, (ey - sy) * vtdata.screen.sizerow);
sy = ey;
}
 
if (sy == ey) {
unsigned int c, i, increment;
 
origin = (unsigned char *)vcd->screen.origin +
sy * vtdata.screen.sizerow;
increment = vtdata.numcolumns * vtdata.screen.bytespercharh;
c = (ex + 1) * vtdata.screen.bytespercharh;
for (i = vtdata.screen.bytespercharv; i; i--, origin += increment)
memset (origin, vcd->cached_backcolwrd, c);
}
}
 
struct console_driver screen_driver = {
vcd_screen_gotoxy,
vcd_screen_scrollup,
vcd_screen_scrolldown,
vcd_screen_buffer_pos,
vcd_screen_delete_char,
vcd_screen_insert_char,
vcd_screen_put_char,
vcd_screen_write_char,
vcd_screen_erase
};
/serial.c
0,0 → 1,2929
/*
* linux/arch/arm/drivers/char/serial.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
* much more extensible to support other serial cards based on the
* 16450/16550A UART's. Added support for the AST FourPort and the
* Accent Async board.
*
* set_serial_info fixed to set the flags, custom divisor, and uart
* type fields. Fix suggested by Michael K. Johnson 12/12/92.
*
* 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk>
*
* 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
*
* rs_set_termios fixed to look also for changes of the input
* flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
 
*
* This module exports the following rs232 io functions:
*
* int rs_init(void);
* int rs_open(struct tty_struct * tty, struct file * filp)
*
* Slight modifications for ARM Copyright (C) 1995, 1996 Russell King
*/
 
#include <linux/module.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/serial.h>
#include <linux/serial_reg.h>
#include <linux/config.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
#include <linux/ioport.h>
#include <linux/mm.h>
 
#include <asm/system.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/segment.h>
#include <asm/bitops.h>
#include <asm/serial.h>
 
static char *serial_name = "Serial driver";
static char *serial_version = "4.13";
 
DECLARE_TASK_QUEUE(tq_serial);
 
struct tty_driver serial_driver, callout_driver;
static int serial_refcount;
 
/* serial subtype definitions */
#define SERIAL_TYPE_NORMAL 1
#define SERIAL_TYPE_CALLOUT 2
 
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
 
/*
* Serial driver configuration section. Here are the various options:
*
* CONFIG_HUB6
* Enables support for the venerable Bell Technologies
* HUB6 card.
*
* SERIAL_PARANOIA_CHECK
* Check the magic number for the async_structure where
* ever possible.
*/
 
#define SERIAL_PARANOIA_CHECK
#define CONFIG_SERIAL_NOPAUSE_IO
#define SERIAL_DO_RESTART
 
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
 
#define RS_STROBE_TIME (10*HZ)
#define RS_ISR_PASS_LIMIT 256
 
#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
 
#define _INLINE_ inline
 
#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
#else
#define DBG_CNT(s)
#endif
/*
* IRQ_timeout - How long the timeout should be for each IRQ
* should be after the IRQ has been active.
*/
 
static struct async_struct *IRQ_ports[NR_IRQS];
static struct rs_multiport_struct rs_multiport[NR_IRQS];
static int IRQ_timeout[NR_IRQS];
static volatile int rs_irq_triggered;
static volatile int rs_triggered;
static int rs_wild_int_mask;
 
static void autoconfig(struct async_struct * info);
static void change_speed(struct async_struct *info);
 
struct async_struct rs_table[] = {
RS_UARTS
};
 
#define NR_PORTS (sizeof(rs_table)/sizeof(struct async_struct))
 
static struct tty_struct *serial_table[NR_PORTS];
static struct termios *serial_termios[NR_PORTS];
static struct termios *serial_termios_locked[NR_PORTS];
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
* lock it in case the memcpy_fromfs blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the serial ports, since it significantly saves
* memory if large numbers of serial ports are open.
*/
static unsigned char *tmp_buf;
static struct semaphore tmp_buf_sem = MUTEX;
 
static inline int serial_paranoia_check(struct async_struct *info,
kdev_t device, const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
static const char *badmagic =
"Warning: bad magic number for serial struct (%s) in %s\n";
static const char *badinfo =
"Warning: null async_struct for (%s) in %s\n";
 
if (!info) {
printk(badinfo, kdevname(device), routine);
return 1;
}
if (info->magic != SERIAL_MAGIC) {
printk(badmagic, kdevname(device), routine);
return 1;
}
#endif
return 0;
}
 
/*
* This is used to figure out the divisor speeds and the timeouts
*/
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 0 };
 
static inline unsigned int serial_in(struct async_struct *info, int offset)
{
#ifdef CONFIG_HUB6
if (info->hub6) {
outb(info->hub6 - 1 + offset, info->port);
return inb(info->port+1);
} else
#endif
return inb(info->port + offset);
}
 
static inline unsigned int serial_inp(struct async_struct *info, int offset)
{
#ifdef CONFIG_HUB6
if (info->hub6) {
outb(info->hub6 - 1 + offset, info->port);
return inb_p(info->port+1);
} else
#endif
#ifdef CONFIG_SERIAL_NOPAUSE_IO
return inb(info->port + offset);
#else
return inb_p(info->port + offset);
#endif
}
 
static inline void serial_out(struct async_struct *info, int offset, int value)
{
#ifdef CONFIG_HUB6
if (info->hub6) {
outb(info->hub6 - 1 + offset, info->port);
outb(value, info->port+1);
} else
#endif
outb(value, info->port+offset);
}
 
static inline void serial_outp(struct async_struct *info, int offset,
int value)
{
#ifdef CONFIG_HUB6
if (info->hub6) {
outb(info->hub6 - 1 + offset, info->port);
outb_p(value, info->port+1);
} else
#endif
#ifdef CONFIG_SERIAL_NOPAUSE_IO
outb(value, info->port+offset);
#else
outb_p(value, info->port+offset);
#endif
}
 
/*
* ------------------------------------------------------------
* rs_stop() and rs_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static void rs_stop(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
 
if (serial_paranoia_check(info, tty->device, "rs_stop"))
return;
save_flags_cli (flags);
if (info->IER & UART_IER_THRI) {
info->IER &= ~UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
restore_flags(flags);
}
 
static void rs_start(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_start"))
return;
save_flags_cli (flags);
if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
restore_flags(flags);
}
 
/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
* subroutines are declared as inline and are folded into
* rs_interrupt(). They were separated out for readability's sake.
*
* Note: rs_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* rs_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
*
* gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
*
* and look at the resulting assemble code in serial.s.
*
* - Ted Ts'o (tytso@mit.edu), 7-Mar-93
* -----------------------------------------------------------------------
*/
 
/*
* This is the serial driver's interrupt routine while we are probing
* for submarines.
*/
static void rs_probe(int irq, void *dev_id, struct pt_regs * regs)
{
rs_irq_triggered = irq;
rs_triggered |= 1 << irq;
return;
}
 
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static _INLINE_ void rs_sched_event(struct async_struct *info,
int event)
{
info->event |= 1 << event;
queue_task_irq_off(&info->tqueue, &tq_serial);
mark_bh(SERIAL_BH);
}
 
static _INLINE_ void receive_chars(struct async_struct *info,
int *status)
{
struct tty_struct *tty = info->tty;
unsigned char ch;
int ignored = 0;
 
do {
ch = serial_inp(info, UART_RX);
if (*status & info->ignore_status_mask) {
if (++ignored > 100)
break;
goto ignore_char;
}
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
break;
tty->flip.count++;
if (*status & (UART_LSR_BI)) {
#ifdef SERIAL_DEBUG_INTR
printk("handling break....");
#endif
*tty->flip.flag_buf_ptr++ = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (*status & UART_LSR_PE)
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
else if (*status & UART_LSR_FE)
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
else if (*status & UART_LSR_OE)
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
else
*tty->flip.flag_buf_ptr++ = 0;
*tty->flip.char_buf_ptr++ = ch;
ignore_char:
*status = serial_inp(info, UART_LSR) & info->read_status_mask;
} while (*status & UART_LSR_DR);
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
#ifdef SERIAL_DEBUG_INTR
printk("DR...");
#endif
}
 
static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
{
int count;
if (info->x_char) {
serial_outp(info, UART_TX, info->x_char);
info->x_char = 0;
if (intr_done)
*intr_done = 0;
return;
}
if ((info->xmit_cnt <= 0) || info->tty->stopped ||
info->tty->hw_stopped) {
info->IER &= ~UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
return;
}
count = info->xmit_fifo_size;
do {
serial_out(info, UART_TX, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
if (--info->xmit_cnt <= 0)
break;
} while (--count > 0);
if (info->xmit_cnt < WAKEUP_CHARS)
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
#ifdef SERIAL_DEBUG_INTR
printk("THRE...");
#endif
if (intr_done)
*intr_done = 0;
 
if (info->xmit_cnt <= 0) {
info->IER &= ~UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
}
 
static _INLINE_ void check_modem_status(struct async_struct *info)
{
int status;
status = serial_in(info, UART_MSR);
 
if (status & UART_MSR_ANY_DELTA) {
/* update input line counters */
if (status & UART_MSR_TERI)
info->icount.rng++;
if (status & UART_MSR_DDSR)
info->icount.dsr++;
if (status & UART_MSR_DDCD)
info->icount.dcd++;
if (status & UART_MSR_DCTS)
info->icount.cts++;
wake_up_interruptible(&info->delta_msr_wait);
}
 
if ((info->flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
printk("ttys%d CD now %s...", info->line,
(status & UART_MSR_DCD) ? "on" : "off");
#endif
if (status & UART_MSR_DCD)
wake_up_interruptible(&info->open_wait);
else if (!((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_CALLOUT_NOHUP))) {
#ifdef SERIAL_DEBUG_OPEN
printk("scheduling hangup...");
#endif
queue_task_irq_off(&info->tqueue_hangup,
&tq_scheduler);
}
}
if (info->flags & ASYNC_CTS_FLOW) {
if (info->tty->hw_stopped) {
if (status & UART_MSR_CTS) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx start...");
#endif
info->tty->hw_stopped = 0;
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
return;
}
} else {
if (!(status & UART_MSR_CTS)) {
#if (defined(SERIAL_DEBUG_INTR) || defined(SERIAL_DEBUG_FLOW))
printk("CTS tx stop...");
#endif
info->tty->hw_stopped = 1;
info->IER &= ~UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
}
}
}
 
/*
* This is the serial driver's generic interrupt routine
*/
static void rs_interrupt(int irq, void *dev_id, struct pt_regs * regs)
{
int status;
struct async_struct * info;
int pass_counter = 0;
struct async_struct *end_mark = 0;
int first_multi = 0;
struct rs_multiport_struct *multi;
 
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt(%d)...", irq);
#endif
info = IRQ_ports[irq];
if (!info)
return;
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
 
do {
if (!info->tty ||
(serial_in(info, UART_IIR) & UART_IIR_NO_INT)) {
if (!end_mark)
end_mark = info;
goto next;
}
end_mark = 0;
 
info->last_active = jiffies;
 
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
 
next:
info = info->next_port;
if (!info) {
info = IRQ_ports[irq];
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
#if 0
printk("rs loop break\n");
#endif
break; /* Prevent infinite loops */
}
continue;
}
} while (end_mark != info);
if (multi->port_monitor)
printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n",
info->irq, first_multi, inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
 
/*
* This is the serial driver's interrupt routine for a single port
*/
static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
{
int status;
int pass_counter = 0;
int first_multi = 0;
struct async_struct * info;
struct rs_multiport_struct *multi;
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_single(%d)...", irq);
#endif
info = IRQ_ports[irq];
if (!info || !info->tty)
return;
 
multi = &rs_multiport[irq];
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
 
do {
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
#if 0
printk("rs_single loop break.\n");
#endif
break;
}
} while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT));
info->last_active = jiffies;
if (multi->port_monitor)
printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n",
info->irq, first_multi, inb(multi->port_monitor));
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
 
/*
* This is the serial driver's for multiport boards
*/
static void rs_interrupt_multi(int irq, void *dev_id, struct pt_regs * regs)
{
int status;
struct async_struct * info;
int pass_counter = 0;
int first_multi= 0;
struct rs_multiport_struct *multi;
 
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_multi(%d)...", irq);
#endif
info = IRQ_ports[irq];
if (!info)
return;
multi = &rs_multiport[irq];
if (!multi->port1) {
/* Should never happen */
printk("rs_interrupt_multi: NULL port1!\n");
return;
}
if (multi->port_monitor)
first_multi = inb(multi->port_monitor);
while (1) {
if (!info->tty ||
(serial_in(info, UART_IIR) & UART_IIR_NO_INT))
goto next;
 
info->last_active = jiffies;
 
status = serial_inp(info, UART_LSR) & info->read_status_mask;
#ifdef SERIAL_DEBUG_INTR
printk("status = %x...", status);
#endif
if (status & UART_LSR_DR)
receive_chars(info, &status);
check_modem_status(info);
if (status & UART_LSR_THRE)
transmit_chars(info, 0);
 
next:
info = info->next_port;
if (info)
continue;
 
info = IRQ_ports[irq];
if (pass_counter++ > RS_ISR_PASS_LIMIT) {
#if 1
printk("rs_multi loop break\n");
#endif
break; /* Prevent infinite loops */
}
if (multi->port_monitor)
printk("rs port monitor irq %d: 0x%x, 0x%x\n",
info->irq, first_multi,
inb(multi->port_monitor));
if ((inb(multi->port1) & multi->mask1) != multi->match1)
continue;
if (!multi->port2)
break;
if ((inb(multi->port2) & multi->mask2) != multi->match2)
continue;
if (!multi->port3)
break;
if ((inb(multi->port3) & multi->mask3) != multi->match3)
continue;
if (!multi->port4)
break;
if ((inb(multi->port4) & multi->mask4) == multi->match4)
continue;
break;
}
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
}
 
 
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
 
/*
* This routine is used to handle the "bottom half" processing for the
* serial driver, known also the "software interrupt" processing.
* This processing is done at the kernel interrupt level, after the
* rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules
* them using rs_sched_event(), and they get done here.
*/
static void do_serial_bh(void)
{
run_task_queue(&tq_serial);
}
 
static void do_softint(void *private_)
{
struct async_struct *info = (struct async_struct *) private_;
struct tty_struct *tty;
tty = info->tty;
if (!tty)
return;
 
if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
}
}
 
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> rs_hangup()
*
*/
static void do_serial_hangup(void *private_)
{
struct async_struct *info = (struct async_struct *) private_;
struct tty_struct *tty;
tty = info->tty;
if (!tty)
return;
 
tty_hangup(tty);
}
 
 
/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work very well for 16450's, but gives barely
* passable results for a 16550A. (Although at the expense of much
* CPU overhead).
*/
static void rs_timer(void)
{
static unsigned long last_strobe = 0;
struct async_struct *info;
unsigned int i;
 
if ((jiffies - last_strobe) >= RS_STROBE_TIME) {
for (i=0; i < 32; i++) {
info = IRQ_ports[i];
if (!info)
continue;
cli();
if (info->next_port) {
do {
serial_out(info, UART_IER, 0);
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
info = info->next_port;
} while (info);
if (rs_multiport[i].port1)
rs_interrupt_multi(i, NULL, NULL);
else
rs_interrupt(i, NULL, NULL);
} else
rs_interrupt_single(i, NULL, NULL);
sti();
}
}
last_strobe = jiffies;
timer_table[RS_TIMER].expires = jiffies + RS_STROBE_TIME;
timer_active |= 1 << RS_TIMER;
 
if (IRQ_ports[0]) {
cli();
rs_interrupt(0, NULL, NULL);
sti();
 
timer_table[RS_TIMER].expires = jiffies + IRQ_timeout[0] - 2;
}
}
 
/*
* ---------------------------------------------------------------
* Low level utility subroutines for the serial driver: routines to
* figure out the appropriate timeout for an interrupt chain, routines
* to initialize and startup a serial port, and routines to shutdown a
* serial port. Useful stuff like that.
* ---------------------------------------------------------------
*/
 
/*
* Grab all interrupts in preparation for doing an automatic irq
* detection. dontgrab is a mask of irq's _not_ to grab. Returns a
* mask of irq's which were grabbed and should therefore be freed
* using free_all_interrupts().
*/
static int grab_all_interrupts(int dontgrab)
{
int irq_lines = 0;
int i, mask;
for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
if (!(mask & dontgrab) && !request_irq(i, rs_probe, SA_INTERRUPT, "serial probe", NULL)) {
irq_lines |= mask;
}
}
return irq_lines;
}
 
/*
* Release all interrupts grabbed by grab_all_interrupts
*/
static void free_all_interrupts(int irq_lines)
{
int i;
for (i = 0; i < 16; i++) {
if (irq_lines & (1 << i))
free_irq(i, NULL);
}
}
 
/*
* This routine figures out the correct timeout for a particular IRQ.
* It uses the smallest timeout of all of the serial ports in a
* particular interrupt chain. Now only used for IRQ 0....
*/
static void figure_IRQ_timeout(int irq)
{
struct async_struct *info;
int timeout = 60*HZ; /* 60 seconds === a long time :-) */
 
info = IRQ_ports[irq];
if (!info) {
IRQ_timeout[irq] = 60*HZ;
return;
}
while (info) {
if (info->timeout < timeout)
timeout = info->timeout;
info = info->next_port;
}
if (!irq)
timeout = timeout / 2;
IRQ_timeout[irq] = timeout ? timeout : 1;
}
 
static int startup(struct async_struct * info)
{
unsigned short ICP;
unsigned long flags;
int retval;
void (*handler)(int, void *, struct pt_regs *);
unsigned long page;
 
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
 
save_flags_cli(flags);
 
if (info->flags & ASYNC_INITIALIZED) {
free_page(page);
restore_flags(flags);
return 0;
}
 
if (!info->port || !info->type) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
free_page(page);
restore_flags(flags);
return 0;
}
if (info->xmit_buf)
free_page(page);
else
info->xmit_buf = (unsigned char *) page;
 
#ifdef SERIAL_DEBUG_OPEN
printk("starting up ttys%d (irq %d)...", info->line, info->irq);
#endif
 
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
if (info->type == PORT_16650) {
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
info->xmit_fifo_size = 1; /* disabled for now */
} else if (info->type == PORT_16550A) {
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
info->xmit_fifo_size = 16;
} else
info->xmit_fifo_size = 1;
 
/*
* At this point there's no way the LSR could still be 0xFF;
* if it is, then bail out, because there's likely no UART
* here.
*/
if (serial_inp(info, UART_LSR) == 0xff) {
restore_flags(flags);
if (suser()) {
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
return 0;
} else
return -ENODEV;
}
/*
* Allocate the IRQ if necessary
*/
if (info->irq && (!IRQ_ports[info->irq] ||
!IRQ_ports[info->irq]->next_port)) {
if (IRQ_ports[info->irq]) {
free_irq(info->irq, NULL);
if (rs_multiport[info->irq].port1)
handler = rs_interrupt_multi;
else
handler = rs_interrupt;
} else
handler = rs_interrupt_single;
 
retval = request_irq(info->irq, handler, IRQ_T(info),
"serial", NULL);
if (retval) {
restore_flags(flags);
if (suser()) {
if (info->tty)
set_bit(TTY_IO_ERROR,
&info->tty->flags);
return 0;
} else
return retval;
}
}
 
/*
* Clear the interrupt registers.
*/
/* (void) serial_inp(info, UART_LSR); */ /* (see above) */
(void) serial_inp(info, UART_RX);
(void) serial_inp(info, UART_IIR);
(void) serial_inp(info, UART_MSR);
 
/*
* Now, initialize the UART
*/
serial_outp(info, UART_LCR, UART_LCR_WLEN8); /* reset DLAB */
if (info->flags & ASYNC_FOURPORT) {
info->MCR = UART_MCR_DTR | UART_MCR_RTS;
info->MCR_noint = UART_MCR_DTR | UART_MCR_OUT1;
} else {
info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
info->MCR_noint = UART_MCR_DTR | UART_MCR_RTS;
}
#if defined(__alpha__) && !defined(CONFIG_PCI)
info->MCR |= UART_MCR_OUT1 | UART_MCR_OUT2;
info->MCR_noint |= UART_MCR_OUT1 | UART_MCR_OUT2;
#endif
if (info->irq == 0)
info->MCR = info->MCR_noint;
serial_outp(info, UART_MCR, info->MCR);
/*
* Finally, enable interrupts
*/
info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI;
serial_outp(info, UART_IER, info->IER); /* enable interrupts */
if (info->flags & ASYNC_FOURPORT) {
/* Enable interrupts on the AST Fourport board */
ICP = (info->port & 0xFE0) | 0x01F;
outb_p(0x80, ICP);
(void) inb_p(ICP);
}
 
/*
* And clear the interrupt registers again for luck.
*/
(void)serial_inp(info, UART_LSR);
(void)serial_inp(info, UART_RX);
(void)serial_inp(info, UART_IIR);
(void)serial_inp(info, UART_MSR);
 
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 
/*
* Insert serial port into IRQ chain.
*/
info->prev_port = 0;
info->next_port = IRQ_ports[info->irq];
if (info->next_port)
info->next_port->prev_port = info;
IRQ_ports[info->irq] = info;
figure_IRQ_timeout(info->irq);
 
/*
* Set up serial timers...
*/
timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
timer_active |= 1 << RS_TIMER;
 
/*
* and set the speed of the serial port
*/
change_speed(info);
 
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
}
 
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static void shutdown(struct async_struct * info)
{
unsigned long flags;
int retval;
 
if (!(info->flags & ASYNC_INITIALIZED))
return;
 
#ifdef SERIAL_DEBUG_OPEN
printk("Shutting down serial port %d (irq %d)....", info->line,
info->irq);
#endif
save_flags_cli(flags); /* Disable interrupts */
 
/*
* clear delta_msr_wait queue to avoid mem leaks: we may free the irq
* here so the queue might never be waken up
*/
wake_up_interruptible(&info->delta_msr_wait);
/*
* First unlink the serial port from the IRQ chain...
*/
if (info->next_port)
info->next_port->prev_port = info->prev_port;
if (info->prev_port)
info->prev_port->next_port = info->next_port;
else
IRQ_ports[info->irq] = info->next_port;
figure_IRQ_timeout(info->irq);
/*
* Free the IRQ, if necessary
*/
if (info->irq && (!IRQ_ports[info->irq] ||
!IRQ_ports[info->irq]->next_port)) {
if (IRQ_ports[info->irq]) {
free_irq(info->irq, NULL);
retval = request_irq(info->irq, rs_interrupt_single,
IRQ_T(info), "serial", NULL);
if (retval)
printk("serial shutdown: request_irq: error %d"
" Couldn't reacquire IRQ.\n", retval);
} else
free_irq(info->irq, NULL);
}
 
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
info->xmit_buf = 0;
}
 
info->IER = 0;
serial_outp(info, UART_IER, 0x00); /* disable all intrs */
if (info->flags & ASYNC_FOURPORT) {
/* reset interrupts on the AST Fourport board */
(void) inb((info->port & 0xFE0) | 0x01F);
}
if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) {
info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
info->MCR_noint &= ~(UART_MCR_DTR|UART_MCR_RTS);
}
serial_outp(info, UART_MCR, info->MCR_noint);
 
/* disable FIFO's */
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX); /* read data port to reset things */
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
restore_flags(flags);
}
 
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
static void change_speed(struct async_struct *info)
{
unsigned short port;
int quot = 0;
unsigned cflag,cval,fcr;
int i;
 
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
if (!(port = info->port))
return;
i = cflag & CBAUD;
if (i & CBAUDEX) {
i &= ~CBAUDEX;
if (i < 1 || i > 2)
info->tty->termios->c_cflag &= ~CBAUDEX;
else
i += 15;
}
if (i == 15) {
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
i += 1;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
i += 2;
if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)
quot = info->custom_divisor;
}
if (quot) {
info->timeout = ((info->xmit_fifo_size*HZ*15*quot) /
info->baud_base) + 2;
} else if (baud_table[i] == 134) {
quot = (2*info->baud_base / 269);
info->timeout = (info->xmit_fifo_size*HZ*30/269) + 2;
} else if (baud_table[i]) {
quot = info->baud_base / baud_table[i];
info->timeout = (info->xmit_fifo_size*HZ*15/baud_table[i]) + 2;
} else {
quot = 0;
info->timeout = 0;
}
if (quot) {
info->MCR |= UART_MCR_DTR;
info->MCR_noint |= UART_MCR_DTR;
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
} else {
info->MCR &= ~UART_MCR_DTR;
info->MCR_noint &= ~UART_MCR_DTR;
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
return;
}
/* byte size and parity */
switch (cflag & CSIZE) {
case CS5: cval = 0x00; break;
case CS6: cval = 0x01; break;
case CS7: cval = 0x02; break;
case CS8: cval = 0x03; break;
default: cval = 0x00; break; /* too keep GCC shut... */
}
if (cflag & CSTOPB) {
cval |= 0x04;
}
if (cflag & PARENB)
cval |= UART_LCR_PARITY;
if (!(cflag & PARODD))
cval |= UART_LCR_EPAR;
if (info->type == PORT_16550A) {
if ((info->baud_base / quot) < 2400)
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1;
else
fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8;
} else if (info->type == PORT_16650) {
/*
* On the 16650, we disable the FIFOs altogether
* because of a design bug in how the implement
* things. We could support it by completely changing
* how we handle the interrupt driver, but not today....
*
* N.B. Because there's no way to set a FIFO trigger
* at 1 char, we'd probably disable at speed below
* 2400 baud anyway...
*/
fcr = 0;
} else
fcr = 0;
/* CTS flow control flag and modem status interrupts */
info->IER &= ~UART_IER_MSI;
if (cflag & CRTSCTS) {
info->flags |= ASYNC_CTS_FLOW;
info->IER |= UART_IER_MSI;
} else
info->flags &= ~ASYNC_CTS_FLOW;
if (cflag & CLOCAL)
info->flags &= ~ASYNC_CHECK_CD;
else {
info->flags |= ASYNC_CHECK_CD;
info->IER |= UART_IER_MSI;
}
serial_out(info, UART_IER, info->IER);
 
/*
* Set up parity check flag
*/
#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
 
info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
if (I_INPCK(info->tty))
info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
if (I_BRKINT(info->tty) || I_PARMRK(info->tty))
info->read_status_mask |= UART_LSR_BI;
info->ignore_status_mask = 0;
#if 0
/* This should be safe, but for some broken bits of hardware... */
if (I_IGNPAR(info->tty)) {
info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
}
#endif
if (I_IGNBRK(info->tty)) {
info->ignore_status_mask |= UART_LSR_BI;
info->read_status_mask |= UART_LSR_BI;
/*
* If we're ignore parity and break indicators, ignore
* overruns too. (For real raw support).
*/
if (I_IGNPAR(info->tty)) {
info->ignore_status_mask |= UART_LSR_OE |
UART_LSR_PE | UART_LSR_FE;
info->read_status_mask |= UART_LSR_OE |
UART_LSR_PE | UART_LSR_FE;
}
}
cli();
serial_outp(info, UART_LCR, cval | UART_LCR_DLAB); /* set DLAB */
serial_outp(info, UART_DLL, quot & 0xff); /* LS of divisor */
serial_outp(info, UART_DLM, quot >> 8); /* MS of divisor */
serial_outp(info, UART_LCR, cval); /* reset DLAB */
serial_outp(info, UART_FCR, fcr); /* set fcr */
sti();
}
 
static void rs_put_char(struct tty_struct *tty, unsigned char ch)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
 
if (serial_paranoia_check(info, tty->device, "rs_put_char"))
return;
 
if (!tty || !info->xmit_buf)
return;
 
save_flags_cli (flags);
if (info->xmit_cnt >= SERIAL_XMIT_SIZE - 1) {
restore_flags(flags);
return;
}
 
info->xmit_buf[info->xmit_head++] = ch;
info->xmit_head &= SERIAL_XMIT_SIZE-1;
info->xmit_cnt++;
restore_flags(flags);
}
 
static void rs_flush_chars(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
return;
 
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!info->xmit_buf)
return;
 
save_flags_cli (flags);
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
restore_flags(flags);
}
 
static int rs_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
int c, total = 0;
struct async_struct *info = (struct async_struct *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_write"))
return 0;
 
if (!tty || !info->xmit_buf || !tmp_buf)
return 0;
if (from_user)
down(&tmp_buf_sem);
save_flags(flags);
while (1) {
cli();
c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0)
break;
 
if (from_user) {
memcpy_fromfs(tmp_buf, buf, c);
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
} else
memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt += c;
restore_flags(flags);
buf += c;
count -= c;
total += c;
}
if (from_user)
up(&tmp_buf_sem);
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped &&
!(info->IER & UART_IER_THRI)) {
info->IER |= UART_IER_THRI;
serial_out(info, UART_IER, info->IER);
}
restore_flags(flags);
return total;
}
 
static int rs_write_room(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
int ret;
if (serial_paranoia_check(info, tty->device, "rs_write_room"))
return 0;
ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
}
 
static int rs_chars_in_buffer(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
return 0;
return info->xmit_cnt;
}
 
static void rs_flush_buffer(struct tty_struct *tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
return;
cli();
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
sti();
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
}
 
/*
* ------------------------------------------------------------
* rs_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static void rs_throttle(struct tty_struct * tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
 
if (serial_paranoia_check(info, tty->device, "rs_throttle"))
return;
if (I_IXOFF(tty))
info->x_char = STOP_CHAR(tty);
 
info->MCR &= ~UART_MCR_RTS;
info->MCR_noint &= ~UART_MCR_RTS;
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
}
 
static void rs_unthrottle(struct tty_struct * tty)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
 
if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
return;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
info->x_char = START_CHAR(tty);
}
info->MCR |= UART_MCR_RTS;
info->MCR_noint |= UART_MCR_RTS;
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
}
 
/*
* ------------------------------------------------------------
* rs_ioctl() and friends
* ------------------------------------------------------------
*/
 
static int get_serial_info(struct async_struct * info,
struct serial_struct * retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.port = info->port;
tmp.irq = info->irq;
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
tmp.hub6 = info->hub6;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
 
static int set_serial_info(struct async_struct * info,
struct serial_struct * new_info)
{
struct serial_struct new_serial;
struct async_struct old_info;
unsigned int i,change_irq,change_port;
int retval = 0;
 
if (!new_info)
return -EFAULT;
memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
 
change_irq = new_serial.irq != info->irq;
change_port = (new_serial.port != info->port) || (new_serial.hub6 != info->hub6);
 
if (!suser()) {
if (change_irq || change_port ||
(new_serial.baud_base != info->baud_base) ||
(new_serial.type != info->type) ||
(new_serial.close_delay != info->close_delay) ||
((new_serial.flags & ~ASYNC_USR_MASK) !=
(info->flags & ~ASYNC_USR_MASK)))
return -EPERM;
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
#if 0
if (new_serial.irq == 2)
new_serial.irq = 9;
 
if ((new_serial.irq > 15) || (new_serial.port > 0xffff) ||
(new_serial.type < PORT_UNKNOWN) || (new_serial.type > PORT_MAX)) {
return -EINVAL;
}
#else
if ((new_serial.irq > 31) || (new_serial.type < PORT_UNKNOWN) ||
(new_serial.type > PORT_MAX)) {
return -EINVAL;
}
#endif
 
/* Make sure address is not already in use */
if (new_serial.type) {
for (i = 0 ; i < NR_PORTS; i++)
if ((info != &rs_table[i]) &&
(rs_table[i].port == new_serial.port) &&
rs_table[i].type)
return -EADDRINUSE;
}
 
if ((change_port || change_irq) && (info->count > 1))
return -EBUSY;
 
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
 
info->baud_base = new_serial.baud_base;
info->flags = ((info->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
info->custom_divisor = new_serial.custom_divisor;
info->type = new_serial.type;
info->close_delay = new_serial.close_delay * HZ/100;
info->closing_wait = new_serial.closing_wait * HZ/100;
 
release_region(info->port,8);
if (change_port || change_irq) {
/*
* We need to shutdown the serial port at the old
* port/irq combination.
*/
shutdown(info);
info->irq = new_serial.irq;
info->port = new_serial.port;
info->hub6 = new_serial.hub6;
}
if(info->type != PORT_UNKNOWN)
request_region(info->port,8,"serial(set)");
 
check_and_exit:
if (!info->port || !info->type)
return 0;
if (info->flags & ASYNC_INITIALIZED) {
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
(old_info.custom_divisor != info->custom_divisor))
change_speed(info);
} else
retval = startup(info);
return retval;
}
 
 
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
static int get_lsr_info(struct async_struct * info, unsigned int *value)
{
unsigned char status;
unsigned int result;
 
cli();
status = serial_in(info, UART_LSR);
sti();
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
put_user(result,value);
return 0;
}
 
 
static int get_modem_info(struct async_struct * info, unsigned int *value)
{
unsigned char control, status;
unsigned int result;
 
control = info->MCR;
cli();
status = serial_in(info, UART_MSR);
sti();
result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
| ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
| ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
| ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
| ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
| ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
put_user(result,value);
return 0;
}
 
static int set_modem_info(struct async_struct * info, unsigned int cmd,
unsigned int *value)
{
int error;
unsigned int arg;
 
error = verify_area(VERIFY_READ, value, sizeof(int));
if (error)
return error;
arg = get_user(value);
switch (cmd) {
case TIOCMBIS:
if (arg & TIOCM_RTS) {
info->MCR |= UART_MCR_RTS;
info->MCR_noint |= UART_MCR_RTS;
}
if (arg & TIOCM_DTR) {
info->MCR |= UART_MCR_DTR;
info->MCR_noint |= UART_MCR_DTR;
}
break;
case TIOCMBIC:
if (arg & TIOCM_RTS) {
info->MCR &= ~UART_MCR_RTS;
info->MCR_noint &= ~UART_MCR_RTS;
}
if (arg & TIOCM_DTR) {
info->MCR &= ~UART_MCR_DTR;
info->MCR_noint &= ~UART_MCR_DTR;
}
break;
case TIOCMSET:
info->MCR = ((info->MCR & ~(UART_MCR_RTS | UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
info->MCR_noint = ((info->MCR_noint
& ~(UART_MCR_RTS | UART_MCR_DTR))
| ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)
| ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0));
break;
default:
return -EINVAL;
}
cli();
serial_out(info, UART_MCR, info->MCR);
sti();
return 0;
}
 
static int do_autoconfig(struct async_struct * info)
{
int retval;
if (!suser())
return -EPERM;
if (info->count > 1)
return -EBUSY;
shutdown(info);
 
cli();
autoconfig(info);
sti();
 
retval = startup(info);
if (retval)
return retval;
return 0;
}
 
 
/*
* This routine sends a break character out the serial port.
*/
static void send_break( struct async_struct * info, int duration)
{
if (!info->port)
return;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + duration;
cli();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) | UART_LCR_SBC);
schedule();
serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC);
sti();
}
 
/*
* This routine returns a bitfield of "wild interrupts". Basically,
* any unclaimed interrupts which is flapping around.
*/
static int check_wild_interrupts(int doprint)
{
int i, mask;
int wild_interrupts = 0;
int irq_lines;
unsigned long timeout;
unsigned long flags;
/* Turn on interrupts (they may be off) */
save_flags(flags); sti();
 
irq_lines = grab_all_interrupts(0);
/*
* Delay for 0.1 seconds -- we use a busy loop since this may
* occur during the bootup sequence
*/
timeout = jiffies+HZ/10;
while (timeout >= jiffies)
;
rs_triggered = 0; /* Reset after letting things settle */
 
timeout = jiffies+HZ/10;
while (timeout >= jiffies)
;
for (i = 0, mask = 1; i < 16; i++, mask <<= 1) {
if ((rs_triggered & (1 << i)) &&
(irq_lines & (1 << i))) {
wild_interrupts |= mask;
if (doprint)
printk("Wild interrupt? (IRQ %d)\n", i);
}
}
free_all_interrupts(irq_lines);
restore_flags(flags);
return wild_interrupts;
}
 
static int get_multiport_struct(struct async_struct * info,
struct serial_multiport_struct *retinfo)
{
struct serial_multiport_struct ret;
struct rs_multiport_struct *multi;
multi = &rs_multiport[info->irq];
 
ret.port_monitor = multi->port_monitor;
ret.port1 = multi->port1;
ret.mask1 = multi->mask1;
ret.match1 = multi->match1;
ret.port2 = multi->port2;
ret.mask2 = multi->mask2;
ret.match2 = multi->match2;
ret.port3 = multi->port3;
ret.mask3 = multi->mask3;
ret.match3 = multi->match3;
ret.port4 = multi->port4;
ret.mask4 = multi->mask4;
ret.match4 = multi->match4;
 
ret.irq = info->irq;
 
memcpy_tofs(retinfo,&ret,sizeof(*retinfo));
return 0;
}
 
static int set_multiport_struct(struct async_struct * info,
struct serial_multiport_struct *in_multi)
{
struct serial_multiport_struct new_multi;
struct rs_multiport_struct *multi;
int was_multi, now_multi;
int retval;
void (*handler)(int, void *, struct pt_regs *);
 
if (!suser())
return -EPERM;
if (!in_multi)
return -EFAULT;
memcpy_fromfs(&new_multi, in_multi,
sizeof(struct serial_multiport_struct));
 
if (new_multi.irq != info->irq || info->irq == 0 ||
!IRQ_ports[info->irq])
return -EINVAL;
 
multi = &rs_multiport[info->irq];
was_multi = (multi->port1 != 0);
multi->port_monitor = new_multi.port_monitor;
if (multi->port1)
release_region(multi->port1,1);
multi->port1 = new_multi.port1;
multi->mask1 = new_multi.mask1;
multi->match1 = new_multi.match1;
if (multi->port1)
request_region(multi->port1,1,"serial(multiport1)");
 
if (multi->port2)
release_region(multi->port2,1);
multi->port2 = new_multi.port2;
multi->mask2 = new_multi.mask2;
multi->match2 = new_multi.match2;
if (multi->port2)
request_region(multi->port2,1,"serial(multiport2)");
 
if (multi->port3)
release_region(multi->port3,1);
multi->port3 = new_multi.port3;
multi->mask3 = new_multi.mask3;
multi->match3 = new_multi.match3;
if (multi->port3)
request_region(multi->port3,1,"serial(multiport3)");
 
if (multi->port4)
release_region(multi->port4,1);
multi->port4 = new_multi.port4;
multi->mask4 = new_multi.mask4;
multi->match4 = new_multi.match4;
if (multi->port4)
request_region(multi->port4,1,"serial(multiport4)");
 
now_multi = (multi->port1 != 0);
if (IRQ_ports[info->irq]->next_port &&
(was_multi != now_multi)) {
free_irq(info->irq, NULL);
if (now_multi)
handler = rs_interrupt_multi;
else
handler = rs_interrupt;
 
retval = request_irq(info->irq, handler, IRQ_T(info),
"serial", NULL);
if (retval) {
printk("Couldn't reallocate serial interrupt "
"driver!!\n");
}
}
 
return 0;
}
 
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
int error;
struct async_struct * info = (struct async_struct *)tty->driver_data;
int retval;
struct async_icount cprev, cnow; /* kernel counter temps */
struct serial_icounter_struct *p_cuser; /* user space */
 
if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
return -ENODEV;
 
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
(cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
if (!arg)
send_break(info, HZ/4); /* 1/4 second */
return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
send_break(info, arg ? arg*(HZ/10) : HZ/4);
return 0;
case TIOCGSOFTCAR:
error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
if (error)
return error;
put_fs_long(C_CLOCAL(tty) ? 1 : 0,
(unsigned long *) arg);
return 0;
case TIOCSSOFTCAR:
error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
if (error)
return error;
arg = get_fs_long((unsigned long *) arg);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCMGET:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(unsigned int));
if (error)
return error;
return get_modem_info(info, (unsigned int *) arg);
case TIOCMBIS:
case TIOCMBIC:
case TIOCMSET:
return set_modem_info(info, cmd, (unsigned int *) arg);
case TIOCGSERIAL:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct serial_struct));
if (error)
return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
error = verify_area(VERIFY_READ, (void *) arg,
sizeof(struct serial_struct));
if (error)
return error;
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERCONFIG:
return do_autoconfig(info);
 
case TIOCSERGWILD:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(int));
if (error)
return error;
put_fs_long(rs_wild_int_mask, (unsigned long *) arg);
return 0;
 
case TIOCSERGETLSR: /* Get line status register */
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(unsigned int));
if (error)
return error;
else
return get_lsr_info(info, (unsigned int *) arg);
 
case TIOCSERSWILD:
if (!suser())
return -EPERM;
error = verify_area(VERIFY_READ, (void *) arg,sizeof(long));
if (error)
return error;
rs_wild_int_mask = get_fs_long((unsigned long *) arg);
if (rs_wild_int_mask < 0)
rs_wild_int_mask = check_wild_interrupts(0);
return 0;
 
case TIOCSERGSTRUCT:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct async_struct));
if (error)
return error;
memcpy_tofs((struct async_struct *) arg,
info, sizeof(struct async_struct));
return 0;
case TIOCSERGETMULTI:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct serial_multiport_struct));
if (error)
return error;
return get_multiport_struct(info,
(struct serial_multiport_struct *) arg);
case TIOCSERSETMULTI:
error = verify_area(VERIFY_READ, (void *) arg,
sizeof(struct serial_multiport_struct));
if (error)
return error;
return set_multiport_struct(info,
(struct serial_multiport_struct *) arg);
/*
* Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
* - mask passed in arg for lines of interest
* (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
* Caller should use TIOCGICOUNT to see which one it was
*/
case TIOCMIWAIT:
cli();
cprev = info->icount; /* note the counters on entry */
sti();
while (1) {
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
if (current->signal & ~current->blocked)
return -ERESTARTSYS;
cli();
cnow = info->icount; /* atomic copy */
sti();
if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd && cnow.cts == cprev.cts)
return -EIO; /* no change => error */
if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) ||
((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) ||
((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) ||
((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) {
return 0;
}
cprev = cnow;
}
/* NOTREACHED */
 
/*
* Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
* Return: write counters to the user passed counter struct
* NB: both 1->0 and 0->1 transitions are counted except for
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct serial_icounter_struct));
if (error)
return error;
cli();
cnow = info->icount;
sti();
p_cuser = (struct serial_icounter_struct *) arg;
put_user(cnow.cts, &p_cuser->cts);
put_user(cnow.dsr, &p_cuser->dsr);
put_user(cnow.rng, &p_cuser->rng);
put_user(cnow.dcd, &p_cuser->dcd);
return 0;
 
default:
return -ENOIOCTLCMD;
}
return 0;
}
 
static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct async_struct *info = (struct async_struct *)tty->driver_data;
 
if ( (tty->termios->c_cflag == old_termios->c_cflag)
&& ( RELEVANT_IFLAG(tty->termios->c_iflag)
== RELEVANT_IFLAG(old_termios->c_iflag)))
return;
 
change_speed(info);
 
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
rs_start(tty);
}
 
#if 0
/*
* No need to wake up processes in open wait, since they
* sample the CLOCAL flag once, and don't recheck it.
* XXX It's not clear whether the current behavior is correct
* or not. Hence, this may change.....
*/
if (!(old_termios->c_cflag & CLOCAL) &&
(tty->termios->c_cflag & CLOCAL))
wake_up_interruptible(&info->open_wait);
#endif
}
 
/*
* ------------------------------------------------------------
* rs_close()
*
* This routine is called when the serial port gets closed. First, we
* wait for the last remaining data to be sent. Then, we unlink its
* async structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
* ------------------------------------------------------------
*/
static void rs_close(struct tty_struct *tty, struct file * filp)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
unsigned long flags;
unsigned long timeout;
 
if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
return;
save_flags_cli (flags);
if (tty_hung_up_p(filp)) {
DBG_CNT("before DEC-hung");
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
#ifdef SERIAL_DEBUG_OPEN
printk("rs_close ttys%d, count = %d\n", info->line, info->count);
#endif
if ((tty->count == 1) && (info->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("rs_close: bad serial port count; tty->count is 1, "
"info->count is %d\n", info->count);
info->count = 1;
}
if (--info->count < 0) {
printk("rs_close: bad serial port count for ttys%d: %d\n",
info->line, info->count);
info->count = 0;
}
if (info->count) {
DBG_CNT("before DEC-2");
MOD_DEC_USE_COUNT;
restore_flags(flags);
return;
}
info->flags |= ASYNC_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if (info->flags & ASYNC_NORMAL_ACTIVE)
info->normal_termios = *tty->termios;
if (info->flags & ASYNC_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
info->IER &= ~UART_IER_RLSI;
info->read_status_mask &= ~UART_LSR_DR;
if (info->flags & ASYNC_INITIALIZED) {
serial_out(info, UART_IER, info->IER);
/*
* Before we drop DTR, make sure the UART transmitter
* has completely drained; this is especially
* important if there is a transmit FIFO!
*/
timeout = jiffies+HZ;
while (!(serial_inp(info, UART_LSR) & UART_LSR_TEMT)) {
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + info->timeout;
schedule();
if (jiffies > timeout)
break;
}
}
shutdown(info);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
info->event = 0;
info->tty = 0;
if (info->blocked_open) {
if (info->close_delay) {
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + info->close_delay;
schedule();
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE|
ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
MOD_DEC_USE_COUNT;
restore_flags(flags);
}
 
/*
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
void rs_hangup(struct tty_struct *tty)
{
struct async_struct * info = (struct async_struct *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "rs_hangup"))
return;
rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE);
info->tty = 0;
wake_up_interruptible(&info->open_wait);
}
 
/*
* ------------------------------------------------------------
* rs_open() and friends
* ------------------------------------------------------------
*/
static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct async_struct *info)
{
struct wait_queue wait = { current, NULL };
int retval;
int do_clocal = 0;
 
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (tty_hung_up_p(filp) ||
(info->flags & ASYNC_CLOSING)) {
if (info->flags & ASYNC_CLOSING)
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
#else
return -EAGAIN;
#endif
}
 
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
if (info->flags & ASYNC_NORMAL_ACTIVE)
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_SESSION_LOCKOUT) &&
(info->session != current->session))
return -EBUSY;
if ((info->flags & ASYNC_CALLOUT_ACTIVE) &&
(info->flags & ASYNC_PGRP_LOCKOUT) &&
(info->pgrp != current->pgrp))
return -EBUSY;
info->flags |= ASYNC_CALLOUT_ACTIVE;
return 0;
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & ASYNC_CALLOUT_ACTIVE)
return -EBUSY;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
 
if (info->flags & ASYNC_CALLOUT_ACTIVE) {
if (info->normal_termios.c_cflag & CLOCAL)
do_clocal = 1;
} else {
if (tty->termios->c_cflag & CLOCAL)
do_clocal = 1;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttys%d, count = %d\n",
info->line, info->count);
#endif
cli();
if (!tty_hung_up_p(filp))
info->count--;
sti();
info->blocked_open++;
while (1) {
cli();
if (!(info->flags & ASYNC_CALLOUT_ACTIVE))
serial_out(info, UART_MCR,
serial_inp(info, UART_MCR) |
(UART_MCR_DTR | UART_MCR_RTS));
sti();
current->state = TASK_INTERRUPTIBLE;
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
if (info->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
#else
retval = -EAGAIN;
#endif
break;
}
if (!(info->flags & ASYNC_CALLOUT_ACTIVE) &&
!(info->flags & ASYNC_CLOSING) &&
(do_clocal || (serial_in(info, UART_MSR) &
UART_MSR_DCD)))
break;
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, info->count);
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
info->line, info->count);
#endif
if (retval)
return retval;
info->flags |= ASYNC_NORMAL_ACTIVE;
return 0;
}
 
/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its async structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
int rs_open(struct tty_struct *tty, struct file * filp)
{
struct async_struct *info;
int retval, line;
unsigned long page;
 
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
info = rs_table + line;
if (serial_paranoia_check(info, tty->device, "rs_open"))
return -ENODEV;
 
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
info->count);
#endif
info->count++;
tty->driver_data = info;
info->tty = tty;
 
if (!tmp_buf) {
page = get_free_page(GFP_KERNEL);
if (!page)
return -ENOMEM;
if (tmp_buf)
free_page(page);
else
tmp_buf = (unsigned char *) page;
}
/*
* Start up serial port
*/
retval = startup(info);
if (retval)
return retval;
 
MOD_INC_USE_COUNT;
retval = block_til_ready(tty, filp, info);
if (retval) {
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open returning after block_til_ready with %d\n",
retval);
#endif
return retval;
}
 
if ((info->count == 1) && (info->flags & ASYNC_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
*tty->termios = info->normal_termios;
else
*tty->termios = info->callout_termios;
change_speed(info);
}
 
info->session = current->session;
info->pgrp = current->pgrp;
 
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open ttys%d successful...", info->line);
#endif
return 0;
}
 
/*
* ---------------------------------------------------------------------
* rs_init() and friends
*
* rs_init() is called at boot-time to initialize the serial driver.
* ---------------------------------------------------------------------
*/
 
/*
* This routine prints out the appropriate serial driver version
* number, and identifies which options were configured into this
* driver.
*/
static void show_serial_version(void)
{
printk(KERN_INFO "%s version %s with", serial_name, serial_version);
#ifdef CONFIG_HUB6
printk(" HUB-6");
#define SERIAL_OPT
#endif
#ifdef SERIAL_OPT
printk(" enabled\n");
#else
printk(" no serial options enabled\n");
#endif
#undef SERIAL_OPT
}
 
/*
* This routine is called by do_auto_irq(); it attempts to determine
* which interrupt a serial port is configured to use. It is not
* fool-proof, but it works a large part of the time.
*/
static int get_auto_irq(struct async_struct *info)
{
unsigned char save_MCR, save_IER, save_ICP=0;
unsigned short ICP=0, port = info->port;
unsigned long timeout;
/*
* Enable interrupts and see who answers
*/
rs_irq_triggered = 0;
cli();
save_IER = serial_inp(info, UART_IER);
save_MCR = serial_inp(info, UART_MCR);
if (info->flags & ASYNC_FOURPORT) {
serial_outp(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
ICP = (port & 0xFE0) | 0x01F;
save_ICP = inb_p(ICP);
outb_p(0x80, ICP);
(void) inb_p(ICP);
} else {
serial_outp(info, UART_MCR,
UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2);
serial_outp(info, UART_IER, 0x0f); /* enable all intrs */
}
sti();
/*
* Next, clear the interrupt registers.
*/
(void)serial_inp(info, UART_LSR);
(void)serial_inp(info, UART_RX);
(void)serial_inp(info, UART_IIR);
(void)serial_inp(info, UART_MSR);
timeout = jiffies+2*HZ/100;
while (timeout >= jiffies) {
if (rs_irq_triggered)
break;
}
/*
* Now check to see if we got any business, and clean up.
*/
cli();
serial_outp(info, UART_IER, save_IER);
serial_outp(info, UART_MCR, save_MCR);
if (info->flags & ASYNC_FOURPORT)
outb_p(save_ICP, ICP);
sti();
return(rs_irq_triggered);
}
 
/*
* Calls get_auto_irq() multiple times, to make sure we don't get
* faked out by random interrupts
*/
static int do_auto_irq(struct async_struct * info)
{
unsigned port = info->port;
int irq_lines = 0;
int irq_try_1 = 0, irq_try_2 = 0;
int retries;
unsigned long flags;
 
if (!port)
return 0;
 
/* Turn on interrupts (they may be off) */
save_flags(flags); sti();
 
irq_lines = grab_all_interrupts(rs_wild_int_mask);
for (retries = 0; retries < 5; retries++) {
if (!irq_try_1)
irq_try_1 = get_auto_irq(info);
if (!irq_try_2)
irq_try_2 = get_auto_irq(info);
if (irq_try_1 && irq_try_2) {
if (irq_try_1 == irq_try_2)
break;
irq_try_1 = irq_try_2 = 0;
}
}
restore_flags(flags);
free_all_interrupts(irq_lines);
return (irq_try_1 == irq_try_2) ? irq_try_1 : 0;
}
 
/*
* This routine is called by rs_init() to initialize a specific serial
* port. It determines what type of UART chip this serial port is
* using: 8250, 16450, 16550, 16550A. The important question is
* whether or not this UART is a 16550A or not, since this will
* determine whether or not we can use its FIFO features or not.
*/
static void autoconfig(struct async_struct * info)
{
unsigned char status1, status2, scratch, scratch2;
unsigned port = info->port;
unsigned long flags;
 
info->type = PORT_UNKNOWN;
if (!port)
return;
 
save_flags_cli (flags);
/*
* Do a simple existence test first; if we fail this, there's
* no point trying anything else.
*
* 0x80 is used as a nonsense port to prevent against false
* positives due to ISA bus float. The assumption is that
* 0x80 is a non-existent port; which should be safe since
* include/asm/io.h also makes this assumption.
*/
scratch = serial_inp(info, UART_IER);
serial_outp(info, UART_IER, 0);
outb(0xff, 0x080);
scratch2 = serial_inp(info, UART_IER);
serial_outp(info, UART_IER, scratch);
if (scratch2) {
restore_flags(flags);
return; /* We failed; there's nothing here */
}
 
/*
* Check to see if a UART is really there. Certain broken
* internal modems based on the Rockwell chipset fail this
* test, because they apparently don't implement the loopback
* test mode. So this test is skipped on the COM 1 through
* COM 4 ports. This *should* be safe, since no board
* manufacturer would be stupid enough to design a board
* that conflicts with COM 1-4 --- we hope!
*/
if (!(info->flags & ASYNC_SKIP_TEST)) {
scratch = serial_inp(info, UART_MCR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | scratch);
scratch2 = serial_inp(info, UART_MSR);
serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A);
status1 = serial_inp(info, UART_MSR) & 0xF0;
serial_outp(info, UART_MCR, scratch);
serial_outp(info, UART_MSR, scratch2);
if (status1 != 0x90) {
restore_flags(flags);
return;
}
}
/*
* If the AUTO_IRQ flag is set, try to do the automatic IRQ
* detection.
*/
if (info->flags & ASYNC_AUTO_IRQ)
info->irq = do_auto_irq(info);
scratch2 = serial_in(info, UART_LCR);
serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
serial_outp(info, UART_EFR, 0); /* EFR is the same as FCR */
serial_outp(info, UART_LCR, scratch2);
serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO);
scratch = serial_in(info, UART_IIR) >> 6;
info->xmit_fifo_size = 1;
switch (scratch) {
case 0:
info->type = PORT_16450;
break;
case 1:
info->type = PORT_UNKNOWN;
break;
case 2:
info->type = PORT_16550;
break;
case 3:
serial_outp(info, UART_LCR, scratch2 | UART_LCR_DLAB);
if (serial_in(info, UART_EFR) == 0) {
info->type = PORT_16650;
info->xmit_fifo_size = 32;
} else {
info->type = PORT_16550A;
info->xmit_fifo_size = 16;
}
serial_outp(info, UART_LCR, scratch2);
break;
}
if (info->type == PORT_16450) {
scratch = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, 0xa5);
status1 = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, 0x5a);
status2 = serial_in(info, UART_SCR);
serial_outp(info, UART_SCR, scratch);
 
if ((status1 != 0xa5) || (status2 != 0x5a))
info->type = PORT_8250;
}
request_region(info->port,8,"serial(auto)");
 
/*
* Reset the UART.
*/
#if defined(__alpha__) && !defined(CONFIG_PCI)
/*
* I wonder what DEC did to the OUT1 and OUT2 lines?
* clearing them results in endless interrupts.
*/
serial_outp(info, UART_MCR, 0x0c);
#else
serial_outp(info, UART_MCR, 0x00);
#endif
serial_outp(info, UART_FCR, (UART_FCR_CLEAR_RCVR |
UART_FCR_CLEAR_XMIT));
(void)serial_in(info, UART_RX);
restore_flags(flags);
}
 
int register_serial(struct serial_struct *req);
void unregister_serial(int line);
 
static struct symbol_table serial_syms = {
#include <linux/symtab_begin.h>
X(register_serial),
X(unregister_serial),
#include <linux/symtab_end.h>
};
 
/*
* The serial driver boot-time initialization code!
*/
int rs_init(void)
{
int i;
struct async_struct * info;
#ifdef CONFIG_ATOMWIDE_SERIAL
extern void atomwide_serial_init (void);
 
atomwide_serial_init ();
#endif
init_bh(SERIAL_BH, do_serial_bh);
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
#ifdef CONFIG_AUTO_IRQ
rs_wild_int_mask = check_wild_interrupts(1);
#endif
 
for (i = 0; i < 32; i++) {
IRQ_ports[i] = 0;
IRQ_timeout[i] = 0;
memset(&rs_multiport[i], 0, sizeof(struct rs_multiport_struct));
}
show_serial_version();
 
/* Initialize the tty_driver structure */
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.name = "ttyS";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = NR_PORTS;
serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
serial_driver.subtype = SERIAL_TYPE_NORMAL;
serial_driver.init_termios = tty_std_termios;
serial_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
serial_driver.flags = TTY_DRIVER_REAL_RAW;
serial_driver.refcount = &serial_refcount;
serial_driver.table = serial_table;
serial_driver.termios = serial_termios;
serial_driver.termios_locked = serial_termios_locked;
 
serial_driver.open = rs_open;
serial_driver.close = rs_close;
serial_driver.write = rs_write;
serial_driver.put_char = rs_put_char;
serial_driver.flush_chars = rs_flush_chars;
serial_driver.write_room = rs_write_room;
serial_driver.chars_in_buffer = rs_chars_in_buffer;
serial_driver.flush_buffer = rs_flush_buffer;
serial_driver.ioctl = rs_ioctl;
serial_driver.throttle = rs_throttle;
serial_driver.unthrottle = rs_unthrottle;
serial_driver.set_termios = rs_set_termios;
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
 
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
callout_driver = serial_driver;
callout_driver.name = "cua";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");
for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
info->magic = SERIAL_MAGIC;
info->line = i;
info->tty = 0;
info->type = PORT_UNKNOWN;
info->custom_divisor = 0;
info->close_delay = 5*HZ/10;
info->closing_wait = 30*HZ;
info->x_char = 0;
info->event = 0;
info->count = 0;
info->blocked_open = 0;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
info->tqueue_hangup.routine = do_serial_hangup;
info->tqueue_hangup.data = info;
info->callout_termios =callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
info->open_wait = 0;
info->close_wait = 0;
info->delta_msr_wait = 0;
info->icount.cts = info->icount.dsr =
info->icount.rng = info->icount.dcd = 0;
info->next_port = 0;
info->prev_port = 0;
#if 0
if (info->irq == 2)
info->irq = 9;
#endif
if (info->type == PORT_UNKNOWN) {
if (!(info->flags & ASYNC_BOOT_AUTOCONF))
continue;
autoconfig(info);
if (info->type == PORT_UNKNOWN)
continue;
}
printk(KERN_INFO "tty%02d%s at 0x%04x (irq = %d)", info->line,
(info->flags & ASYNC_FOURPORT) ? " FourPort" : "",
info->port, info->irq);
switch (info->type) {
case PORT_8250:
printk(" is a 8250\n");
break;
case PORT_16450:
printk(" is a 16450\n");
break;
case PORT_16550:
printk(" is a 16550\n");
break;
case PORT_16550A:
printk(" is a 16550A\n");
break;
case PORT_16650:
printk(" is a 16650\n");
break;
default:
printk("\n");
break;
}
}
register_symtab(&serial_syms);
return 0;
}
 
int register_pre_init_serial (struct serial_struct *req)
{
int i;
struct async_struct *info;
 
for (i = 0; i < NR_PORTS; i++) {
if (rs_table[i].port == req->port)
break;
}
 
if (i == NR_PORTS) {
for (i = 0; i < NR_PORTS; i++)
if (!rs_table[i].port)
break;
}
if (i == NR_PORTS)
return -1;
 
info = &rs_table[i];
if (info->count) {
printk ("Couldn't configure serial #%d (port=%d, irq=%d): "
"device already open\n", i, req->port, req->irq);
return -1;
}
info->irq = req->irq;
info->port = req->port;
info->baud_base = req->baud_base;
serial_outp (info, UART_IER, 0);
return 0;
}
 
/*
* register_serial and unregister_serial allows for serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
int register_serial(struct serial_struct *req)
{
int i;
unsigned long flags;
struct async_struct *info;
 
save_flags_cli (flags);
for (i = 0; i < NR_PORTS; i++) {
if (rs_table[i].port == req->port)
break;
}
if (i == NR_PORTS) {
for (i = 0; i < NR_PORTS; i++)
if ((rs_table[i].type == PORT_UNKNOWN) &&
(rs_table[i].count == 0))
break;
}
if (i == NR_PORTS) {
restore_flags(flags);
return -1;
}
info = &rs_table[i];
if (rs_table[i].count) {
restore_flags(flags);
printk("Couldn't configure serial #%d (port=%d,irq=%d): "
"device already open\n", i, req->port, req->irq);
return -1;
}
info->irq = req->irq;
info->port = req->port;
if (req->baud_base) /* rmk: need this to set the baud rate... */
info->baud_base = req->baud_base;
serial_outp(info, UART_IER, 0); /* rmk: need to make sure port is disabled */
info->flags = req->flags;
autoconfig(info);
if (info->type == PORT_UNKNOWN) {
restore_flags(flags);
printk("register_serial(): autoconfig failed\n");
return -1;
}
printk(KERN_INFO "tty%02d at 0x%04x (irq = %d)", info->line,
info->port, info->irq);
switch (info->type) {
case PORT_8250:
printk(" is a 8250\n"); break;
case PORT_16450:
printk(" is a 16450\n"); break;
case PORT_16550:
printk(" is a 16550\n"); break;
case PORT_16550A:
printk(" is a 16550A\n"); break;
default:
printk("\n"); break;
}
restore_flags(flags);
register_symtab(&serial_syms);
return info->line;
}
 
void unregister_serial(int line)
{
unsigned long flags;
struct async_struct *info = &rs_table[line];
 
save_flags_cli (flags);
if (info->tty)
tty_hangup(info->tty);
info->type = PORT_UNKNOWN;
release_region(info->port,8);
printk(KERN_INFO "tty%02d unloaded\n", info->line);
restore_flags(flags);
}
 
#ifdef MODULE
int init_module(void)
{
return rs_init();
}
 
void cleanup_module(void)
{
unsigned long flags;
int e1, e2;
int i;
 
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
save_flags_cli(flags);
timer_active &= ~(1 << RS_TIMER);
timer_table[RS_TIMER].fn = NULL;
timer_table[RS_TIMER].expires = 0;
if ((e1 = tty_unregister_driver(&serial_driver)))
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
if ((e2 = tty_unregister_driver(&callout_driver)))
printk("SERIAL: failed to unregister callout driver (%d)\n",
e2);
restore_flags(flags);
 
for (i = 0; i < NR_PORTS; i++) {
if (rs_table[i].type != PORT_UNKNOWN)
release_region(rs_table[i].port, 8);
}
if (tmp_buf) {
free_page((unsigned long)tmp_buf);
tmp_buf = NULL;
}
}
#endif /* MODULE */
/kbd_kern.h
0,0 → 1,147
#ifndef _KBD_KERN_H
#define _KBD_KERN_H
 
#include <linux/interrupt.h>
#include <linux/keyboard.h>
 
extern int shift_state;
extern char *func_table[MAX_NR_FUNC];
extern char func_buf[];
extern char *funcbufptr;
extern int funcbufsize, funcbufleft;
 
#ifndef _VT_KERN_H
#include "vt_kern.h"
#endif
 
extern void compute_shiftstate (void);
extern int kbd_struct_init (struct vt *vt, int init);
extern int kbd_ioctl (struct vt *vt, int cmd, unsigned long arg);
extern int kbd_init (void);
 
/*
* kbd->xxx contains the VC-local things (flag settings etc..)
*
* Note: externally visible are LED_SCR, LED_NUM, LED_CAP defined in kd.h
* The code in KDGETLED / KDSETLED depends on the internal and
* external order being the same.
*
* Note: lockstate is used as index in the array key_map.
*/
struct kbd_struct {
 
unsigned char lockstate;
/* 8 modifiers - the names do not have any meaning at all;
they can be associated to arbitrarily chosen keys */
#define VC_SHIFTLOCK KG_SHIFT /* shift lock mode */
#define VC_ALTGRLOCK KG_ALTGR /* altgr lock mode */
#define VC_CTRLLOCK KG_CTRL /* control lock mode */
#define VC_ALTLOCK KG_ALT /* alt lock mode */
#define VC_SHIFTLLOCK KG_SHIFTL /* shiftl lock mode */
#define VC_SHIFTRLOCK KG_SHIFTR /* shiftr lock mode */
#define VC_CTRLLLOCK KG_CTRLL /* ctrll lock mode */
#define VC_CTRLRLOCK KG_CTRLR /* ctrlr lock mode */
unsigned char slockstate; /* for `sticky' Shift, Ctrl, etc. */
 
unsigned char ledmode:2; /* one 2-bit value */
#define LED_SHOW_FLAGS 0 /* traditional state */
#define LED_SHOW_IOCTL 1 /* only change leds upon ioctl */
#define LED_SHOW_MEM 2 /* `heartbeat': peek into memory */
 
unsigned char ledflagstate:3; /* flags, not lights */
unsigned char default_ledflagstate:3;
#define VC_SCROLLOCK 0 /* scroll-lock mode */
#define VC_NUMLOCK 1 /* numeric lock mode */
#define VC_CAPSLOCK 2 /* capslock mode */
 
unsigned char kbdmode:2; /* one 2-bit value */
#define VC_XLATE 0 /* translate keycodes using keymap */
#define VC_MEDIUMRAW 1 /* medium raw (keycode) mode */
#define VC_RAW 2 /* raw (scancode) mode */
#define VC_UNICODE 3 /* Unicode mode */
 
unsigned char modeflags:5;
#define VC_APPLIC 0 /* application key mode */
#define VC_CKMODE 1 /* cursor key mode */
#define VC_REPEAT 2 /* keyboard repeat */
#define VC_CRLF 3 /* 0 - enter sends CR, 1 - enter sends CRLF */
#define VC_META 4 /* 0 - meta, 1 - meta=prefix with ESC */
};
 
extern unsigned char getledstate(void);
extern void setledstate(struct kbd_struct *kbd, unsigned int led);
 
extern int do_poke_blanked_console;
extern struct vt *want_console;
extern struct vt *last_console;
 
extern inline void show_console(void)
{
do_poke_blanked_console = 1;
mark_bh(CONSOLE_BH);
}
 
extern inline void set_console(struct vt *vt)
{
want_console = vt;
mark_bh(CONSOLE_BH);
}
 
extern inline void set_leds(void)
{
mark_bh(KEYBOARD_BH);
}
 
extern inline int vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
return ((kbd->modeflags >> flag) & 1);
}
 
extern inline int vc_kbd_led(struct kbd_struct * kbd, int flag)
{
return ((kbd->ledflagstate >> flag) & 1);
}
 
extern inline void set_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags |= 1 << flag;
}
 
extern inline void set_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate |= 1 << flag;
}
 
extern inline void clr_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags &= ~(1 << flag);
}
 
extern inline void clr_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate &= ~(1 << flag);
}
 
extern inline void chg_vc_kbd_lock(struct kbd_struct * kbd, int flag)
{
kbd->lockstate ^= 1 << flag;
}
 
extern inline void chg_vc_kbd_slock(struct kbd_struct * kbd, int flag)
{
kbd->slockstate ^= 1 << flag;
}
 
extern inline void chg_vc_kbd_mode(struct kbd_struct * kbd, int flag)
{
kbd->modeflags ^= 1 << flag;
}
 
extern inline void chg_vc_kbd_led(struct kbd_struct * kbd, int flag)
{
kbd->ledflagstate ^= 1 << flag;
}
 
#define U(x) ((x) ^ 0xf000)
 
#endif
/diacr.h
0,0 → 1,8
#ifndef _DIACR_H
#define _DIACR_H
#include <linux/kd.h>
 
extern struct kbdiacr accent_table[];
extern unsigned int accent_table_size;
 
#endif /* _DIACR_H */
/Makefile
0,0 → 1,184
#
# Makefile for the kernel character device drivers.
#
# Note! Dependencies are done automagically by 'make dep', which also
# removes any old dependencies. DON'T put your own dependencies here
# unless it's something special (ie not a .c file).
#
# Note 2! The CFLAGS definitions are now inherited from the
# parent makes..
#
 
#
# This file contains the font map for the default font
#
 
FONTMAPFILE = cp437.uni
 
# Links to make
 
LK = conmakehash.c consolemap.c consolemap.h cp437.uni random.c tty_ioctl.c softdog.c
 
all: links first_rule
 
L_TARGET := char.a
M_OBJS :=
L_OBJS := tty_io.o n_tty.o tty_ioctl.o pty.o mem.o random.o
 
# architecture options
 
ifeq ($(CONFIG_SERIAL_TRIO),y)
L_OBJS += trioserial.o
else
ifeq ($(CONFIG_SERIAL_TRIO),m)
M_OBJS += trioserial.o
endif
endif
 
ifeq ($(MACHINE),arc)
ifeq ($(CONFIG_SERIAL),y)
L_OBJS += serial6850.o
else
ifeq ($(CONFIG_SERIAL),m)
M_OBJS += serial6850.o
endif
endif
else
ifeq ($(CONFIG_SERIAL),y)
SL = y
else
ifeq ($(CONFIG_SERIAL),m)
SM = m
endif
endif
endif
 
ifeq ($(MACHINE),arc)
L_OBJS += console.o consoledriver.o keyboard.o keyb_arc.o vc_screen.o selection.o \
defkeymap.o consolemap.o vt.o
MOUSE_OBJS =
endif
 
ifeq ($(MACHINE),a5k)
L_OBJS += console.o consoledriver.o keyboard.o keyb_arc.o vc_screen.o selection.o \
defkeymap.o consolemap.o vt.o
MOUSE_OBJS =
endif
 
ifeq ($(MACHINE),rpc)
L_OBJS += console.o consoledriver.o keyboard.o keyb_ps2.o vc_screen.o selection.o \
defkeymap.o consolemap.o vt.o
ifeq ($(CONFIG_RPCMOUSE),y)
MOUSE_OBJS = mouse_rpc.o
endif
endif
 
ifeq ($(MACHINE),ebsa110)
L_OBJS += console-dummy.o vt-dummy.o
endif
 
ifeq ($(MACHINE),trio)
L_OBJS += console-trio.o vt-dummy.o kbd_trio.o
endif
 
 
# Common dependencies
 
ifeq ($(CONFIG_ATOMWIDE_SERIAL),y)
L_OBJS += serial-atomwide.o
SL=y
else
ifeq ($(CONFIG_ATOMWIDE_SERIAL),m)
M_OBJS += serial-atomwide.o
SM=m
endif
endif
 
ifeq ($(CONFIG_DUALSP_SERIAL),y)
L_OBJS += serial-dualsp.o
SL=y
else
ifeq ($(CONFIG_DUALSP_SERIAL),m)
M_OBJS += serial-dualsp.o
SM=m
endif
endif
 
ifdef SL
LX_OBJS += serial.o
else
ifdef SM
MX_OBJS += serial.o
endif
endif
 
ifeq ($(CONFIG_PRINTER),y)
L_OBJS += lp.o
else
ifeq ($(CONFIG_PRINTER),m)
M_OBJS += lp.o
endif
endif
 
ifeq ($(CONFIG_UMISC),y)
# To support third-party modules, misc.c must reside in the kernel
M = y
endif
 
ifeq ($(CONFIG_MOUSE),y)
M = y
L_OBJS += mouse.o $(MOUSE_OBJS)
else
ifeq ($(CONFIG_MOUSE),m)
MM = m
M_OBJS += mouse.o $(MOUSE_OBJS)
endif
endif
 
ifeq ($(CONFIG_SOFT_WATCHDOG),y)
M = y
L_OBJS += softdog.o
else
ifeq ($(CONFIG_SOFT_WATCHDOG),m)
M_OBJS += softdog.o
MM = m
endif
endif
 
ifdef M
L_OBJS += misc.o
else
ifdef MM
M_OBJS += misc.o
endif
endif
 
include $(TOPDIR)/Rules.make
 
fastdep: links uni_hash.tbl
 
conmakehash: conmakehash.c
$(HOSTCC) -o conmakehash conmakehash.c
 
uni_hash.tbl: $(FONTMAPFILE) conmakehash
./conmakehash $(FONTMAPFILE) > uni_hash.tbl
 
.PHONY: links
links:
-@for f in $(LK); do \
if [ ! -e $$f ]; then \
echo "ln -s ../../../../drivers/char/$$f .";\
ln -s ../../../../drivers/char/$$f .; \
fi; \
done
 
mrproper:
-@for f in $(LK); do \
if [ -L $$f ]; then \
echo $(RM) $$f; \
$(RM) $$f; \
elif [ -f $$f ]; then \
echo not removing $$f; \
fi; \
done
$(RM) conmakehash
/n_tty.c
0,0 → 1,1037
/*
* n_tty.c --- implements the N_TTY line discipline.
*
* This code used to be in tty_io.c, but things are getting hairy
* enough that it made sense to split things off. (The N_TTY
* processing has changed so much that it's hardly recognizable,
* anyway...)
*
* Note that the open routine for N_TTY is guaranteed never to return
* an error. This is because Linux will fall back to setting a line
* to N_TTY if it can not switch to any other line discipline.
*
* Written by Theodore Ts'o, Copyright 1994.
*
* This file also contains code originally written by Linus Torvalds,
* Copyright 1991, 1992, 1993, and by Julian Cowley, Copyright 1994.
*
* This file may be redistributed under the terms of the GNU Public
* License.
*
* Altered for ARM to reduce memory usage.
*/
 
#include <linux/types.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/kd.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
 
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
/* number of characters left in xmit buffer before select has we have room */
#define WAKEUP_CHARS 256
 
/*
* This defines the low- and high-watermarks for throttling and
* unthrottling the TTY driver. These watermarks are used for
* controlling the space in the read buffer.
*/
#define TTY_THRESHOLD_THROTTLE (N_TTY_BUF_SIZE - 128)
#define TTY_THRESHOLD_UNTHROTTLE 128
 
static inline void put_tty_queue(unsigned char c, struct tty_struct *tty)
{
if (tty->read_cnt < N_TTY_BUF_SIZE) {
tty->read_buf[tty->read_head] = c;
tty->read_head = (tty->read_head + 1) & (N_TTY_BUF_SIZE-1);
tty->read_cnt++;
}
}
 
/*
* Flush the input buffer
*/
void n_tty_flush_buffer(struct tty_struct * tty)
{
tty->read_head = tty->read_tail = tty->read_cnt = 0;
tty->canon_head = tty->canon_data = tty->erasing = 0;
memset(&tty->read_flags, 0, sizeof tty->read_flags);
if (!tty->link)
return;
 
if (tty->driver.unthrottle)
(tty->driver.unthrottle)(tty);
if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->read_wait);
}
}
 
/*
* Return number of characters buffered to be delivered to user
*/
int n_tty_chars_in_buffer(struct tty_struct *tty)
{
if (tty->icanon) {
if (!tty->canon_data) return 0;
 
return (tty->canon_head > tty->read_tail) ?
tty->canon_head - tty->read_tail :
tty->canon_head + (N_TTY_BUF_SIZE - tty->read_tail);
}
return tty->read_cnt;
}
 
/*
* Perform OPOST processing. Returns -1 when the output device is
* full and the character must be retried.
*/
static int opost(unsigned char c, struct tty_struct *tty)
{
int space, spaces;
 
space = tty->driver.write_room(tty);
if (!space)
return -1;
 
if (O_OPOST(tty)) {
switch (c) {
case '\n':
if (O_ONLRET(tty))
tty->column = 0;
if (O_ONLCR(tty)) {
if (space < 2)
return -1;
tty->driver.put_char(tty, '\r');
tty->column = 0;
}
tty->canon_column = tty->column;
break;
case '\r':
if (O_ONOCR(tty) && tty->column == 0)
return 0;
if (O_OCRNL(tty)) {
c = '\n';
if (O_ONLRET(tty))
tty->canon_column = tty->column = 0;
break;
}
tty->canon_column = tty->column = 0;
break;
case '\t':
spaces = 8 - (tty->column & 7);
if (O_TABDLY(tty) == XTABS) {
if (space < spaces)
return -1;
tty->column += spaces;
tty->driver.write(tty, 0, " ", spaces);
return 0;
}
tty->column += spaces;
break;
case '\b':
if (tty->column > 0)
tty->column--;
break;
default:
if (O_OLCUC(tty))
c = toupper(c);
if (!iscntrl(c))
tty->column++;
break;
}
}
tty->driver.put_char(tty, c);
return 0;
}
 
static inline void put_char(unsigned char c, struct tty_struct *tty)
{
tty->driver.put_char(tty, c);
}
 
/* Must be called only when L_ECHO(tty) is true. */
 
static void echo_char(unsigned char c, struct tty_struct *tty)
{
if (L_ECHOCTL(tty) && iscntrl(c) && c != '\t') {
put_char('^', tty);
put_char(c ^ 0100, tty);
tty->column += 2;
} else
opost(c, tty);
}
 
static inline void finish_erasing(struct tty_struct *tty)
{
if (tty->erasing) {
put_char('/', tty);
tty->column += 2;
tty->erasing = 0;
}
}
 
static void eraser(unsigned char c, struct tty_struct *tty)
{
enum { ERASE, WERASE, KILL } kill_type;
int head, seen_alnums;
 
if (tty->read_head == tty->canon_head) {
/* opost('\a', tty); */ /* what do you think? */
return;
}
if (c == ERASE_CHAR(tty))
kill_type = ERASE;
else if (c == WERASE_CHAR(tty))
kill_type = WERASE;
else {
if (!L_ECHO(tty)) {
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
return;
}
if (!L_ECHOK(tty) || !L_ECHOKE(tty) || !L_ECHOE(tty)) {
tty->read_cnt -= ((tty->read_head - tty->canon_head) &
(N_TTY_BUF_SIZE - 1));
tty->read_head = tty->canon_head;
finish_erasing(tty);
echo_char(KILL_CHAR(tty), tty);
/* Add a newline if ECHOK is on and ECHOKE is off. */
if (L_ECHOK(tty))
opost('\n', tty);
return;
}
kill_type = KILL;
}
 
seen_alnums = 0;
while (tty->read_head != tty->canon_head) {
head = (tty->read_head - 1) & (N_TTY_BUF_SIZE-1);
c = tty->read_buf[head];
if (kill_type == WERASE) {
/* Equivalent to BSD's ALTWERASE. */
if (isalnum(c) || c == '_')
seen_alnums++;
else if (seen_alnums)
break;
}
tty->read_head = head;
tty->read_cnt--;
if (L_ECHO(tty)) {
if (L_ECHOPRT(tty)) {
if (!tty->erasing) {
put_char('\\', tty);
tty->column++;
tty->erasing = 1;
}
echo_char(c, tty);
} else if (kill_type == ERASE && !L_ECHOE(tty)) {
echo_char(ERASE_CHAR(tty), tty);
} else if (c == '\t') {
unsigned int col = tty->canon_column;
unsigned long tail = tty->canon_head;
 
/* Find the column of the last char. */
while (tail != tty->read_head) {
c = tty->read_buf[tail];
if (c == '\t')
col = (col | 7) + 1;
else if (iscntrl(c)) {
if (L_ECHOCTL(tty))
col += 2;
} else
col++;
tail = (tail+1) & (N_TTY_BUF_SIZE-1);
}
 
/* should never happen */
if (tty->column > 0x80000000)
tty->column = 0;
 
/* Now backup to that column. */
while (tty->column > col) {
/* Can't use opost here. */
put_char('\b', tty);
if (tty->column > 0)
tty->column--;
}
} else {
if (iscntrl(c) && L_ECHOCTL(tty)) {
put_char('\b', tty);
put_char(' ', tty);
put_char('\b', tty);
if (tty->column > 0)
tty->column--;
}
if (!iscntrl(c) || L_ECHOCTL(tty)) {
put_char('\b', tty);
put_char(' ', tty);
put_char('\b', tty);
if (tty->column > 0)
tty->column--;
}
}
}
if (kill_type == ERASE)
break;
}
if (tty->read_head == tty->canon_head)
finish_erasing(tty);
}
 
static inline void isig(int sig, struct tty_struct *tty, int flush)
{
if (tty->pgrp > 0)
kill_pg(tty->pgrp, sig, 1);
if (flush || !L_NOFLSH(tty)) {
n_tty_flush_buffer(tty);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
}
}
 
static inline void n_tty_receive_break(struct tty_struct *tty)
{
if (I_IGNBRK(tty))
return;
if (I_BRKINT(tty)) {
isig(SIGINT, tty, 1);
return;
}
if (I_PARMRK(tty)) {
put_tty_queue('\377', tty);
put_tty_queue('\0', tty);
}
put_tty_queue('\0', tty);
wake_up_interruptible(&tty->read_wait);
}
 
static inline void n_tty_receive_overrun(struct tty_struct *tty)
{
char buf[64];
 
tty->num_overrun++;
if (tty->overrun_time < (jiffies - HZ)) {
printk("%s: %d input overrun(s)\n", _tty_name(tty, buf),
tty->num_overrun);
tty->overrun_time = jiffies;
tty->num_overrun = 0;
}
}
 
static inline void n_tty_receive_parity_error(struct tty_struct *tty,
unsigned char c)
{
if (I_IGNPAR(tty)) {
return;
}
if (I_PARMRK(tty)) {
put_tty_queue('\377', tty);
put_tty_queue('\0', tty);
put_tty_queue(c, tty);
} else if (I_INPCK(tty))
put_tty_queue('\0', tty);
else
put_tty_queue(c, tty);
wake_up_interruptible(&tty->read_wait);
}
 
static inline void n_tty_receive_char(struct tty_struct *tty, unsigned char c)
{
if (tty->raw) {
put_tty_queue(c, tty);
return;
}
if (tty->stopped && I_IXON(tty) && I_IXANY(tty)) {
start_tty(tty);
return;
}
if (I_ISTRIP(tty))
c &= 0x7f;
if (I_IUCLC(tty) && L_IEXTEN(tty))
c=tolower(c);
 
if (tty->closing) {
if (I_IXON(tty)) {
if (c == START_CHAR(tty))
start_tty(tty);
else if (c == STOP_CHAR(tty))
stop_tty(tty);
}
return;
}
 
/*
* If the previous character was LNEXT, or we know that this
* character is not one of the characters that we'll have to
* handle specially, do shortcut processing to speed things
* up.
*/
if (!test_bit(c, &tty->process_char_map) || tty->lnext) {
finish_erasing(tty);
tty->lnext = 0;
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty); /* beep if no space */
return;
}
/* Record the column of first canon char. */
if (tty->canon_head == tty->read_head)
tty->canon_column = tty->column;
echo_char(c, tty);
}
if (I_PARMRK(tty) && c == (unsigned char) '\377')
put_tty_queue(c, tty);
put_tty_queue(c, tty);
return;
}
if (c == '\r') {
if (I_IGNCR(tty))
return;
if (I_ICRNL(tty))
c = '\n';
} else if (c == '\n' && I_INLCR(tty))
c = '\r';
if (I_IXON(tty)) {
if (c == START_CHAR(tty)) {
start_tty(tty);
return;
}
if (c == STOP_CHAR(tty)) {
stop_tty(tty);
return;
}
}
if (L_ISIG(tty)) {
int signal;
signal = SIGINT;
if (c == INTR_CHAR(tty))
goto send_signal;
signal = SIGQUIT;
if (c == QUIT_CHAR(tty))
goto send_signal;
signal = SIGTSTP;
if (c == SUSP_CHAR(tty)) {
send_signal:
isig(signal, tty, 0);
return;
}
}
if (L_ICANON(tty)) {
if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
(c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
eraser(c, tty);
return;
}
if (c == LNEXT_CHAR(tty) && L_IEXTEN(tty)) {
tty->lnext = 1;
if (L_ECHO(tty)) {
finish_erasing(tty);
if (L_ECHOCTL(tty)) {
put_char('^', tty);
put_char('\b', tty);
}
}
return;
}
if (c == REPRINT_CHAR(tty) && L_ECHO(tty) &&
L_IEXTEN(tty)) {
unsigned long tail = tty->canon_head;
 
finish_erasing(tty);
echo_char(c, tty);
opost('\n', tty);
while (tail != tty->read_head) {
echo_char(tty->read_buf[tail], tty);
tail = (tail+1) & (N_TTY_BUF_SIZE-1);
}
return;
}
if (c == '\n') {
if (L_ECHO(tty) || L_ECHONL(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty);
return;
}
opost('\n', tty);
}
goto handle_newline;
}
if (c == EOF_CHAR(tty)) {
if (tty->canon_head != tty->read_head)
set_bit(TTY_PUSH, &tty->flags);
c = __DISABLED_CHAR;
goto handle_newline;
}
if ((c == EOL_CHAR(tty)) ||
(c == EOL2_CHAR(tty) && L_IEXTEN(tty))) {
/*
* XXX are EOL_CHAR and EOL2_CHAR echoed?!?
*/
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty);
return;
}
/* Record the column of first canon char. */
if (tty->canon_head == tty->read_head)
tty->canon_column = tty->column;
echo_char(c, tty);
}
/*
* XXX does PARMRK doubling happen for
* EOL_CHAR and EOL2_CHAR?
*/
if (I_PARMRK(tty) && c == (unsigned char) '\377')
put_tty_queue(c, tty);
 
handle_newline:
set_bit(tty->read_head, &tty->read_flags);
put_tty_queue(c, tty);
tty->canon_head = tty->read_head;
tty->canon_data++;
if (tty->fasync)
kill_fasync(tty->fasync, SIGIO);
if (tty->read_wait)
wake_up_interruptible(&tty->read_wait);
return;
}
}
finish_erasing(tty);
if (L_ECHO(tty)) {
if (tty->read_cnt >= N_TTY_BUF_SIZE-1) {
put_char('\a', tty); /* beep if no space */
return;
}
if (c == '\n')
opost('\n', tty);
else {
/* Record the column of first canon char. */
if (tty->canon_head == tty->read_head)
tty->canon_column = tty->column;
echo_char(c, tty);
}
}
 
if (I_PARMRK(tty) && c == (unsigned char) '\377')
put_tty_queue(c, tty);
 
put_tty_queue(c, tty);
}
 
static void n_tty_receive_buf(struct tty_struct *tty, const unsigned char *cp,
char *fp, int count)
{
const unsigned char *p;
char *f, flags = 0;
int i;
 
if (!tty->read_buf)
return;
 
if (tty->real_raw) {
i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head));
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
cp += i;
count -= i;
 
i = MIN(count, MIN(N_TTY_BUF_SIZE - tty->read_cnt,
N_TTY_BUF_SIZE - tty->read_head));
memcpy(tty->read_buf + tty->read_head, cp, i);
tty->read_head = (tty->read_head + i) & (N_TTY_BUF_SIZE-1);
tty->read_cnt += i;
} else {
for (i=count, p = cp, f = fp; i; i--, p++) {
if (f)
flags = *f++;
switch (flags) {
case TTY_NORMAL:
n_tty_receive_char(tty, *p);
break;
case TTY_BREAK:
n_tty_receive_break(tty);
break;
case TTY_PARITY:
case TTY_FRAME:
n_tty_receive_parity_error(tty, *p);
break;
case TTY_OVERRUN:
n_tty_receive_overrun(tty);
break;
default:
printk("%s: unknown flag %d\n", tty_name(tty),
flags);
break;
}
}
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
}
 
if (!tty->icanon && (tty->read_cnt >= tty->minimum_to_wake)) {
if (tty->fasync)
kill_fasync(tty->fasync, SIGIO);
if (tty->read_wait)
wake_up_interruptible(&tty->read_wait);
}
 
if ((tty->read_cnt >= TTY_THRESHOLD_THROTTLE) &&
tty->driver.throttle &&
!set_bit(TTY_THROTTLED, &tty->flags))
tty->driver.throttle(tty);
}
 
static int n_tty_receive_room(struct tty_struct *tty)
{
int left = N_TTY_BUF_SIZE - tty->read_cnt - 1;
 
/*
* If we are doing input canonicalization, and there are no
* pending newlines, let characters through without limit, so
* that erase characters will be handled. Other excess
* characters will be beeped.
*/
if (tty->icanon && !tty->canon_data)
return N_TTY_BUF_SIZE;
 
if (left > 0)
return left;
return 0;
}
 
int is_ignored(int sig)
{
return ((current->blocked & (1<<(sig-1))) ||
(current->sig->action[sig-1].sa_handler == SIG_IGN));
}
 
static void n_tty_set_termios(struct tty_struct *tty, struct termios * old)
{
if (!tty)
return;
tty->icanon = (L_ICANON(tty) != 0);
if (I_ISTRIP(tty) || I_IUCLC(tty) || I_IGNCR(tty) ||
I_ICRNL(tty) || I_INLCR(tty) || L_ICANON(tty) ||
I_IXON(tty) || L_ISIG(tty) || L_ECHO(tty) ||
I_PARMRK(tty)) {
cli();
memset(tty->process_char_map, 0, 256/8);
 
if (I_IGNCR(tty) || I_ICRNL(tty))
set_bit('\r', &tty->process_char_map);
if (I_INLCR(tty))
set_bit('\n', &tty->process_char_map);
 
if (L_ICANON(tty)) {
set_bit(ERASE_CHAR(tty), &tty->process_char_map);
set_bit(KILL_CHAR(tty), &tty->process_char_map);
set_bit(EOF_CHAR(tty), &tty->process_char_map);
set_bit('\n', &tty->process_char_map);
set_bit(EOL_CHAR(tty), &tty->process_char_map);
if (L_IEXTEN(tty)) {
set_bit(WERASE_CHAR(tty),
&tty->process_char_map);
set_bit(LNEXT_CHAR(tty),
&tty->process_char_map);
set_bit(EOL2_CHAR(tty),
&tty->process_char_map);
if (L_ECHO(tty))
set_bit(REPRINT_CHAR(tty),
&tty->process_char_map);
}
}
if (I_IXON(tty)) {
set_bit(START_CHAR(tty), &tty->process_char_map);
set_bit(STOP_CHAR(tty), &tty->process_char_map);
}
if (L_ISIG(tty)) {
set_bit(INTR_CHAR(tty), &tty->process_char_map);
set_bit(QUIT_CHAR(tty), &tty->process_char_map);
set_bit(SUSP_CHAR(tty), &tty->process_char_map);
}
clear_bit(__DISABLED_CHAR, &tty->process_char_map);
sti();
tty->raw = 0;
tty->real_raw = 0;
} else {
tty->raw = 1;
if ((I_IGNBRK(tty) || (!I_BRKINT(tty) && !I_PARMRK(tty))) &&
(I_IGNPAR(tty) || !I_INPCK(tty)) &&
(tty->driver.flags & TTY_DRIVER_REAL_RAW))
tty->real_raw = 1;
else
tty->real_raw = 0;
}
}
 
static void n_tty_close(struct tty_struct *tty)
{
n_tty_flush_buffer(tty);
if (tty->read_buf) {
kfree_s (tty->read_buf, N_TTY_BUF_SIZE);
tty->read_buf = 0;
}
}
 
static int n_tty_open(struct tty_struct *tty)
{
if (!tty)
return -EINVAL;
 
if (!tty->read_buf) {
tty->read_buf = (unsigned char *)
kmalloc (N_TTY_BUF_SIZE, intr_count ? GFP_ATOMIC : GFP_KERNEL);
if (!tty->read_buf)
return -ENOMEM;
}
memset(tty->read_buf, 0, N_TTY_BUF_SIZE);
tty->read_head = tty->read_tail = tty->read_cnt = 0;
tty->canon_head = tty->canon_data = tty->erasing = 0;
tty->column = 0;
memset(tty->read_flags, 0, sizeof(tty->read_flags));
n_tty_set_termios(tty, 0);
tty->minimum_to_wake = 1;
tty->closing = 0;
return 0;
}
 
static inline int input_available_p(struct tty_struct *tty, int amt)
{
if (L_ICANON(tty)) {
if (tty->canon_data)
return 1;
} else if (tty->read_cnt >= (amt ? amt : 1))
return 1;
 
return 0;
}
 
/*
* Helper function to speed up read_chan. It is only called when
* ICANON is off; it copies characters straight from the tty queue to
* user space directly. It can be profitably called twice; once to
* drain the space from the tail pointer to the (physical) end of the
* buffer, and once to drain the space from the (physical) beginning of
* the buffer to head pointer.
*/
static inline void copy_from_read_buf(struct tty_struct *tty,
unsigned char **b,
unsigned int *nr)
 
{
int n;
 
n = MIN(*nr, MIN(tty->read_cnt, N_TTY_BUF_SIZE - tty->read_tail));
if (!n)
return;
memcpy_tofs(*b, &tty->read_buf[tty->read_tail], n);
tty->read_tail = (tty->read_tail + n) & (N_TTY_BUF_SIZE-1);
tty->read_cnt -= n;
*b += n;
*nr -= n;
}
 
static int read_chan(struct tty_struct *tty, struct file *file,
unsigned char *buf, unsigned int nr)
{
struct wait_queue wait = { current, NULL };
int c;
unsigned char *b = buf;
int minimum, time;
int retval = 0;
int size;
 
do_it_again:
 
if (!tty->read_buf) {
printk("n_tty_read_chan: called with read_buf == NULL?!?\n");
return -EIO;
}
 
/* Job control check -- must be done at start and after
every sleep (POSIX.1 7.1.1.4). */
/* NOTE: not yet done after every sleep pending a thorough
check of the logic of this change. -- jlc */
/* don't stop on /dev/console */
if (file->f_inode->i_rdev != CONSOLE_DEV &&
current->tty == tty) {
if (tty->pgrp <= 0)
printk("read_chan: tty->pgrp <= 0!\n");
else if (current->pgrp != tty->pgrp) {
if (is_ignored(SIGTTIN) ||
is_orphaned_pgrp(current->pgrp))
return -EIO;
kill_pg(current->pgrp, SIGTTIN, 1);
return -ERESTARTSYS;
}
}
 
if (L_ICANON(tty)) {
minimum = time = 0;
current->timeout = (unsigned long) -1;
} else {
time = (HZ / 10) * TIME_CHAR(tty);
minimum = MIN_CHAR(tty);
if (minimum) {
current->timeout = (unsigned long) -1;
if (time)
tty->minimum_to_wake = 1;
else if (!waitqueue_active(&tty->read_wait) ||
(tty->minimum_to_wake > minimum))
tty->minimum_to_wake = minimum;
} else {
if (time) {
current->timeout = time + jiffies;
time = 0;
} else
current->timeout = 0;
tty->minimum_to_wake = minimum = 1;
}
}
 
add_wait_queue(&tty->read_wait, &wait);
while (1) {
/* First test for status change. */
if (tty->packet && tty->link->ctrl_status) {
if (b != buf)
break;
put_user(tty->link->ctrl_status, b++);
tty->link->ctrl_status = 0;
break;
}
/* This statement must be first before checking for input
so that any interrupt will set the state back to
TASK_RUNNING. */
current->state = TASK_INTERRUPTIBLE;
if (((minimum - (b - buf)) < tty->minimum_to_wake) &&
((minimum - (b - buf)) >= 1))
tty->minimum_to_wake = (minimum - (b - buf));
if (!input_available_p(tty, 0)) {
if (tty->flags & (1 << TTY_OTHER_CLOSED)) {
retval = -EIO;
break;
}
if (tty_hung_up_p(file))
break;
if (!current->timeout)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
schedule();
continue;
}
current->state = TASK_RUNNING;
 
/* Deal with packet mode. */
if (tty->packet && b == buf) {
put_user(TIOCPKT_DATA, b++);
nr--;
}
 
if (L_ICANON(tty)) {
while (1) {
int eol;
 
disable_bh(TQUEUE_BH);
if (!tty->read_cnt) {
enable_bh(TQUEUE_BH);
break;
}
eol = clear_bit(tty->read_tail,
&tty->read_flags);
c = tty->read_buf[tty->read_tail];
tty->read_tail = ((tty->read_tail+1) &
(N_TTY_BUF_SIZE-1));
tty->read_cnt--;
enable_bh(TQUEUE_BH);
if (!eol) {
put_user(c, b++);
if (--nr)
continue;
break;
}
if (--tty->canon_data < 0) {
tty->canon_data = 0;
}
if (c != __DISABLED_CHAR) {
put_user(c, b++);
nr--;
}
break;
}
} else {
disable_bh(TQUEUE_BH);
copy_from_read_buf(tty, &b, &nr);
copy_from_read_buf(tty, &b, &nr);
enable_bh(TQUEUE_BH);
}
 
/* If there is enough space in the read buffer now, let the
low-level driver know. */
if (tty->driver.unthrottle &&
(tty->read_cnt <= TTY_THRESHOLD_UNTHROTTLE)
&& clear_bit(TTY_THROTTLED, &tty->flags))
tty->driver.unthrottle(tty);
 
if (b - buf >= minimum || !nr)
break;
if (time)
current->timeout = time + jiffies;
}
remove_wait_queue(&tty->read_wait, &wait);
 
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = minimum;
 
current->state = TASK_RUNNING;
current->timeout = 0;
size = b - buf;
if (size && nr)
clear_bit(TTY_PUSH, &tty->flags);
if (!size && clear_bit(TTY_PUSH, &tty->flags))
goto do_it_again;
if (!size && !retval)
clear_bit(TTY_PUSH, &tty->flags);
return (size ? size : retval);
}
 
static int write_chan(struct tty_struct * tty, struct file * file,
const unsigned char * buf, unsigned int nr)
{
struct wait_queue wait = { current, NULL };
int c;
const unsigned char *b = buf;
int retval = 0;
 
/* Job control check -- must be done at start (POSIX.1 7.1.1.4). */
if (L_TOSTOP(tty) && file->f_inode->i_rdev != CONSOLE_DEV) {
retval = tty_check_change(tty);
if (retval)
return retval;
}
 
add_wait_queue(&tty->write_wait, &wait);
while (1) {
current->state = TASK_INTERRUPTIBLE;
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
if (tty_hung_up_p(file) || (tty->link && !tty->link->count)) {
retval = -EIO;
break;
}
if (O_OPOST(tty)) {
while (nr > 0) {
c = get_user(b);
if (opost(c, tty) < 0)
break;
b++; nr--;
}
if (tty->driver.flush_chars)
tty->driver.flush_chars(tty);
} else {
c = tty->driver.write(tty, 1, b, nr);
b += c;
nr -= c;
}
if (!nr)
break;
if (file->f_flags & O_NONBLOCK) {
retval = -EAGAIN;
break;
}
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&tty->write_wait, &wait);
return (b - buf) ? b - buf : retval;
}
 
static int normal_select(struct tty_struct * tty, struct inode * inode,
struct file * file, int sel_type, select_table *wait)
{
switch (sel_type) {
case SEL_IN:
if (input_available_p(tty, TIME_CHAR(tty) ? 0 :
MIN_CHAR(tty)))
return 1;
/* fall through */
case SEL_EX:
if (tty->packet && tty->link->ctrl_status)
return 1;
if (tty->flags & (1 << TTY_OTHER_CLOSED))
return 1;
if (tty_hung_up_p(file))
return 1;
if (!waitqueue_active(&tty->read_wait)) {
if (MIN_CHAR(tty) && !TIME_CHAR(tty))
tty->minimum_to_wake = MIN_CHAR(tty);
else
tty->minimum_to_wake = 1;
}
select_wait(&tty->read_wait, wait);
return 0;
case SEL_OUT:
if (tty->driver.chars_in_buffer(tty) < WAKEUP_CHARS)
return 1;
select_wait(&tty->write_wait, wait);
return 0;
}
return 0;
}
 
struct tty_ldisc tty_ldisc_N_TTY = {
TTY_LDISC_MAGIC, /* magic */
0, /* num */
0, /* flags */
n_tty_open, /* open */
n_tty_close, /* close */
n_tty_flush_buffer, /* flush_buffer */
n_tty_chars_in_buffer, /* chars_in_buffer */
read_chan, /* read */
write_chan, /* write */
n_tty_ioctl, /* ioctl */
n_tty_set_termios, /* set_termios */
normal_select, /* select */
n_tty_receive_buf, /* receive_buf */
n_tty_receive_room, /* receive_room */
0 /* write_wakeup */
};
 
/kbd_trio.c
0,0 → 1,5
 
int kbd_init()
{
return 0;
}
/uni_hash.tbl
0,0 → 1,89
/*
* uni_hash.tbl
*
* Do not edit this file; it was automatically generated by
*
* conmakehash cp437.uni > uni_hash.tbl
*
*/
 
#include <linux/types.h>
#include <linux/kd.h>
 
static u8 dfont_unicount[256] =
{
1, 1, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 2, 1, 1, 1, 1, 1,
1, 1, 1, 1, 2, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 5, 1, 2, 1, 4, 1, 1,
1, 5, 1, 2, 1, 1, 1, 5,
1, 1, 2, 1, 1, 4, 1, 1,
1, 2, 1, 1, 1, 1, 1, 2,
1, 2, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2,
1, 1, 1, 1, 1, 1, 1, 1,
2, 2, 1, 1, 2, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 2,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 2, 1, 1, 1, 1, 2, 1,
2, 1, 2, 1, 1, 2, 1, 1,
1, 1, 1, 1, 1, 1, 1, 1,
1, 1, 1, 1, 1, 1, 2, 1
};
 
static u16 dfont_unitable[297] =
{
0x0000, 0x263a, 0x263b, 0x2665, 0x2666, 0x25c6, 0x2663, 0x2660,
0x2022, 0x25d8, 0x25cb, 0x25d9, 0x2642, 0x2640, 0x266a, 0x266b,
0x263c, 0x25b6, 0x25ba, 0x25c0, 0x25c4, 0x2195, 0x203c, 0x00b6,
0x00a7, 0x25ac, 0x21a8, 0x2191, 0x2193, 0x2192, 0x2190, 0x221f,
0x2194, 0x25b2, 0x25bc, 0x0020, 0x0021, 0x0022, 0x00a8, 0x0023,
0x0024, 0x0025, 0x0026, 0x0027, 0x0028, 0x0029, 0x002a, 0x002b,
0x002c, 0x00b8, 0x002d, 0x00ad, 0x002e, 0x002f, 0x0030, 0x0031,
0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037, 0x0038, 0x0039,
0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f, 0x0040, 0x0041,
0x00c0, 0x00c1, 0x00c2, 0x00c3, 0x0042, 0x0043, 0x00a9, 0x0044,
0x0045, 0x00c8, 0x00ca, 0x00cb, 0x0046, 0x0047, 0x0048, 0x0049,
0x00cc, 0x00cd, 0x00ce, 0x00cf, 0x004a, 0x004b, 0x212a, 0x004c,
0x004d, 0x004e, 0x004f, 0x00d2, 0x00d3, 0x00d4, 0x00d5, 0x0050,
0x0051, 0x0052, 0x00ae, 0x0053, 0x0054, 0x0055, 0x00d9, 0x00da,
0x00db, 0x0056, 0x0057, 0x0058, 0x0059, 0x00dd, 0x005a, 0x005b,
0x005c, 0x005d, 0x005e, 0x005f, 0xf804, 0x0060, 0x0061, 0x00e3,
0x0062, 0x0063, 0x0064, 0x0065, 0x0066, 0x0067, 0x0068, 0x0069,
0x006a, 0x006b, 0x006c, 0x006d, 0x006e, 0x006f, 0x00f5, 0x0070,
0x0071, 0x0072, 0x0073, 0x0074, 0x0075, 0x0076, 0x0077, 0x0078,
0x00d7, 0x0079, 0x00fd, 0x007a, 0x007b, 0x007c, 0x00a5, 0x007d,
0x007e, 0x2302, 0x00c7, 0x00fc, 0x00e9, 0x00e2, 0x00e4, 0x00e0,
0x00e5, 0x00e7, 0x00ea, 0x00eb, 0x00e8, 0x00ef, 0x00ee, 0x00ec,
0x00c4, 0x00c5, 0x212b, 0x00c9, 0x00e6, 0x00c6, 0x00f4, 0x00f6,
0x00f2, 0x00fb, 0x00f9, 0x00ff, 0x00d6, 0x00dc, 0x00a2, 0x00a3,
0x00a5, 0x20a7, 0x0192, 0x00e1, 0x00ed, 0x00f3, 0x00fa, 0x00f1,
0x00d1, 0x00aa, 0x00ba, 0x00bf, 0x2310, 0x00ac, 0x00bd, 0x00bc,
0x00a1, 0x00ab, 0x00bb, 0x2591, 0x2592, 0x2593, 0x2502, 0x2524,
0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255d,
0x255c, 0x255b, 0x2510, 0x2514, 0x2534, 0x252c, 0x251c, 0x2500,
0x253c, 0x255e, 0x255f, 0x255a, 0x2554, 0x2569, 0x2566, 0x2560,
0x2550, 0x256c, 0x2567, 0x2568, 0x2564, 0x2565, 0x2559, 0x2558,
0x2552, 0x2553, 0x256b, 0x256a, 0x2518, 0x250c, 0x2588, 0x2584,
0x258c, 0x2590, 0x2580, 0x03b1, 0x03b2, 0x00df, 0x0393, 0x03c0,
0x03a3, 0x03c3, 0x00b5, 0x03bc, 0x03c4, 0x03a6, 0x00d8, 0x0398,
0x03a9, 0x2126, 0x03b4, 0x221e, 0x03c6, 0x00f8, 0x03b5, 0x2229,
0x2261, 0x00b1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00f7, 0x2248,
0x00b0, 0x2219, 0x00b7, 0x221a, 0x207f, 0x00b2, 0x25a0, 0xfffd,
0x00a0
};
/vc_screen.c
0,0 → 1,268
/*
* linux/arch/arm/drivers/char/vc_screen.c
*
* Provide access to virtual console memory.
* /dev/vcs0: the screen as it is being viewed right now (possibly scrolled)
* /dev/vcsN: the screen of /dev/ttyN (1 <= N <= 63)
* [minor: N]
*
* /dev/vcsaN: idem, but including attributes, and prefixed with
* the 4 bytes lines,columns,x,y (as screendump used to give)
* [minor: N+128]
*
* This replaces screendump and part of selection, so that the system
* administrator can control access using file system permissions.
*
* aeb@cwi.nl - efter Friedas begravelse - 950211
*
* Modified by Russell King (01/01/96) [experimental + in development]
*/
 
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/tty.h>
#include <linux/fs.h>
#include <asm/segment.h>
 
#include "vt_kern.h"
#include "selection.h"
 
#define HEADER_SIZE 4
 
static inline int vcs_size (struct inode *inode)
{
int size = vtdata.numrows * vtdata.numcolumns;
 
if (MINOR(inode->i_rdev) & 128)
size = sizeof (unsigned long) * size + HEADER_SIZE;
return size;
}
 
static int vcs_lseek (struct inode *inode, struct file *file, off_t offset, int orig)
{
int size = vcs_size(inode);
 
switch (orig) {
case 0:
file->f_pos = offset;
break;
case 1:
file->f_pos += offset;
break;
case 2:
file->f_pos = size + offset;
break;
default:
return -EINVAL;
}
if (file->f_pos < 0 || file->f_pos > size)
return -EINVAL;
return file->f_pos;
}
 
static int vcs_read (struct inode *inode, struct file *file, char *buf, int count)
{
struct vt *vt;
unsigned long p = file->f_pos;
unsigned int cons = MINOR(inode->i_rdev);
int attr, size, read;
char *buf0;
unsigned long *org, d;
 
attr = (cons & 128);
cons = (cons & 127);
 
if (cons == 0)
vt = vtdata.fgconsole;
else
vt = vt_con_data + (cons - 1);
 
if (!vt_allocated (vt))
return -ENXIO;
 
size = vcs_size(inode);
if (count < 0 || p > size)
return -EINVAL;
 
if (count > size - p)
count = size - p;
 
buf0 = buf;
if (!attr) {
org = screen_pos (vt, p);
while (count-- > 0)
put_user (*org++ & 0xff, buf++);
} else {
if (p < HEADER_SIZE) {
char header[HEADER_SIZE];
header[0] = (char) vtdata.numrows;
header[1] = (char) vtdata.numcolumns;
getconsxy (vt, header + 2);
while (p < HEADER_SIZE && count-- > 0)
put_user (header[p++], buf++);
}
p -= HEADER_SIZE;
org = screen_pos (vt, p >> 2);
 
if (p & 3) {
unsigned long d = *org++;
 
switch (p & 3) {
case 1:
if (count-- > 0)
put_user ((d >> 8) & 255, buf++);
case 2:
if (count-- > 0)
put_user ((d >> 16) & 255, buf++);
case 3:
if (count-- > 0)
put_user (d >> 24, buf++);
}
}
 
while (count > 3) {
put_user (*org++, (unsigned long *) buf);
buf += 4;
count -= 4;
}
 
if (count > 0) {
d = *org;
put_user (d & 0xff, buf++);
if (count > 1)
put_user ((d >> 8) & 0xff, buf++);
if (count > 2)
put_user ((d >> 16) & 0xff, buf++);
}
}
read = buf - buf0;
file->f_pos += read;
return read;
}
 
static int vcs_write (struct inode *inode, struct file *file, const char *buf, int count)
{
struct vt *vt;
unsigned long p = file->f_pos;
unsigned int cons = MINOR(inode->i_rdev);
int viewed, attr, size, written;
const char *buf0;
unsigned long *org;
 
attr = (cons & 128);
cons = (cons & 127);
 
if (cons == 0) {
vt = vtdata.fgconsole;
viewed = 1;
} else {
vt = vt_con_data + (cons - 1);
viewed = 0;
}
 
if (!vt_allocated (vt))
return -ENXIO;
 
size = vcs_size(inode);
 
if (count < 0 || p > size)
return -EINVAL;
 
if (count > size - p)
count = size - p;
 
buf0 = buf;
if (!attr) {
org = screen_pos (vt, p);
while (count-- > 0) {
*org = (*org & 0xffffff00) | get_user (buf++);
org++;
}
} else {
if (p < HEADER_SIZE) {
char header[HEADER_SIZE];
getconsxy (vt, header+2);
 
while (p < HEADER_SIZE && count-- > 0)
header[p++] = get_user (buf++);
if (!viewed)
putconsxy (vt, header + 2);
}
p -= HEADER_SIZE;
org = screen_pos (vt, p >> 2);
 
if (p & 3) {
unsigned long d = *org;
 
switch (p & 3) {
case 1:
if (count-- > 0)
d = (d & 0xffff00ff) | (get_user (buf++) << 8);
case 2:
if (count-- > 0)
d = (d & 0xff00ffff) | (get_user (buf++) << 16);
case 3:
if (count-- > 0)
d = (d & 0x00ffffff) | (get_user (buf++) << 24);
}
*org ++ = d;
}
 
while (count > 3) {
*org ++ = get_user ((const unsigned long *)buf);
buf += 4;
count -= 4;
}
 
if (count > 0) {
unsigned long d;
 
d = (*org >> (count * 8)) << (count * 8);
d |= get_user (buf ++);
 
if (count > 1)
d |= get_user (buf ++) << 8;
 
if (count > 2)
d |= get_user (buf ++) << 16;
*org = d;
}
}
written = buf - buf0;
update_scrmem (vt, file->f_pos >> 2, (written + 3) >> 2);
file->f_pos += written;
return written;
}
 
static int vcs_open (struct inode *inode, struct file *filp)
{
unsigned int cons = (MINOR(inode->i_rdev) & 127);
 
if (cons && !vt_allocated (vt_con_data + cons - 1))
return -ENXIO;
return 0;
}
 
static struct file_operations vcs_fops = {
vcs_lseek, /* lseek */
vcs_read, /* read */
vcs_write, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
vcs_open, /* open */
NULL, /* release */
NULL /* fsync */
};
 
int vcs_init(void)
{
int error;
 
error = register_chrdev(VCS_MAJOR, "vcs", &vcs_fops);
if (error)
printk("unable to get major %d for vcs device", VCS_MAJOR);
return error;
}
/tty_io.c
0,0 → 1,1889
/*
* linux/arch/arm/drivers/char/tty_io.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Modifications for ARM processor Copright (C) 1995, 1996 Russell King.
*/
 
/*
* 'tty_io.c' gives an orthogonal feeling to tty's, be they consoles
* or rs-channels. It also implements echoing, cooked mode etc.
*
* Kill-line thanks to John T Kohl, who also corrected VMIN = VTIME = 0.
*
* Modified by Theodore Ts'o, 9/14/92, to dynamically allocate the
* tty_struct and tty_queue structures. Previously there was an array
* of 256 tty_struct's which was statically allocated, and the
* tty_queue structures were allocated at boot time. Both are now
* dynamically allocated only when the tty is open.
*
* Also restructured routines so that there is more of a separation
* between the high-level tty routines (tty_io.c and tty_ioctl.c) and
* the low-level tty routines (serial.c, pty.c, console.c). This
* makes for cleaner and more compact code. -TYT, 9/17/92
*
* Modified by Fred N. van Kempen, 01/29/93, to add line disciplines
* which can be dynamically activated and de-activated by the line
* discipline handling modules (like SLIP).
*
* NOTE: pay no attention to the line discipline code (yet); its
* interface is still subject to change in this version...
* -- TYT, 1/31/92
*
* Added functionality to the OPOST tty handling. No delays, but all
* other bits should be there.
* -- Nick Holloway <alfie@dcs.warwick.ac.uk>, 27th May 1993.
*
* Rewrote canonical mode and added more termios flags.
* -- julian@uhunix.uhcc.hawaii.edu (J. Cowley), 13Jan94
*
* Reorganized FASYNC support so mouse code can share it.
* -- ctm@ardi.com, 9Sep95
*
* Altered get_free_page to kmalloc's for tty structures to save memory
* on the ARM processor
* -- rmk@ecs.soton.ac.uk
*
* New TIOCLINUX variants added.
* -- mj@k332.feld.cvut.cz, 19-Nov-95
*
* Restrict vt switching via ioctl()
* -- grif@cs.ucr.edu, 5-Dec-95
*
* Rewrote init_dev and release_dev to eliminate races.
* -- Bill Hawes <whawes@star.net>, June 97
*/
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/major.h>
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/fcntl.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/timer.h>
#include <linux/ctype.h>
#include <linux/kd.h>
#include <linux/mm.h>
#include <linux/string.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
 
/*#include <linux/scc.h>*/
 
#include "kbd_kern.h"
#include "vt_kern.h"
#include "selection.h"
 
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
 
#ifdef CONFIG_SERIAL_TRIO
extern int rs_trio_init(void);
#endif
 
#if PAGE_SIZE > 8192
static __inline__ struct tty_struct * alloc_tty_struct (int prio)
{
return kmalloc (sizeof (struct tty_struct), prio);
}
 
static __inline__ void free_tty_struct (struct tty_struct *tty)
{
kfree (tty);
}
#else
static __inline__ struct tty_struct * alloc_tty_struct (int prio)
{
return (struct tty_struct *) get_free_page (prio);
}
 
static __inline__ void free_tty_struct (struct tty_struct *tty)
{
free_page ((unsigned long) tty);
}
#endif
 
#define CONSOLE_DEV MKDEV(TTY_MAJOR,0)
#define TTY_DEV MKDEV(TTYAUX_MAJOR,0)
 
#undef TTY_DEBUG_HANGUP
 
#define TTY_PARANOIA_CHECK
#define CHECK_TTY_COUNT
 
/*extern void set_vesa_blanking(const unsigned long arg);*/
 
struct termios tty_std_termios; /* for the benefit of tty drivers */
struct tty_driver *tty_drivers; /* linked list of tty drivers */
struct tty_ldisc ldiscs[NR_LDISCS]; /* line disc dispatch table */
 
int kmsg_redirect;
struct tty_struct * redirect;
struct wait_queue * keypress_wait;
 
static void initialize_tty_struct(struct tty_struct *tty);
 
static int tty_read(struct inode *, struct file *, char *, int);
static int tty_write(struct inode *, struct file *, const char *, int);
static int tty_select(struct inode *, struct file *, int, select_table *);
static int tty_open(struct inode *, struct file *);
static void tty_release(struct inode *, struct file *);
static int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg);
static int tty_fasync(struct inode * inode, struct file * filp, int on);
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
/*
* These two routines return the name of tty. tty_name() should NOT
* be used in interrupt drivers, since it's not re-entrant. Use
* _tty_name() instead.
*/
char *_tty_name(struct tty_struct *tty, char *buf)
{
if (tty)
sprintf(buf, "%s%d", tty->driver.name,
MINOR(tty->device) - tty->driver.minor_start +
tty->driver.name_base);
else
strcpy(buf, "NULL tty");
return buf;
}
 
char *tty_name(struct tty_struct *tty)
{
static char buf[64];
 
return(_tty_name(tty, buf));
}
 
inline int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
const char *routine)
{
#ifdef TTY_PARANOIA_CHECK
static const char *badmagic =
"Warning: bad magic number for tty struct (%s) in %s\n";
static const char *badtty =
"Warning: null TTY for (%s) in %s\n";
 
if (!tty) {
printk(badtty, kdevname(device), routine);
return 1;
}
if (tty->magic != TTY_MAGIC) {
printk(badmagic, kdevname(device), routine);
return 1;
}
#endif
return 0;
}
 
static int check_tty_count(struct tty_struct *tty, const char *routine)
{
#ifdef CHECK_TTY_COUNT
struct file *f;
int i, count = 0;
for (f = first_file, i=0; i<nr_files; i++, f = f->f_next) {
if (!f->f_count)
continue;
if (f->private_data == tty) {
count++;
}
}
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_SLAVE &&
tty->link && tty->link->count)
count++;
if (tty->count != count) {
printk("Warning: dev (%s) tty->count(%d) != #fd's(%d) in %s\n",
kdevname(tty->device), tty->count, count, routine);
return count;
}
#endif
return 0;
}
 
int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc)
{
if (disc < N_TTY || disc >= NR_LDISCS)
return -EINVAL;
if (new_ldisc) {
ldiscs[disc] = *new_ldisc;
ldiscs[disc].flags |= LDISC_FLAG_DEFINED;
ldiscs[disc].num = disc;
} else
memset(&ldiscs[disc], 0, sizeof(struct tty_ldisc));
return 0;
}
 
/* Set the discipline of a tty line. */
static int tty_set_ldisc(struct tty_struct *tty, int ldisc)
{
int retval = 0;
struct tty_ldisc o_ldisc;
 
if ((ldisc < N_TTY) || (ldisc >= NR_LDISCS))
return -EINVAL;
#ifdef CONFIG_KERNELD
/* Eduardo Blanco <ejbs@cs.cs.com.uy> */
if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED)) {
char modname [20];
sprintf(modname, "tty-ldisc-%d", ldisc);
request_module (modname);
}
#endif
if (!(ldiscs[ldisc].flags & LDISC_FLAG_DEFINED))
return -EINVAL;
 
if (tty->ldisc.num == ldisc)
return 0; /* We are already in the desired discipline */
o_ldisc = tty->ldisc;
 
tty_wait_until_sent(tty, 0);
/* Shutdown the current discipline. */
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
 
/* Now set up the new line discipline. */
tty->ldisc = ldiscs[ldisc];
tty->termios->c_line = ldisc;
if (tty->ldisc.open)
retval = (tty->ldisc.open)(tty);
if (retval < 0) {
tty->ldisc = o_ldisc;
tty->termios->c_line = tty->ldisc.num;
if (tty->ldisc.open && (tty->ldisc.open(tty) < 0)) {
tty->ldisc = ldiscs[N_TTY];
tty->termios->c_line = N_TTY;
if (tty->ldisc.open) {
int r = tty->ldisc.open(tty);
 
if (r < 0)
panic("Couldn't open N_TTY ldisc for "
"%s --- error %d.",
tty_name(tty), r);
}
}
}
if (tty->ldisc.num != o_ldisc.num && tty->driver.set_ldisc)
tty->driver.set_ldisc(tty);
return retval;
}
 
/*
* This routine returns a tty driver structure, given a device number
*/
struct tty_driver *get_tty_driver(kdev_t device)
{
int major, minor;
struct tty_driver *p;
minor = MINOR(device);
major = MAJOR(device);
 
for (p = tty_drivers; p; p = p->next) {
if (p->major != major)
continue;
if (minor < p->minor_start)
continue;
if (minor >= p->minor_start + p->num)
continue;
return p;
}
return NULL;
}
 
/*
* If we try to write to, or set the state of, a terminal and we're
* not in the foreground, send a SIGTTOU. If the signal is blocked or
* ignored, go ahead and perform the operation. (POSIX 7.2)
*/
int tty_check_change(struct tty_struct * tty)
{
if (current->tty != tty)
return 0;
if (tty->pgrp <= 0) {
printk("tty_check_change: tty->pgrp <= 0!\n");
return 0;
}
if (current->pgrp == tty->pgrp)
return 0;
if (is_ignored(SIGTTOU))
return 0;
if (is_orphaned_pgrp(current->pgrp))
return -EIO;
(void) kill_pg(current->pgrp,SIGTTOU,1);
return -ERESTARTSYS;
}
 
static int hung_up_tty_read(struct inode * inode, struct file * file, char * buf, int count)
{
return 0;
}
 
static int hung_up_tty_write(struct inode * inode, struct file * file, const char * buf, int count)
{
return -EIO;
}
 
static int hung_up_tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
{
return 1;
}
 
static int hung_up_tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
}
 
static int tty_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
return -ESPIPE;
}
 
static struct file_operations tty_fops = {
tty_lseek,
tty_read,
tty_write,
NULL, /* tty_readdir */
tty_select,
tty_ioctl,
NULL, /* tty_mmap */
tty_open,
tty_release,
NULL, /* tty_fsync */
tty_fasync
};
 
static struct file_operations hung_up_tty_fops = {
tty_lseek,
hung_up_tty_read,
hung_up_tty_write,
NULL, /* hung_up_tty_readdir */
hung_up_tty_select,
hung_up_tty_ioctl,
NULL, /* hung_up_tty_mmap */
NULL, /* hung_up_tty_open */
tty_release, /* hung_up_tty_release */
NULL, /* hung_up_tty_fsync */
NULL /* hung_up_tty_fasync */
};
 
void do_tty_hangup(struct tty_struct * tty, struct file_operations *fops)
{
int i;
struct file * filp;
struct task_struct *p;
 
if (!tty)
return;
check_tty_count(tty, "do_tty_hangup");
for (filp = first_file, i=0; i<nr_files; i++, filp = filp->f_next) {
if (!filp->f_count)
continue;
if (filp->private_data != tty)
continue;
if (!filp->f_inode)
continue;
if (filp->f_inode->i_rdev == CONSOLE_DEV)
continue;
if (filp->f_op != &tty_fops)
continue;
tty_fasync(filp->f_inode, filp, 0);
filp->f_op = fops;
}
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
wake_up_interruptible(&tty->read_wait);
 
/*
* Shutdown the current line discipline, and reset it to
* N_TTY.
*/
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
tty->ldisc = ldiscs[N_TTY];
tty->termios->c_line = N_TTY;
if (tty->ldisc.open) {
i = (tty->ldisc.open)(tty);
if (i < 0)
printk("do_tty_hangup: N_TTY open: error %d\n",
-i);
}
}
for_each_task(p) {
if ((tty->session > 0) && (p->session == tty->session) &&
p->leader) {
send_sig(SIGHUP,p,1);
send_sig(SIGCONT,p,1);
if (tty->pgrp > 0)
p->tty_old_pgrp = tty->pgrp;
}
if (p->tty == tty)
p->tty = NULL;
}
tty->flags = 0;
tty->session = 0;
tty->pgrp = -1;
tty->ctrl_status = 0;
if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS)
*tty->termios = tty->driver.init_termios;
if (tty->driver.hangup)
(tty->driver.hangup)(tty);
}
 
void tty_hangup(struct tty_struct * tty)
{
#ifdef TTY_DEBUG_HANGUP
printk("%s hangup...\n", tty_name(tty));
#endif
do_tty_hangup(tty, &hung_up_tty_fops);
}
 
void tty_vhangup(struct tty_struct * tty)
{
#ifdef TTY_DEBUG_HANGUP
printk("%s vhangup...\n", tty_name(tty));
#endif
do_tty_hangup(tty, &hung_up_tty_fops);
}
 
int tty_hung_up_p(struct file * filp)
{
return (filp->f_op == &hung_up_tty_fops);
}
 
/*
* This function is typically called only by the session leader, when
* it wants to disassociate itself from its controlling tty.
*
* It performs the following functions:
* (1) Sends a SIGHUP and SIGCONT to the foreground process group
* (2) Clears the tty from being controlling the session
* (3) Clears the controlling tty for all processes in the
* session group.
*
* The argument on_exit is set to 1 if called when a process is
* exiting; it is 0 if called by the ioctl TIOCNOTTY.
*/
void disassociate_ctty(int on_exit)
{
struct tty_struct *tty = current->tty;
struct task_struct *p;
int tty_pgrp = -1;
 
if (tty) {
tty_pgrp = tty->pgrp;
if (on_exit && tty->driver.type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty);
} else {
if (current->tty_old_pgrp) {
kill_pg(current->tty_old_pgrp, SIGHUP, on_exit);
kill_pg(current->tty_old_pgrp, SIGCONT, on_exit);
}
return;
}
if (tty_pgrp > 0) {
kill_pg(tty_pgrp, SIGHUP, on_exit);
if (!on_exit)
kill_pg(tty_pgrp, SIGCONT, on_exit);
}
 
current->tty_old_pgrp = 0;
tty->session = 0;
tty->pgrp = -1;
 
for_each_task(p)
if (p->session == current->session)
p->tty = NULL;
}
 
void wait_for_keypress(void)
{
sleep_on(&keypress_wait);
}
 
void stop_tty(struct tty_struct *tty)
{
if (tty->stopped)
return;
tty->stopped = 1;
if (tty->link && tty->link->packet) {
tty->ctrl_status &= ~TIOCPKT_START;
tty->ctrl_status |= TIOCPKT_STOP;
wake_up_interruptible(&tty->link->read_wait);
}
if (tty->driver.stop)
(tty->driver.stop)(tty);
}
 
void start_tty(struct tty_struct *tty)
{
if (!tty->stopped)
return;
tty->stopped = 0;
if (tty->link && tty->link->packet) {
tty->ctrl_status &= ~TIOCPKT_STOP;
tty->ctrl_status |= TIOCPKT_START;
wake_up_interruptible(&tty->link->read_wait);
}
if (tty->driver.start)
(tty->driver.start)(tty);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
}
 
static int tty_read(struct inode * inode, struct file * file, char * buf, int count)
{
int i;
struct tty_struct * tty;
 
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_read"))
return -EIO;
if (!tty || (tty->flags & (1 << TTY_IO_ERROR)))
return -EIO;
 
/* This check not only needs to be done before reading, but also
whenever read_chan() gets woken up after sleeping, so I've
moved it to there. This should only be done for the N_TTY
line discipline, anyway. Same goes for write_chan(). -- jlc. */
#if 0
if ((inode->i_rdev != CONSOLE_DEV) && /* don't stop on /dev/console */
(tty->pgrp > 0) &&
(current->tty == tty) &&
(tty->pgrp != current->pgrp))
if (is_ignored(SIGTTIN) || is_orphaned_pgrp(current->pgrp))
return -EIO;
else {
(void) kill_pg(current->pgrp, SIGTTIN, 1);
return -ERESTARTSYS;
}
#endif
if (tty->ldisc.read)
/* XXX casts are for what kernel-wide prototypes should be. */
i = (tty->ldisc.read)(tty,file,(unsigned char *)buf,(unsigned int)count);
else
i = -EIO;
if (i > 0)
inode->i_atime = CURRENT_TIME;
return i;
}
 
/*
* Split writes up in sane blocksizes to avoid
* denial-of-service type attacks
*/
static inline int do_tty_write(
int (*write)(struct tty_struct *, struct file *, const unsigned char *, unsigned int),
struct inode *inode,
struct tty_struct *tty,
struct file *file,
const unsigned char *buf,
unsigned int count)
{
int ret = 0, written = 0;
 
for (;;) {
/* why should this depend on the page size? (PAGE_SIZE*2) */
unsigned int size = 8192;
if (size > count)
size = count;
ret = write(tty, file, buf, size);
if (ret <= 0)
break;
written += ret;
buf += ret;
count -= ret;
if (!count)
break;
ret = -ERESTARTSYS;
if (current->signal & ~current->blocked)
break;
if (need_resched)
schedule();
}
if (written) {
inode->i_mtime = CURRENT_TIME;
ret = written;
}
return ret;
}
 
 
static int tty_write(struct inode * inode, struct file * file, const char * buf, int count)
{
int is_console;
struct tty_struct * tty;
 
is_console = (inode->i_rdev == CONSOLE_DEV);
 
if (is_console && redirect)
tty = redirect;
else
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_write"))
return -EIO;
if (!tty || !tty->driver.write || (tty->flags & (1 << TTY_IO_ERROR)))
return -EIO;
#if 0
if (!is_console && L_TOSTOP(tty) && (tty->pgrp > 0) &&
(current->tty == tty) && (tty->pgrp != current->pgrp)) {
if (is_orphaned_pgrp(current->pgrp))
return -EIO;
if (!is_ignored(SIGTTOU)) {
(void) kill_pg(current->pgrp, SIGTTOU, 1);
return -ERESTARTSYS;
}
}
#endif
if (!tty->ldisc.write)
return -EIO;
return do_tty_write(tty->ldisc.write,
inode, tty, file,
(const unsigned char *)buf,
(unsigned int)count);
}
 
/* Semaphore to protect creating and releasing a tty */
static struct semaphore tty_sem = MUTEX;
static void down_tty_sem(int index)
{
down(&tty_sem);
}
static void up_tty_sem(int index)
{
up(&tty_sem);
}
static void release_mem(struct tty_struct *tty, int idx);
 
/*
* Rewritten to remove races and properly clean up after a failed open.
* The new code protects the open with a semaphore, so it's really
* quite straightforward. The semaphore locking can probably be
* relaxed for the (most common) case of reopening a tty.
*/
static int init_dev(kdev_t device, struct tty_struct **ret_tty)
{
struct tty_struct *tty, *o_tty;
struct termios *tp, **tp_loc, *o_tp, **o_tp_loc;
struct termios *ltp, **ltp_loc, *o_ltp, **o_ltp_loc;
struct tty_driver *driver;
int retval;
int idx;
 
driver = get_tty_driver(device);
if (!driver)
return -ENODEV;
 
idx = MINOR(device) - driver->minor_start;
 
/*
* Check whether we need to acquire the tty semaphore to avoid
* race conditions. For now, play it safe.
*/
down_tty_sem(idx);
 
/* check whether we're reopening an existing tty */
tty = driver->table[idx];
if(tty) goto fast_track;
 
/*
* First time open is complex, especially for PTY devices.
* This code guarantees that either everything succeeds and the
* TTY is ready for operation, or else the table slots are vacated
* and the allocated memory released. (Except that the termios
* and locked termios may be retained.)
*/
 
o_tty = NULL;
tp = o_tp = NULL;
ltp = o_ltp = NULL;
 
tty = alloc_tty_struct (GFP_KERNEL);
if(!tty)
goto fail_no_mem;
initialize_tty_struct(tty);
tty->device = device;
tty->driver = *driver;
 
tp_loc = &driver->termios[idx];
if (!*tp_loc) {
tp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!tp)
goto free_mem_out;
*tp = driver->init_termios;
}
 
ltp_loc = &driver->termios_locked[idx];
if (!*ltp_loc) {
ltp = (struct termios *) kmalloc(sizeof(struct termios),
GFP_KERNEL);
if (!ltp)
goto free_mem_out;
memset(ltp, 0, sizeof(struct termios));
}
 
if (driver->type == TTY_DRIVER_TYPE_PTY) {
o_tty = alloc_tty_struct (GFP_KERNEL);
if (!o_tty)
goto free_mem_out;
initialize_tty_struct(o_tty);
o_tty->device = (kdev_t) MKDEV(driver->other->major,
driver->other->minor_start + idx);
o_tty->driver = *driver->other;
 
o_tp_loc = &driver->other->termios[idx];
if (!*o_tp_loc) {
o_tp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_tp)
goto free_mem_out;
*o_tp = driver->other->init_termios;
}
 
o_ltp_loc = &driver->other->termios_locked[idx];
if (!*o_ltp_loc) {
o_ltp = (struct termios *)
kmalloc(sizeof(struct termios), GFP_KERNEL);
if (!o_ltp)
goto free_mem_out;
memset(o_ltp, 0, sizeof(struct termios));
}
 
/*
* Everything allocated ... set up the o_tty structure.
*/
driver->other->table[idx] = o_tty;
if (!*o_tp_loc)
*o_tp_loc = o_tp;
if (!*o_ltp_loc)
*o_ltp_loc = o_ltp;
o_tty->termios = *o_tp_loc;
o_tty->termios_locked = *o_ltp_loc;
(*driver->other->refcount)++;
if (driver->subtype == PTY_TYPE_MASTER)
o_tty->count++;
 
/* Establish the links in both directions */
tty->link = o_tty;
o_tty->link = tty;
}
 
/*
* All structures have been allocated, so now we install them.
* Failures after this point use release_mem to clean up, so
* there's no need to null out the local pointers.
*/
driver->table[idx] = tty;
if (!*tp_loc)
*tp_loc = tp;
if (!*ltp_loc)
*ltp_loc = ltp;
tty->termios = *tp_loc;
tty->termios_locked = *ltp_loc;
(*driver->refcount)++;
tty->count++;
 
/*
* Structures all installed ... call the ldisc open routines.
* If we fail here just call release_mem to clean up. No need
* to decrement the use counts, as release_mem doesn't care.
*/
if (tty->ldisc.open) {
retval = (tty->ldisc.open)(tty);
if (retval)
goto release_mem_out;
}
if (o_tty && o_tty->ldisc.open) {
retval = (o_tty->ldisc.open)(o_tty);
if (retval) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
goto release_mem_out;
}
}
goto success;
 
/*
* This fast open can be used if the tty is already open.
* No memory is allocated, and the only failures are from
* attempting to open a closing tty or attempting multiple
* opens on a pty master.
*/
fast_track:
retval = -EIO;
if (test_bit(TTY_CLOSING, &tty->flags))
goto end_init;
 
if (driver->type == TTY_DRIVER_TYPE_PTY &&
driver->subtype == PTY_TYPE_MASTER) {
/*
* special case for PTY masters: only one open permitted,
* and the slave side open count is incremented as well.
*/
if (tty->count)
goto end_init;
tty->link->count++;
}
tty->count++;
tty->driver = *driver; /* N.B. why do this every time?? */
 
success:
retval = 0;
*ret_tty = tty;
/* All paths come through here to release the semaphore */
end_init:
up_tty_sem(idx);
return retval;
 
/* Release locally allocated memory ... nothing placed in slots */
free_mem_out:
if (o_tp)
kfree_s(o_tp, sizeof(struct termios));
if (o_tty)
free_tty_struct(o_tty);
if (ltp)
kfree_s(ltp, sizeof(struct termios));
if (tp)
kfree_s(tp, sizeof(struct termios));
free_tty_struct(tty);
 
fail_no_mem:
retval = -ENOMEM;
goto end_init;
 
/* call the tty release_mem routine to clean out this slot */
release_mem_out:
printk("init_dev: ldisc open failed, clearing slot %d\n", idx);
release_mem(tty, idx);
goto end_init;
}
 
/*
* Releases memory associated with a tty structure, and clears out the
* driver table slots.
*/
static void release_mem(struct tty_struct *tty, int idx)
{
struct tty_struct *o_tty;
struct termios *tp;
 
if ((o_tty = tty->link) != NULL) {
o_tty->driver.table[idx] = NULL;
if (o_tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
tp = o_tty->driver.termios[idx];
o_tty->driver.termios[idx] = NULL;
kfree_s(tp, sizeof(struct termios));
}
o_tty->magic = 0;
(*o_tty->driver.refcount)--;
free_tty_struct(o_tty);
}
 
tty->driver.table[idx] = NULL;
if (tty->driver.flags & TTY_DRIVER_RESET_TERMIOS) {
tp = tty->driver.termios[idx];
tty->driver.termios[idx] = NULL;
kfree_s(tp, sizeof(struct termios));
}
tty->magic = 0;
(*tty->driver.refcount)--;
free_tty_struct(tty);
}
 
/*
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
*/
static void release_dev(struct file * filp)
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
int idx;
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, filp->f_inode->i_rdev, "release_dev"))
return;
 
check_tty_count(tty, "release_dev");
 
tty_fasync(filp->f_inode, filp, 0);
 
idx = MINOR(tty->device) - tty->driver.minor_start;
pty_master = (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER);
o_tty = tty->link;
 
#ifdef TTY_PARANOIA_CHECK
if (idx < 0 || idx >= tty->driver.num) {
printk("release_dev: bad idx when trying to free (%s)\n",
kdevname(tty->device));
return;
}
if (tty != tty->driver.table[idx]) {
printk("release_dev: driver.table[%d] not tty for (%s)\n",
idx, kdevname(tty->device));
return;
}
if (tty->termios != tty->driver.termios[idx]) {
printk("release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, kdevname(tty->device));
return;
}
if (tty->termios_locked != tty->driver.termios_locked[idx]) {
printk("release_dev: driver.termios_locked[%d] not "
"termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
#endif
 
#ifdef TTY_DEBUG_HANGUP
printk("release_dev of %s (tty count=%d)...", tty_name(tty),
tty->count);
#endif
 
#ifdef TTY_PARANOIA_CHECK
if (tty->driver.other) {
if (o_tty != tty->driver.other->table[idx]) {
printk("release_dev: other->table[%d] not o_tty for ("
"%s)\n",
idx, kdevname(tty->device));
return;
}
if (o_tty->termios != tty->driver.other->termios[idx]) {
printk("release_dev: other->termios[%d] not o_termios "
"for (%s)\n",
idx, kdevname(tty->device));
return;
}
if (o_tty->termios_locked !=
tty->driver.other->termios_locked[idx]) {
printk("release_dev: other->termios_locked[%d] not "
"o_termios_locked for (%s)\n",
idx, kdevname(tty->device));
return;
}
if (o_tty->link != tty) {
printk("release_dev: bad pty pointers\n");
return;
}
}
#endif
 
if (tty->driver.close)
tty->driver.close(tty, filp);
 
/*
* Sanity check: if tty->count is going to zero, there shouldn't be
* any waiters on tty->read_wait or tty->write_wait. We test the
* wait queues and kick everyone out _before_ actually starting to
* close. This ensures that we won't block while releasing the tty
* structure.
*
* The test for the o_tty closing is necessary, since the master and
* slave sides may close in any order. If the slave side closes out
* first, its count will be one, since the master side holds an open.
* Thus this test wouldn't be triggered at the time the slave closes,
* so we do it now.
*
* Note that it's possible for the tty to be opened again while we're
* flushing out waiters. By recalculating the closing flags before
* each iteration we avoid any problems.
*/
while (1) {
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
do_sleep = 0;
 
if (tty_closing) {
if (waitqueue_active(&tty->read_wait)) {
wake_up(&tty->read_wait);
do_sleep++;
}
if (waitqueue_active(&tty->write_wait)) {
wake_up(&tty->write_wait);
do_sleep++;
}
}
if (o_tty_closing) {
if (waitqueue_active(&o_tty->read_wait)) {
wake_up(&o_tty->read_wait);
do_sleep++;
}
if (waitqueue_active(&o_tty->write_wait)) {
wake_up(&o_tty->write_wait);
do_sleep++;
}
}
if (!do_sleep)
break;
 
printk("release_dev: %s: read/write wait queue active!\n",
tty_name(tty));
schedule();
}
 
/*
* The closing flags are now consistent with the open counts on
* both sides, and we've completed the last operation that could
* block, so it's safe to proceed with closing.
*/
 
if (pty_master) {
if (--o_tty->count < 0) {
printk("release_dev: bad pty slave count (%d) for %s\n",
o_tty->count, tty_name(o_tty));
o_tty->count = 0;
}
}
if (--tty->count < 0) {
printk("release_dev: bad tty->count (%d) for %s\n",
tty->count, tty_name(tty));
tty->count = 0;
}
 
/*
* Perform some housekeeping before deciding whether to return.
*
* Set the TTY_CLOSING flag if this was the last open. In the
* case of a pty we may have to wait around for the other side
* to close, and TTY_CLOSING makes sure we can't be reopened.
*/
if(tty_closing)
set_bit(TTY_CLOSING, &tty->flags);
if(o_tty_closing)
set_bit(TTY_CLOSING, &o_tty->flags);
 
/*
* If _either_ side is closing, make sure there aren't any
* processes that still think tty or o_tty is their controlling
* tty. Also, clear redirect if it points to either tty.
*/
if (tty_closing || o_tty_closing) {
struct task_struct *p;
 
for_each_task(p) {
if (p->tty == tty || (o_tty && p->tty == o_tty))
p->tty = NULL;
}
 
if (redirect == tty || (o_tty && redirect == o_tty))
redirect = NULL;
}
 
/* check whether both sides are closing ... */
if (!tty_closing || (o_tty && !o_tty_closing))
return;
filp->private_data = 0;
#ifdef TTY_DEBUG_HANGUP
printk("freeing tty structure...");
#endif
 
/*
* Shutdown the current line discipline, and reset it to N_TTY.
* N.B. why reset ldisc when we're releasing the memory??
*/
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
tty->ldisc = ldiscs[N_TTY];
tty->termios->c_line = N_TTY;
if (o_tty) {
if (o_tty->ldisc.close)
(o_tty->ldisc.close)(o_tty);
o_tty->ldisc = ldiscs[N_TTY];
}
/*
* Make sure that the tty's task queue isn't activated. If it
* is, take it out of the linked list. The tqueue isn't used by
* pty's, so skip the test for them.
*/
if (tty->driver.type != TTY_DRIVER_TYPE_PTY) {
cli();
if (tty->flip.tqueue.sync) {
struct tq_struct *tq, *prev;
 
for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
if (tq == &tty->flip.tqueue) {
if (prev)
prev->next = tq->next;
else
tq_timer = tq->next;
break;
}
}
}
sti();
}
 
/*
* The release_mem function takes care of the details of clearing
* the slots and preserving the termios structure.
*/
release_mem(tty, idx);
}
 
/*
* tty_open and tty_release keep up the tty count that contains the
* number of opens done on a tty. We cannot use the inode-count, as
* different inodes might point to the same tty.
*
* Open-counting is needed for pty masters, as well as for keeping
* track of serial lines: DTR is dropped when the last close happens.
* (This is not done solely through tty->count, now. - Ted 1/27/92)
*
* The termios state of a pty is reset on first open so that
* settings don't persist across reuse.
*/
static int tty_open(struct inode * inode, struct file * filp)
{
struct tty_struct *tty;
int minor;
int noctty, retval;
kdev_t device;
 
retry_open:
noctty = filp->f_flags & O_NOCTTY;
device = inode->i_rdev;
if (device == TTY_DEV) {
if (!current->tty)
return -ENXIO;
device = current->tty->device;
/* noctty = 1; */
}
if (device == CONSOLE_DEV) {
device = MKDEV(TTY_MAJOR, vtdata.fgconsole->num);
noctty = 1;
}
minor = MINOR(device);
retval = init_dev(device, &tty);
if (retval)
return retval;
filp->private_data = tty;
check_tty_count(tty, "tty_open");
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
noctty = 1;
#ifdef TTY_DEBUG_HANGUP
printk("opening %s...", tty_name(tty));
#endif
if (tty->driver.open)
retval = tty->driver.open(tty, filp);
else
retval = -ENODEV;
 
if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) && !suser())
retval = -EBUSY;
 
if (retval) {
#ifdef TTY_DEBUG_HANGUP
printk("error %d in opening %s...", retval, tty_name(tty));
#endif
 
release_dev(filp);
if (retval != -ERESTARTSYS)
return retval;
if (current->signal & ~current->blocked)
return retval;
schedule();
/*
* Need to reset f_op in case a hangup happened.
*/
filp->f_op = &tty_fops;
goto retry_open;
}
if (!noctty &&
current->leader &&
!current->tty &&
tty->session == 0) {
current->tty = tty;
current->tty_old_pgrp = 0;
tty->session = current->session;
tty->pgrp = current->pgrp;
}
return 0;
}
 
static void tty_release(struct inode * inode, struct file * filp)
{
release_dev(filp);
}
 
static int tty_select(struct inode * inode, struct file * filp, int sel_type, select_table * wait)
{
struct tty_struct * tty;
 
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_select"))
return 0;
 
if (tty->ldisc.select)
return (tty->ldisc.select)(tty, inode, filp, sel_type, wait);
return 0;
}
 
/*
* fasync_helper() is used by some character device drivers (mainly mice)
* to set up the fasync queue. It returns negative on error, 0 if it did
* no changes and positive if it added/deleted the entry.
*/
int fasync_helper(struct inode * inode, struct file * filp, int on, struct fasync_struct **fapp)
{
struct fasync_struct *fa, **fp;
unsigned long flags;
 
for (fp = fapp; (fa = *fp) != NULL; fp = &fa->fa_next) {
if (fa->fa_file == filp)
break;
}
 
if (on) {
if (fa)
return 0;
fa = (struct fasync_struct *)kmalloc(sizeof(struct fasync_struct), GFP_KERNEL);
if (!fa)
return -ENOMEM;
fa->magic = FASYNC_MAGIC;
fa->fa_file = filp;
save_flags_cli (flags);
fa->fa_next = *fapp;
*fapp = fa;
restore_flags(flags);
return 1;
}
if (!fa)
return 0;
save_flags_cli (flags);
*fp = fa->fa_next;
restore_flags(flags);
kfree(fa);
return 1;
}
 
static int tty_fasync(struct inode * inode, struct file * filp, int on)
{
struct tty_struct * tty;
int retval;
 
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_fasync"))
return 0;
retval = fasync_helper(inode, filp, on, &tty->fasync);
if (retval <= 0)
return retval;
 
if (on) {
if (!waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = 1;
if (filp->f_owner.pid == 0) {
filp->f_owner.pid = (-tty->pgrp) ? : current->pid;
filp->f_owner.uid = current->uid;
filp->f_owner.euid = current->euid;
}
} else {
if (!tty->fasync && !waitqueue_active(&tty->read_wait))
tty->minimum_to_wake = N_TTY_BUF_SIZE;
}
return 0;
}
 
#if 0
/*
* XXX does anyone use this anymore?!?
*/
static int do_get_ps_info(unsigned long arg)
{
struct tstruct {
int flag;
int present[NR_TASKS];
struct task_struct tasks[NR_TASKS];
};
struct tstruct *ts = (struct tstruct *)arg;
struct task_struct **p;
char *c, *d;
int i, n = 0;
i = verify_area(VERIFY_WRITE, (void *)arg, sizeof(struct tstruct));
if (i)
return i;
for (p = &FIRST_TASK ; p <= &LAST_TASK ; p++, n++)
if (*p)
{
c = (char *)(*p);
d = (char *)(ts->tasks+n);
for (i=0 ; i<sizeof(struct task_struct) ; i++)
put_user(*c++, d++);
put_user(1, ts->present+n);
}
else
put_user(0, ts->present+n);
return(0);
}
#endif
 
static int tty_ioctl(struct inode * inode, struct file * file,
unsigned int cmd, unsigned long arg)
{
int retval;
struct tty_struct * tty;
struct tty_struct * real_tty;
struct winsize tmp_ws;
pid_t pgrp;
unsigned char ch;
char mbz = 0;
tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode->i_rdev, "tty_ioctl"))
return -EINVAL;
 
if (tty->driver.type == TTY_DRIVER_TYPE_PTY &&
tty->driver.subtype == PTY_TYPE_MASTER)
real_tty = tty->link;
else
real_tty = tty;
 
switch (cmd) {
case TIOCSTI:
if ((current->tty != tty) && !suser())
return -EPERM;
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
return retval;
ch = get_user((char *) arg);
tty->ldisc.receive_buf(tty, &ch, &mbz, 1);
return 0;
case TIOCGWINSZ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (struct winsize));
if (retval)
return retval;
memcpy_tofs((struct winsize *) arg, &tty->winsize,
sizeof (struct winsize));
return 0;
case TIOCSWINSZ:
retval = verify_area(VERIFY_READ, (void *) arg,
sizeof (struct winsize));
if (retval)
return retval;
memcpy_fromfs(&tmp_ws, (struct winsize *) arg,
sizeof (struct winsize));
if (memcmp(&tmp_ws, &tty->winsize,
sizeof(struct winsize))) {
if (tty->pgrp > 0)
kill_pg(tty->pgrp, SIGWINCH, 1);
if ((real_tty->pgrp != tty->pgrp) &&
(real_tty->pgrp > 0))
kill_pg(real_tty->pgrp, SIGWINCH, 1);
}
tty->winsize = tmp_ws;
real_tty->winsize = tmp_ws;
return 0;
case TIOCCONS:
if (tty->driver.type == TTY_DRIVER_TYPE_CONSOLE) {
if (!suser())
return -EPERM;
redirect = NULL;
return 0;
}
if (redirect)
return -EBUSY;
redirect = real_tty;
return 0;
case FIONBIO:
retval = verify_area(VERIFY_READ, (void *) arg, sizeof(int));
if (retval)
return retval;
arg = get_user((unsigned int *) arg);
if (arg)
file->f_flags |= O_NONBLOCK;
else
file->f_flags &= ~O_NONBLOCK;
return 0;
case TIOCEXCL:
set_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCNXCL:
clear_bit(TTY_EXCLUSIVE, &tty->flags);
return 0;
case TIOCNOTTY:
if (current->tty != tty)
return -ENOTTY;
if (current->leader)
disassociate_ctty(0);
current->tty = NULL;
return 0;
case TIOCSCTTY:
if (current->leader &&
(current->session == tty->session))
return 0;
/*
* The process must be a session leader and
* not have a controlling tty already.
*/
if (!current->leader || current->tty)
return -EPERM;
if (tty->session > 0) {
/*
* This tty is already the controlling
* tty for another session group!
*/
if ((arg == 1) && suser()) {
/*
* Steal it away
*/
struct task_struct *p;
 
for_each_task(p)
if (p->tty == tty)
p->tty = NULL;
} else
return -EPERM;
}
current->tty = tty;
current->tty_old_pgrp = 0;
tty->session = current->session;
tty->pgrp = current->pgrp;
return 0;
case TIOCGPGRP:
/*
* (tty == real_tty) is a cheap way of
* testing if the tty is NOT a master pty.
*/
if (tty == real_tty && current->tty != real_tty)
return -ENOTTY;
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (pid_t));
if (retval)
return retval;
put_user(real_tty->pgrp, (pid_t *) arg);
return 0;
case TIOCSPGRP:
retval = tty_check_change(real_tty);
if (retval == -EIO)
return -ENOTTY;
if (retval)
return retval;
if (!current->tty ||
(current->tty != real_tty) ||
(real_tty->session != current->session))
return -ENOTTY;
pgrp = get_user((pid_t *) arg);
if (pgrp < 0)
return -EINVAL;
if (session_of_pgrp(pgrp) != current->session)
return -EPERM;
real_tty->pgrp = pgrp;
return 0;
case TIOCGETD:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof (int));
if (retval)
return retval;
put_user(tty->ldisc.num, (int *) arg);
return 0;
case TIOCSETD:
retval = tty_check_change(tty);
if (retval)
return retval;
retval = verify_area(VERIFY_READ, (void *) arg,
sizeof (int));
if (retval)
return retval;
arg = get_user((int *) arg);
return tty_set_ldisc(tty, arg);
case TIOCLINUX:
if (tty->driver.type != TTY_DRIVER_TYPE_CONSOLE)
return -EINVAL;
if (current->tty != tty && !suser())
return -EPERM;
retval = verify_area(VERIFY_READ, (void *) arg, 1);
if (retval)
return retval;
switch (retval = get_user((char *)arg))
{
case 0:
case 8:
case 9:
printk("TIOCLINUX (0/8/9) ioctl is gone - use /dev/vcs\n");
return -EINVAL;
#if 0
case 1:
printk("Deprecated TIOCLINUX (1) ioctl\n");
return do_get_ps_info(arg);
#endif
case 2:
return set_selection(arg, tty);
case 3:
return paste_selection(tty);
case 4:
vt_do_unblankscreen ();
return 0;
case 5:
return sel_loadlut(arg);
case 6:
/*
* Make it possible to react to Shift+Mousebutton.
* Note that 'shift_state' is an undocumented
* kernel-internal variable; programs not closely
* related to the kernel should not use this.
*/
retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
if (retval)
return retval;
put_user(shift_state,(char *) arg);
return 0;
case 7:
retval = verify_area(VERIFY_WRITE, (void *) arg, 1);
if (retval)
return retval;
put_user(mouse_reporting(),(char *) arg);
return 0;
/* case 10:
set_vesa_blanking(arg);
return 0;*/
case 11: /* set kmsg redirect */
if (!suser())
return -EPERM;
retval = verify_area(VERIFY_READ,
(void *) arg+1, 1);
if (retval)
return retval;
kmsg_redirect = get_user((char *)arg+1);
return 0;
case 12: /* get fg console */
return vtdata.fgconsole->num;
default:
return -EINVAL;
}
 
case TIOCTTYGSTRUCT:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct tty_struct));
if (retval)
return retval;
memcpy_tofs((struct tty_struct *) arg,
tty, sizeof(struct tty_struct));
return 0;
default:
if (tty->driver.ioctl) {
retval = (tty->driver.ioctl)(tty, file,
cmd, arg);
if (retval != -ENOIOCTLCMD)
return retval;
}
if (tty->ldisc.ioctl) {
retval = (tty->ldisc.ioctl)(tty, file,
cmd, arg);
if (retval != -ENOIOCTLCMD)
return retval;
}
return -EINVAL;
}
}
 
 
/*
* This implements the "Secure Attention Key" --- the idea is to
* prevent trojan horses by killing all processes associated with this
* tty when the user hits the "Secure Attention Key". Required for
* super-paranoid applications --- see the Orange Book for more details.
*
* This code could be nicer; ideally it should send a HUP, wait a few
* seconds, then send a INT, and then a KILL signal. But you then
* have to coordinate with the init process, since all processes associated
* with the current tty must be dead before the new getty is allowed
* to spawn.
*/
void do_SAK( struct tty_struct *tty)
{
#ifdef TTY_SOFT_SAK
tty_hangup(tty);
#else
struct task_struct **p;
int session;
int i;
struct file *filp;
if (!tty)
return;
session = tty->session;
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
for (p = &LAST_TASK ; p > &FIRST_TASK ; --p) {
if (!(*p))
continue;
if (((*p)->tty == tty) ||
((session > 0) && ((*p)->session == session)))
send_sig(SIGKILL, *p, 1);
else if ((*p)->files) {
for (i=0; i < NR_OPEN; i++) {
filp = (*p)->files->fd[i];
if (filp && (filp->f_op == &tty_fops) &&
(filp->private_data == tty)) {
send_sig(SIGKILL, *p, 1);
break;
}
}
}
}
#endif
}
 
/*
* This routine is called out of the software interrupt to flush data
* from the flip buffer to the line discipline.
*/
static void flush_to_ldisc(void *private_)
{
struct tty_struct *tty = (struct tty_struct *) private_;
unsigned char *cp;
char *fp;
int count;
 
if (tty->flip.buf_num) {
cp = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
fp = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
tty->flip.buf_num = 0;
 
cli();
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
} else {
cp = tty->flip.char_buf;
fp = tty->flip.flag_buf;
tty->flip.buf_num = 1;
 
cli();
tty->flip.char_buf_ptr = tty->flip.char_buf + TTY_FLIPBUF_SIZE;
tty->flip.flag_buf_ptr = tty->flip.flag_buf + TTY_FLIPBUF_SIZE;
}
count = tty->flip.count;
tty->flip.count = 0;
sti();
#if 0
if (count > tty->max_flip_cnt)
tty->max_flip_cnt = count;
#endif
tty->ldisc.receive_buf(tty, cp, fp, count);
}
 
/*
* This subroutine initializes a tty structure.
*/
static void initialize_tty_struct(struct tty_struct *tty)
{
memset(tty, 0, sizeof(struct tty_struct));
tty->magic = TTY_MAGIC;
tty->ldisc = ldiscs[N_TTY];
tty->pgrp = -1;
tty->flip.char_buf_ptr = tty->flip.char_buf;
tty->flip.flag_buf_ptr = tty->flip.flag_buf;
tty->flip.tqueue.routine = flush_to_ldisc;
tty->flip.tqueue.data = tty;
}
 
/*
* The default put_char routine if the driver did not define one.
*/
void tty_default_put_char(struct tty_struct *tty, unsigned char ch)
{
tty->driver.write(tty, 0, &ch, 1);
}
 
/*
* Called by a tty driver to register itself.
*/
int tty_register_driver(struct tty_driver *driver)
{
int error;
 
if (driver->flags & TTY_DRIVER_INSTALLED)
return 0;
 
error = register_chrdev(driver->major, driver->name, &tty_fops);
if (error < 0)
return error;
else if(driver->major == 0)
driver->major = error;
 
if (!driver->put_char)
driver->put_char = tty_default_put_char;
driver->prev = 0;
driver->next = tty_drivers;
if (tty_drivers) tty_drivers->prev = driver;
tty_drivers = driver;
return error;
}
 
/*
* Called by a tty driver to unregister itself.
*/
int tty_unregister_driver(struct tty_driver *driver)
{
int retval;
struct tty_driver *p;
int found = 0;
const char *othername = NULL;
if (*driver->refcount)
return -EBUSY;
 
for (p = tty_drivers; p; p = p->next) {
if (p == driver)
found++;
else if (p->major == driver->major)
othername = p->name;
}
if (!found)
return -ENOENT;
 
if (othername == NULL) {
retval = unregister_chrdev(driver->major, driver->name);
if (retval)
return retval;
} else
register_chrdev(driver->major, othername, &tty_fops);
 
if (driver->prev)
driver->prev->next = driver->next;
else
tty_drivers = driver->next;
if (driver->next)
driver->next->prev = driver->prev;
 
return 0;
}
 
 
/*
* Initialize the console device. This is called *early*, so
* we can't necessarily depend on lots of kernel help here.
* Just do some early initializations, and do the complex setup
* later.
*/
long console_init(long kmem_start, long kmem_end)
{
/* Setup the default TTY line discipline. */
memset(ldiscs, 0, sizeof(ldiscs));
(void) tty_register_ldisc(N_TTY, &tty_ldisc_N_TTY);
 
/*
* Set up the standard termios. Individual tty drivers may
* deviate from this; this is used as a template.
*/
memset(&tty_std_termios, 0, sizeof(struct termios));
memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
tty_std_termios.c_iflag = ICRNL | IXON;
tty_std_termios.c_oflag = OPOST | ONLCR;
tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
ECHOCTL | ECHOKE | IEXTEN;
 
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
#ifdef CONFIG_CONSOLE
/*
* set up the console device so that later boot sequences can
* inform about problems etc..
*/
return vt_pre_init(kmem_start);
#else /* !CONFIG_CONSOLE */
return kmem_start;
#endif /* !CONFIG_CONSOLE */
}
 
static struct tty_driver dev_tty_driver, dev_console_driver;
 
/*
* Ok, now we can initialize the rest of the tty devices and can count
* on memory allocations, interrupts etc..
*/
int tty_init(void)
{
if (sizeof(struct tty_struct) > PAGE_SIZE)
panic("size of tty structure > PAGE_SIZE!");
vt_post_init ();
 
/*
* dev_tty_driver and dev_console_driver are actually magic
* devices which get redirected at open time. Nevertheless,
* we register them so that register_chrdev is called
* appropriately.
*/
memset(&dev_tty_driver, 0, sizeof(struct tty_driver));
dev_tty_driver.magic = TTY_DRIVER_MAGIC;
dev_tty_driver.name = "tty";
dev_tty_driver.name_base = 0;
dev_tty_driver.major = TTY_MAJOR;
dev_tty_driver.minor_start = 0;
dev_tty_driver.num = 1;
if (tty_register_driver(&dev_tty_driver))
panic("Couldn't register /dev/tty driver\n");
 
dev_console_driver = dev_tty_driver;
dev_console_driver.name = "console";
dev_console_driver.major = TTYAUX_MAJOR;
 
if (tty_register_driver(&dev_console_driver))
panic("Couldn't register /dev/console driver\n");
#ifndef CONFIG_ARCH_EBSA110
kbd_init();
#endif
#ifdef CONFIG_SERIAL
rs_init();
#endif
#ifdef CONFIG_SERIAL_TRIO
rs_trio_init();
#endif
pty_init();
vcs_init();
return 0;
}
 
/lp.c
0,0 → 1,687
/*
* Copyright (C) 1992 by Jim Weigand and Linus Torvalds
* Copyright (C) 1992,1993 by Michael K. Johnson
* - Thanks much to Gunter Windau for pointing out to me where the error
* checking ought to be.
* Copyright (C) 1993 by Nigel Gamble (added interrupt code)
* Copyright (C) 1994 by Alan Cox (Modularised it)
* LPCAREFUL, LPABORT, LPGETSTATUS added by Chris Metcalf, metcalf@lcs.mit.edu
* Statistics and support for slow printers by Rob Janssen, rob@knoware.nl
* "lp=" command line parameters added by Grant Guenther, grant@torque.net
*/
 
#include <linux/module.h>
 
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/sched.h>
#include <linux/lp.h>
#include <linux/malloc.h>
#include <linux/ioport.h>
#include <linux/fcntl.h>
#include <linux/delay.h>
 
#include <asm/io.h>
#include <asm/segment.h>
#include <asm/system.h>
 
#define NO_IRQ (-1)
 
/* the BIOS manuals say there can be up to 4 lpt devices
* but I have not seen a board where the 4th address is listed
* if you have different hardware change the table below
* please let me know if you have different equipment
* if you have more than 3 printers, remember to increase LP_NO
*/
struct lp_struct lp_table[] = {
{ 0x3bc, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
{ 0x378, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
{ 0x278, NO_IRQ, 0, LP_INIT_CHAR, LP_INIT_TIME, LP_INIT_WAIT, NULL, NULL, 0, 0, 0, {0} },
};
#define LP_NO 3
 
/* Test if printer is ready (and optionally has no error conditions) */
#define LP_READY(minor, status) \
((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : (status & LP_PBUSY))
#define LP_CAREFUL_READY(minor, status) \
((LP_F(minor) & LP_CAREFUL) ? _LP_CAREFUL_READY(status) : 1)
#define _LP_CAREFUL_READY(status) \
(status & (LP_PBUSY|LP_POUTPA|LP_PSELECD|LP_PERRORP)) == \
(LP_PBUSY|LP_PSELECD|LP_PERRORP)
 
/*
* All my debugging code assumes that you debug with only one printer at
* a time. RWWH
* Debug info moved into stats area, so this is no longer true (Rob Janssen)
*/
 
#undef LP_DEBUG
 
static int lp_reset(int minor)
{
outb_p(LP_PSELECP, LP_C(minor));
udelay(LP_DELAY);
outb_p(LP_PSELECP | LP_PINITP, LP_C(minor));
return LP_S(minor);
}
 
static inline int lp_char_polled(char lpchar, int minor)
{
int status, wait = 0;
unsigned long count = 0;
struct lp_stats *stats;
 
do {
status = LP_S(minor);
count ++;
if(need_resched)
schedule();
} while(!LP_READY(minor,status) && count < LP_CHAR(minor));
 
if (count == LP_CHAR(minor)) {
return 0;
/* we timed out, and the character was /not/ printed */
}
outb_p(lpchar, LP_B(minor));
stats = &LP_STAT(minor);
stats->chars++;
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
/* Wait until NBUSY line goes high */
count = 0;
do {
status = LP_S(minor);
count++;
if (need_resched)
schedule();
} while (LP_READY(minor, status) && (count<LP_CHAR(minor)));
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
/* update waittime statistics */
if (count > stats->maxwait) {
#ifdef LP_DEBUG
printk(KERN_DEBUG "lp%d success after %d counts.\n",minor,count);
#endif
stats->maxwait = count;
}
count *= 256;
wait = (count > stats->meanwait)? count - stats->meanwait :
stats->meanwait - count;
stats->meanwait = (255*stats->meanwait + count + 128) / 256;
stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
 
return 1;
}
 
static inline int lp_char_interrupt(char lpchar, int minor)
{
int wait;
unsigned long count = 0;
unsigned char status;
struct lp_stats *stats;
 
do {
if(need_resched)
schedule();
if ((status = LP_S(minor)) & LP_PBUSY) {
if (!LP_CAREFUL_READY(minor, status))
return 0;
outb_p(lpchar, LP_B(minor));
stats = &LP_STAT(minor);
stats->chars++;
/* must wait before taking strobe high, and after taking strobe
low, according spec. Some printers need it, others don't. */
wait = 0;
while(wait != LP_WAIT(minor)) wait++;
/* control port takes strobe high */
outb_p(( LP_PSELECP | LP_PINITP | LP_PSTROBE ), ( LP_C( minor )));
while(wait) wait--;
/* take strobe low */
outb_p(( LP_PSELECP | LP_PINITP ), ( LP_C( minor )));
/* update waittime statistics */
if (count) {
if (count > stats->maxwait)
stats->maxwait = count;
count *= 256;
wait = (count > stats->meanwait)? count - stats->meanwait :
stats->meanwait - count;
stats->meanwait = (255*stats->meanwait + count + 128) / 256;
stats->mdev = ((127 * stats->mdev) + wait + 64) / 128;
}
return 1;
}
} while (count++ < LP_CHAR(minor));
 
return 0;
}
 
static void lp_interrupt(int irq, void *dev_id, struct pt_regs *regs)
{
struct lp_struct *lp = &lp_table[0];
 
while (irq != lp->irq) {
if (++lp >= &lp_table[LP_NO])
return;
}
 
wake_up(&lp->lp_wait_q);
}
 
static inline int lp_write_interrupt(unsigned int minor, const char * buf, int count)
{
unsigned long copy_size;
unsigned long total_bytes_written = 0;
unsigned long bytes_written;
struct lp_struct *lp = &lp_table[minor];
unsigned char status;
 
do {
bytes_written = 0;
copy_size = (count <= LP_BUFFER_SIZE ? count : LP_BUFFER_SIZE);
memcpy_fromfs(lp->lp_buffer, buf, copy_size);
 
while (copy_size) {
if (lp_char_interrupt(lp->lp_buffer[bytes_written], minor)) {
--copy_size;
++bytes_written;
lp_table[minor].runchars++;
} else {
int rc = total_bytes_written + bytes_written;
if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
LP_STAT(minor).maxrun = lp_table[minor].runchars;
status = LP_S(minor);
if ((status & LP_POUTPA)) {
printk(KERN_INFO "lp%d out of paper\n", minor);
if (LP_F(minor) & LP_ABORT)
return rc?rc:-ENOSPC;
} else if (!(status & LP_PSELECD)) {
printk(KERN_INFO "lp%d off-line\n", minor);
if (LP_F(minor) & LP_ABORT)
return rc?rc:-EIO;
} else if (!(status & LP_PERRORP)) {
printk(KERN_ERR "lp%d printer error\n", minor);
if (LP_F(minor) & LP_ABORT)
return rc?rc:-EIO;
}
LP_STAT(minor).sleeps++;
cli();
outb_p((LP_PSELECP|LP_PINITP|LP_PINTEN), (LP_C(minor)));
status = LP_S(minor);
if ((!(status & LP_PACK) || (status & LP_PBUSY))
&& LP_CAREFUL_READY(minor, status)) {
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
continue;
}
lp_table[minor].runchars=0;
current->timeout = jiffies + LP_TIMEOUT_INTERRUPT;
interruptible_sleep_on(&lp->lp_wait_q);
outb_p((LP_PSELECP|LP_PINITP), (LP_C(minor)));
sti();
if (current->signal & ~current->blocked) {
if (total_bytes_written + bytes_written)
return total_bytes_written + bytes_written;
else
return -EINTR;
}
}
}
 
total_bytes_written += bytes_written;
buf += bytes_written;
count -= bytes_written;
 
} while (count > 0);
 
return total_bytes_written;
}
 
static inline int lp_write_polled(unsigned int minor, const char * buf, int count)
{
int retval,status;
char c;
const char *temp;
 
temp = buf;
while (count > 0) {
c = get_user(temp);
retval = lp_char_polled(c, minor);
/* only update counting vars if character was printed */
if (retval) {
count--; temp++;
lp_table[minor].runchars++;
} else { /* if printer timed out */
if (lp_table[minor].runchars > LP_STAT(minor).maxrun)
LP_STAT(minor).maxrun = lp_table[minor].runchars;
status = LP_S(minor);
 
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-ENOSPC;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
if (!(status & LP_PSELECD)) {
printk(KERN_INFO "lp%d off-line\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
} else
/* not offline or out of paper. on fire? */
if (!(status & LP_PERRORP)) {
printk(KERN_ERR "lp%d reported invalid error status (on fire, eh?)\n", minor);
if(LP_F(minor) & LP_ABORT)
return temp-buf?temp-buf:-EIO;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIMEOUT_POLLED;
schedule();
}
 
/* check for signals before going to sleep */
if (current->signal & ~current->blocked) {
if (temp != buf)
return temp-buf;
else
return -EINTR;
}
LP_STAT(minor).sleeps++;
#ifdef LP_DEBUG
printk(KERN_DEBUG "lp%d sleeping at %d characters for %d jiffies\n",
minor,lp_table[minor].runchars, LP_TIME(minor));
#endif
lp_table[minor].runchars=0;
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + LP_TIME(minor);
schedule();
}
}
return temp-buf;
}
 
static int lp_write(struct inode * inode, struct file * file, const char * buf, int count)
{
unsigned int minor = MINOR(inode->i_rdev);
 
if (jiffies-lp_table[minor].lastcall > LP_TIME(minor))
lp_table[minor].runchars = 0;
lp_table[minor].lastcall = jiffies;
 
if (LP_IRQ(minor) != NO_IRQ)
return lp_write_interrupt(minor, buf, count);
else
return lp_write_polled(minor, buf, count);
}
 
static int lp_lseek(struct inode * inode, struct file * file,
off_t offset, int origin)
{
return -ESPIPE;
}
 
static int lp_open(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
int ret;
unsigned int irq;
 
if (minor >= LP_NO)
return -ENXIO;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENXIO;
if (LP_F(minor) & LP_BUSY)
return -EBUSY;
 
MOD_INC_USE_COUNT;
 
/* If ABORTOPEN is set and the printer is offline or out of paper,
we may still want to open it to perform ioctl()s. Therefore we
have commandeered O_NONBLOCK, even though it is being used in
a non-standard manner. This is strictly a Linux hack, and
should most likely only ever be used by the tunelp application. */
if ((LP_F(minor) & LP_ABORTOPEN) && !(file->f_flags & O_NONBLOCK)) {
int status = LP_S(minor);
if (status & LP_POUTPA) {
printk(KERN_INFO "lp%d out of paper\n", minor);
MOD_DEC_USE_COUNT;
return -ENOSPC;
} else if (!(status & LP_PSELECD)) {
printk(KERN_INFO "lp%d off-line\n", minor);
MOD_DEC_USE_COUNT;
return -EIO;
} else if (!(status & LP_PERRORP)) {
printk(KERN_ERR "lp%d printer error\n", minor);
MOD_DEC_USE_COUNT;
return -EIO;
}
}
 
if ((irq = LP_IRQ(minor)) != NO_IRQ) {
lp_table[minor].lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp_table[minor].lp_buffer) {
MOD_DEC_USE_COUNT;
return -ENOMEM;
}
 
ret = request_irq(irq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
if (ret) {
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
printk("lp%d unable to use interrupt %d, error %d\n", minor, irq, ret);
MOD_DEC_USE_COUNT;
return ret;
}
}
 
LP_F(minor) |= LP_BUSY;
return 0;
}
 
static void lp_release(struct inode * inode, struct file * file)
{
unsigned int minor = MINOR(inode->i_rdev);
unsigned int irq;
 
if ((irq = LP_IRQ(minor)) != NO_IRQ) {
free_irq(irq, NULL);
kfree_s(lp_table[minor].lp_buffer, LP_BUFFER_SIZE);
lp_table[minor].lp_buffer = NULL;
}
 
LP_F(minor) &= ~LP_BUSY;
MOD_DEC_USE_COUNT;
}
 
 
static int lp_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
unsigned int minor = MINOR(inode->i_rdev);
int retval = 0;
 
#ifdef LP_DEBUG
printk(KERN_DEBUG "lp%d ioctl, cmd: 0x%x, arg: 0x%x\n", minor, cmd, arg);
#endif
if (minor >= LP_NO)
return -ENODEV;
if ((LP_F(minor) & LP_EXIST) == 0)
return -ENODEV;
switch ( cmd ) {
case LPTIME:
LP_TIME(minor) = arg * HZ/100;
break;
case LPCHAR:
LP_CHAR(minor) = arg;
break;
case LPABORT:
if (arg)
LP_F(minor) |= LP_ABORT;
else
LP_F(minor) &= ~LP_ABORT;
break;
case LPABORTOPEN:
if (arg)
LP_F(minor) |= LP_ABORTOPEN;
else
LP_F(minor) &= ~LP_ABORTOPEN;
break;
case LPCAREFUL:
if (arg)
LP_F(minor) |= LP_CAREFUL;
else
LP_F(minor) &= ~LP_CAREFUL;
break;
case LPWAIT:
LP_WAIT(minor) = arg;
break;
case LPSETIRQ: {
int oldirq;
int newirq = arg;
struct lp_struct *lp = &lp_table[minor];
 
if (!suser())
return -EPERM;
 
oldirq = LP_IRQ(minor);
 
/* Allocate buffer now if we are going to need it */
if (oldirq == NO_IRQ && newirq != NO_IRQ) {
lp->lp_buffer = (char *) kmalloc(LP_BUFFER_SIZE, GFP_KERNEL);
if (!lp->lp_buffer)
return -ENOMEM;
}
 
if (oldirq != NO_IRQ) {
free_irq(oldirq, NULL);
}
if (newirq != NO_IRQ) {
/* Install new irq */
if ((retval = request_irq(newirq, lp_interrupt, SA_INTERRUPT, "printer", NULL))) {
if (oldirq != NO_IRQ) {
/* restore old irq */
request_irq(oldirq, lp_interrupt, SA_INTERRUPT, "printer", NULL);
} else {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
return retval;
}
}
if (oldirq != NO_IRQ && newirq == NO_IRQ) {
/* We don't need the buffer */
kfree_s(lp->lp_buffer, LP_BUFFER_SIZE);
lp->lp_buffer = NULL;
}
LP_IRQ(minor) = newirq;
lp_reset(minor);
break;
}
case LPGETIRQ:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(int));
if (retval)
return retval;
memcpy_tofs((int *) arg, &LP_IRQ(minor), sizeof(int));
break;
case LPGETSTATUS:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(int));
if (retval)
return retval;
else {
int status = LP_S(minor);
memcpy_tofs((int *) arg, &status, sizeof(int));
}
break;
case LPRESET:
lp_reset(minor);
break;
case LPGETSTATS:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct lp_stats));
if (retval)
return retval;
else {
memcpy_tofs((int *) arg, &LP_STAT(minor), sizeof(struct lp_stats));
if (suser())
memset(&LP_STAT(minor), 0, sizeof(struct lp_stats));
}
break;
case LPGETFLAGS:
retval = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(int));
if (retval)
return retval;
else {
int status = LP_F(minor);
memcpy_tofs((int *) arg, &status, sizeof(int));
}
break;
default:
retval = -EINVAL;
}
return retval;
}
 
 
static struct file_operations lp_fops = {
lp_lseek,
NULL, /* lp_read */
lp_write,
NULL, /* lp_readdir */
NULL, /* lp_select */
lp_ioctl,
NULL, /* lp_mmap */
lp_open,
lp_release
};
 
static int lp_probe(int offset)
{
int base, size;
unsigned int testvalue;
 
base = LP_B(offset);
if (base == 0)
return -1; /* de-configured by command line */
if (LP_IRQ(offset) != NO_IRQ && LP_IRQ(offset) > 15)
return -1; /* bogus interrupt value */
size = (base == 0x3bc)? 3 : 8;
if (check_region(base, size) < 0)
return -1;
/* write to port & read back to check */
outb_p(LP_DUMMY, base);
udelay(LP_DELAY);
testvalue = inb_p(base);
if (testvalue == LP_DUMMY) {
LP_F(offset) |= LP_EXIST;
lp_reset(offset);
printk(KERN_INFO "lp%d at 0x%04x, ", offset, base);
request_region(base, size, "lp");
if (LP_IRQ(offset) != NO_IRQ)
printk("(irq = %d)\n", LP_IRQ(offset));
else
printk("(polling)\n");
return 1;
} else
return 0;
}
 
/* Command line parameters:
 
When the lp driver is built in to the kernel, you may use the
LILO/LOADLIN command line to set the port addresses and interrupts
that the driver will use.
 
Syntax: lp=port0[,irq0[,port1[,irq1[,port2[,irq2]]]]]
 
For example: lp=0x378,0 or lp=0x278,5,0x378,7
 
Note that if this feature is used, you must specify *all* the ports
you want considered, there are no defaults. You can disable a
built-in driver with lp=0 .
 
*/
 
void lp_setup(char *str, int *ints)
 
{
LP_B(0) = ((ints[0] > 0) ? ints[1] : 0 );
LP_IRQ(0) = ((ints[0] > 1) ? ints[2] : NO_IRQ );
LP_B(1) = ((ints[0] > 2) ? ints[3] : 0 );
LP_IRQ(1) = ((ints[0] > 3) ? ints[4] : NO_IRQ );
LP_B(2) = ((ints[0] > 4) ? ints[5] : 0 );
LP_IRQ(2) = ((ints[0] > 5) ? ints[6] : NO_IRQ );
}
 
#ifdef MODULE
static int io[] = {0, 0, 0};
static int irq[] = {NO_IRQ, NO_IRQ, NO_IRQ};
 
#define lp_init init_module
#endif
 
int lp_init(void)
{
int offset = 0;
int count = 0;
#ifdef MODULE
int failed = 0;
#endif
 
if (register_chrdev(LP_MAJOR,"lp",&lp_fops)) {
printk("lp: unable to get major %d\n", LP_MAJOR);
return -EIO;
}
#ifdef MODULE
/* When user feeds parameters, use them */
for (offset=0; offset < LP_NO; offset++) {
int specified=0;
 
if (io[offset] != 0) {
LP_B(offset) = io[offset];
specified++;
}
if (irq[offset] != NO_IRQ) {
LP_IRQ(offset) = irq[offset];
specified++;
}
if (specified) {
if (lp_probe(offset) <= 0) {
printk(KERN_INFO "lp%d: Not found\n", offset);
failed++;
} else
count++;
}
}
/* Successful specified devices increase count
* Unsuccessful specified devices increase failed
*/
if (count)
return 0;
if (failed) {
printk(KERN_INFO "lp: No override devices found.\n");
unregister_chrdev(LP_MAJOR,"lp");
return -EIO;
}
/* Only get here if there were no specified devices. To continue
* would be silly since the above code has scribbled all over the
* probe list.
*/
#endif
/* take on all known port values */
for (offset = 0; offset < LP_NO; offset++) {
int ret = lp_probe(offset);
if (ret < 0)
continue;
count += ret;
}
if (count == 0)
printk("lp: Driver configured but no interfaces found.\n");
 
return 0;
}
 
#ifdef MODULE
void cleanup_module(void)
{
int offset;
 
unregister_chrdev(LP_MAJOR,"lp");
for (offset = 0; offset < LP_NO; offset++) {
int base, size;
base = LP_B(offset);
size = (base == 0x3bc)? 3 : 8;
if (LP_F(offset) & LP_EXIST)
release_region(LP_B(offset),size);
}
}
#endif
/pty.c
0,0 → 1,364
/*
* linux/arch/arm/drivers/char/pty.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Modifications for ARM processor Copyright (C) 1995, 1996 Russell King.
*/
 
/*
* pty.c
*
* This module exports the following pty function:
*
* int pty_open(struct tty_struct * tty, struct file * filp);
*/
 
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/fcntl.h>
#include <linux/string.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/system.h>
#include <asm/bitops.h>
 
struct pty_struct {
int magic;
struct wait_queue * open_wait;
};
 
#define PTY_MAGIC 0x5001
 
#define PTY_BUF_SIZE 4096/2
 
#if PAGE_SIZE > 8192
static inline unsigned long alloc_pty_buffer (int prio)
{
return (unsigned long)kmalloc (PTY_BUF_SIZE*2, prio);
}
 
static inline void free_pty_buffer (unsigned long buffer)
{
kfree ((void *)buffer);
}
#else
static inline unsigned long alloc_pty_buffer (int prio)
{
return get_free_page(prio);
}
 
static inline void free_pty_buffer (unsigned long buffer)
{
free_page(buffer);
}
#endif
 
/*
* tmp_buf is used as a temporary buffer by pty_write. We need to
* lock it in case the memcpy_fromfs blocks while swapping in a page,
* and some other program tries to do a pty write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the PTY's, since it significantly saves memory if
* large numbers of PTY's are open.
*/
static unsigned char *tmp_buf;
static struct semaphore tmp_buf_sem = MUTEX;
 
struct tty_driver pty_driver, pty_slave_driver;
struct tty_driver old_pty_driver, old_pty_slave_driver;
static int pty_refcount;
 
static struct tty_struct *pty_table[NR_PTYS];
static struct termios *pty_termios[NR_PTYS];
static struct termios *pty_termios_locked[NR_PTYS];
static struct tty_struct *ttyp_table[NR_PTYS];
static struct termios *ttyp_termios[NR_PTYS];
static struct termios *ttyp_termios_locked[NR_PTYS];
static struct pty_struct pty_state[NR_PTYS];
 
#define MIN(a,b) ((a) < (b) ? (a) : (b))
 
static void pty_close(struct tty_struct * tty, struct file * filp)
{
if (!tty)
return;
if (tty->driver.subtype == PTY_TYPE_MASTER) {
if (tty->count > 1)
printk("master pty_close: count = %d!!\n", tty->count);
} else {
if (tty->count > 2)
return;
}
wake_up_interruptible(&tty->read_wait);
wake_up_interruptible(&tty->write_wait);
if (!tty->link)
return;
wake_up_interruptible(&tty->link->read_wait);
wake_up_interruptible(&tty->link->write_wait);
set_bit(TTY_OTHER_CLOSED, &tty->link->flags);
if (tty->driver.subtype == PTY_TYPE_MASTER) {
tty_hangup(tty->link);
set_bit(TTY_OTHER_CLOSED, &tty->flags);
}
}
 
/*
* The unthrottle routine is called by the line discipline to signal
* that it can receive more characters. For PTY's, the TTY_THROTTLED
* flag is always set, to force the line discipline to always call the
* unthrottle routine when there are fewer than TTY_THRESHOLD_UNTHROTTLE
* characters in the queue. This is necessary since each time this
* happens, we need to wake up any sleeping processes that could be
* (1) trying to send data to the pty, or (2) waiting in wait_until_sent()
* for the pty buffer to be drained.
*/
static void pty_unthrottle(struct tty_struct * tty)
{
struct tty_struct *o_tty = tty->link;
 
if (!o_tty)
return;
 
if ((o_tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
o_tty->ldisc.write_wakeup)
(o_tty->ldisc.write_wakeup)(o_tty);
wake_up_interruptible(&o_tty->write_wait);
set_bit(TTY_THROTTLED, &tty->flags);
}
 
static int pty_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
struct tty_struct *to = tty->link;
int c=0, n, r;
char *temp_buffer;
 
if (!to || tty->stopped)
return 0;
 
if (from_user) {
down(&tmp_buf_sem);
temp_buffer = tmp_buf +
((tty->driver.subtype-1) * PTY_BUF_SIZE);
while (count > 0) {
n = MIN(count, PTY_BUF_SIZE);
memcpy_fromfs(temp_buffer, buf, n);
r = to->ldisc.receive_room(to);
if (r <= 0)
break;
n = MIN(n, r);
to->ldisc.receive_buf(to, temp_buffer, 0, n);
buf += n; c+= n;
count -= n;
}
up(&tmp_buf_sem);
} else {
c = MIN(count, to->ldisc.receive_room(to));
to->ldisc.receive_buf(to, buf, 0, c);
}
 
return c;
}
 
static int pty_write_room(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
 
if (!to || tty->stopped)
return 0;
 
return to->ldisc.receive_room(to);
}
 
/*
* Modified for asymmetric master/slave behavior
* The chars_in_buffer() value is used by the ldisc select() function
* to hold off writing when chars_in_buffer > WAKEUP_CHARS (== 256).
* To allow typed-ahead commands to accumulate, the master side returns 0
* until the buffer is half full. The slave side returns the true count.
*/
static int pty_chars_in_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
int count;
 
if (!to || !to->ldisc.chars_in_buffer)
return 0;
 
/* The ldisc must report 0 if no characters available to be read */
count = to->ldisc.chars_in_buffer(to);
 
if (tty->driver.subtype == PTY_TYPE_SLAVE) return count;
 
/*
* Master side driver ... return 0 if the other side's read buffer
* is less than half full. This allows room for typed-ahead commands
* with a reasonable margin to avoid overflow.
*/
return ((count < N_TTY_BUF_SIZE/2) ? 0 : count);
}
 
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
 
if (!to)
return;
 
if (to->ldisc.flush_buffer)
to->ldisc.flush_buffer(to);
 
if (to->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait);
}
}
 
int pty_open(struct tty_struct *tty, struct file * filp)
{
#if PTY_SLAVE_WAITS_ON_OPEN
struct wait_queue wait = { current, NULL };
#endif
int retval;
int line;
struct pty_struct *pty;
 
if (!tty || !tty->link)
return -ENODEV;
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PTYS))
return -ENODEV;
pty = pty_state + line;
tty->driver_data = pty;
 
if (!tmp_buf) {
unsigned long page = alloc_pty_buffer(GFP_KERNEL);
if (!tmp_buf) {
if (!page)
return -ENOMEM;
tmp_buf = (unsigned char *) page;
} else
free_pty_buffer(page);
}
 
clear_bit(TTY_OTHER_CLOSED, &tty->link->flags);
wake_up_interruptible(&pty->open_wait);
set_bit(TTY_THROTTLED, &tty->flags);
if (filp->f_flags & O_NDELAY)
return 0;
/*
* If we're opening the master pty, just return. If we're
* trying to open the slave pty, then we have to wait for the
* master pty to open.
*/
if (tty->driver.subtype == PTY_TYPE_MASTER)
return 0;
retval = 0;
#if PTY_SLAVE_WAITS_ON_OPEN
add_wait_queue(&pty->open_wait, &wait);
while (1) {
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
/*
* Block until the master is open...
*/
current->state = TASK_INTERRUPTIBLE;
if (tty->link->count &&
!test_bit(TTY_OTHER_CLOSED, &tty->flags))
break;
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&pty->open_wait, &wait);
#else
if (!tty->link->count || test_bit(TTY_OTHER_CLOSED, &tty->flags))
retval = -EPERM;
#endif
return retval;
}
 
static void pty_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
tty->termios->c_cflag &= ~(CSIZE | PARENB);
tty->termios->c_cflag |= (CS8 | CREAD);
}
 
int pty_init(void)
{
memset(&pty_state, 0, sizeof(pty_state));
memset(&pty_driver, 0, sizeof(struct tty_driver));
pty_driver.magic = TTY_DRIVER_MAGIC;
pty_driver.name = "pty";
pty_driver.major = PTY_MASTER_MAJOR;
pty_driver.minor_start = 0;
pty_driver.num = NR_PTYS;
pty_driver.type = TTY_DRIVER_TYPE_PTY;
pty_driver.subtype = PTY_TYPE_MASTER;
pty_driver.init_termios = tty_std_termios;
pty_driver.init_termios.c_iflag = 0;
pty_driver.init_termios.c_oflag = 0;
pty_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
pty_driver.init_termios.c_lflag = 0;
pty_driver.flags = TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW;
pty_driver.refcount = &pty_refcount;
pty_driver.table = pty_table;
pty_driver.termios = pty_termios;
pty_driver.termios_locked = pty_termios_locked;
pty_driver.other = &pty_slave_driver;
 
pty_driver.open = pty_open;
pty_driver.close = pty_close;
pty_driver.write = pty_write;
pty_driver.write_room = pty_write_room;
pty_driver.flush_buffer = pty_flush_buffer;
pty_driver.chars_in_buffer = pty_chars_in_buffer;
pty_driver.unthrottle = pty_unthrottle;
pty_driver.set_termios = pty_set_termios;
 
pty_slave_driver = pty_driver;
pty_slave_driver.name = "ttyp";
pty_slave_driver.subtype = PTY_TYPE_SLAVE;
pty_slave_driver.major = PTY_SLAVE_MAJOR;
pty_slave_driver.minor_start = 0;
pty_slave_driver.init_termios = tty_std_termios;
pty_slave_driver.init_termios.c_cflag = B38400 | CS8 | CREAD;
pty_slave_driver.table = ttyp_table;
pty_slave_driver.termios = ttyp_termios;
pty_slave_driver.termios_locked = ttyp_termios_locked;
pty_slave_driver.other = &pty_driver;
 
old_pty_driver = pty_driver;
old_pty_driver.major = TTY_MAJOR;
old_pty_driver.minor_start = 128;
old_pty_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_driver.other = &old_pty_slave_driver;
 
old_pty_slave_driver = pty_slave_driver;
old_pty_slave_driver.major = TTY_MAJOR;
old_pty_slave_driver.minor_start = 192;
old_pty_slave_driver.num = (NR_PTYS > 64) ? 64 : NR_PTYS;
old_pty_slave_driver.other = &old_pty_driver;
 
tmp_buf = 0;
 
if (tty_register_driver(&pty_driver))
panic("Couldn't register pty driver");
if (tty_register_driver(&pty_slave_driver))
panic("Couldn't register pty slave driver");
if (tty_register_driver(&old_pty_driver))
panic("Couldn't register compat pty driver");
if (tty_register_driver(&old_pty_slave_driver))
panic("Couldn't register compat pty slave driver");
 
return 0;
}
/vt_kern.h
0,0 → 1,226
/*
* linux/arch/arm/drivers/char/vt.h
*
* Virtual Terminal bits...
*/
 
#ifndef _VT_KERN_H
#define _VT_KERN_H
 
#include <linux/vt.h>
#define NPAR 16
#define SEL_BUFFER_SIZE 4096
 
struct vc_state {
unsigned char forecol; /* foreground */
unsigned char backcol; /* background */
unsigned char x; /* x position */
unsigned char y; /* y position */
#define FLG_BOLD 0x01
#define FLG_ITALIC 0x02
#define FLG_UNDERLINE 0x04
#define FLG_FLASH 0x08
#define FLG_INVERSE 0x10
#define FLG_CHRSET 0x20
unsigned char flags; /* special flags */
unsigned char G0_charset; /* G0 character set */
unsigned char G1_charset; /* G1 character set */
unsigned char __unused; /* unused */
};
 
extern struct con_struct *___GCC_HACK_TO_STOP_ITS_SILLY_COMPLAINTS___;
 
struct console_driver {
void (*gotoxy)(struct con_struct *vcd);
void (*scroll_up)(struct con_struct *vcd,unsigned int,unsigned int,unsigned int);
void (*scroll_down)(struct con_struct *vcd,unsigned int,unsigned int,unsigned int);
unsigned long *(*buffer_pos)(struct con_struct *vcd,unsigned int offset);
void (*delete_char)(struct con_struct *vcd,unsigned int);
void (*insert_char)(struct con_struct *vcd,unsigned int);
void (*put_char)(struct con_struct *vcd,unsigned long);
void (*write_char)(struct con_struct *vcd,unsigned long);
void (*erase)(struct con_struct *vcd,unsigned char,unsigned char,unsigned char,unsigned char);
};
 
struct con_struct {
struct {
unsigned int origin; /* address of top-left */
unsigned int pos; /* current position into screen */
unsigned long *palette_entries; /* Current palette */
unsigned int cursoron; /* Cursor on count */
} screen;
 
struct {
unsigned long *buffer; /* pointer to actual buffer */
unsigned int size; /* size of buffer */
unsigned int pos; /* current position into buffer */
unsigned char kmalloced : 1; /* buffer kmalloced */
} buffer;
 
struct console_driver driver;
/*
* State
*/
struct vc_state curstate; /* current state */
struct vc_state savedstate; /* saved state */
unsigned long combined_state; /* combined state */
unsigned long cached_backcolwrd; /* cached background colour */
unsigned long tab_stop[5]; /* tab stops */
unsigned short *translate; /* translation table */
 
unsigned char top; /* top of scrollable region */
unsigned char bottom; /* bottom of scrollable region */
unsigned char def_forecol; /* default foreground */
unsigned char def_backcol; /* default background */
 
unsigned char cursor_count; /* on/off cursor count (int) */
 
unsigned char disp_ctrl : 1; /* display control characters */
unsigned char toggle_meta : 1; /* toggle high bit */
unsigned char decscnm : 1; /* screen mode */
unsigned char decom : 1; /* origin mode */
unsigned char decawm : 1; /* autowrap mode */
unsigned char deccm : 1; /* cursor visible */
unsigned char decim : 1; /* insert mode */
unsigned char deccolm : 1; /* 80/132 col mode */
 
unsigned char report_mouse : 2; /* mouse reporting? */
unsigned char need_wrap : 1; /* need to wrap */
unsigned char ques : 1;
/*
* UTF
*/
unsigned char utf : 1; /* Unicode UTF-8 encoding */
 
unsigned char utf_count; /* UTF character count */
unsigned long utf_char; /* UTF character built */
unsigned long npar; /* number of params */
unsigned long par[NPAR]; /* params */
unsigned int bell_pitch; /* console bell pitch */
unsigned int bell_duration; /* console bell duration */
unsigned char state; /* Current escape state */
 
};
 
struct vt_data {
unsigned char numcolumns; /* number of columns */
unsigned char numrows; /* number of rows */
unsigned char __unused[2];
struct vt *fgconsole; /* displayed VC */
struct vt *blanked; /* blanked VC */
struct {
unsigned char bitsperpix; /* bits per pixel */
unsigned char bytespercharh; /* horiz. bytes a char takes */
unsigned char bytespercharv; /* vert. bytes a char takes */
unsigned char __unused[1];
unsigned long sizerow; /* size of a row of chars */
unsigned long totsize; /* total character size */
unsigned long memstart; /* video mem start */
unsigned long memsize; /* video mem size */
unsigned long memend; /* video mem end */
unsigned long blankinterval; /* blank interval */
} screen;
struct {
unsigned long *buffer; /* address of screen buffer */
unsigned long totsize; /* total buffer size */
unsigned long lastorigin; /* last origin address */
unsigned long origin; /* current origin */
unsigned long sizerow; /* size of a row of chars */
} buffer;
struct {
struct vt *vt; /* selected VC */
int start; /* start offset */
int end; /* end offset */
int length; /* buffer length */
char *buffer; /* buffer */
} select;
};
 
extern struct vt_data vtdata;
extern struct tty_driver console_driver;
 
struct vt_struct {
unsigned char vc_mode; /* hmm... */
unsigned char vc_kbdraw;
unsigned char vc_kbde0;
unsigned char vc_kbdleds;
struct vt_mode vt_mode;
pid_t vt_pid;
struct vt *vt_newvt; /* VT to switch to.. */
struct wait_queue *paste_wait;
unsigned char * xmit_buf;
unsigned int xmit_cnt;
unsigned int xmit_out;
unsigned int xmit_in;
unsigned int xmitting;
};
 
struct vt {
/*
* Per-console data
*/
struct con_struct *vcd;
/*
* Keyboard stuff
*/
struct kbd_struct *kbd;
/*
* VT stuff
*/
struct vt_struct *vtd;
/*
* tty that this VT is connected to
*/
struct tty_struct **tty;
/*
* tty number of this vt struct
*/
unsigned char num;
/*
* is this vt allocated and initialised?
*/
unsigned char allocinit;
/*
* might add scrmem at some time, to have everything
* in one place - the disadvantage would be that
* vc_cons etc can no longer be static
*/
};
 
extern struct vt vt_con_data[];
 
#define VT_IS_IN_USE(i) (vt_driver.table[i] && vt_driver.table[i]->count)
#define VT_BUSY(i) (VT_IS_IN_USE(i) || vt_con_data + i == vtdata.fgconsole || vt_con_data + i == vtdata.select.vt)
 
extern inline int vt_allocated (const struct vt * const vt)
{
return vt->allocinit != 0;
}
 
extern void vt_reset (const struct vt *vt);
extern void vt_completechangeconsole (const struct vt *vt);
extern void vt_changeconsole (struct vt *newvt);
extern void vt_mksound (unsigned int count, unsigned int vol, unsigned int ticks);
extern int vt_deallocate (int arg);
extern int vt_resize (int cols, int rows);
 
/*
* Blanking ...
*/
extern void vt_pokeblankedconsole (void);
extern void vt_do_unblankscreen (void);
extern void vt_do_blankscreen (int nopowersave);
 
/*
* Screen switching...
*/
extern void vt_updatescreen (const struct vt *newvt);
 
/*
* Initialisation ...
*/
extern unsigned long vt_pre_init (unsigned long kmem);
extern void vt_post_init (void);
 
#endif /* _VT_KERN_H */
/console-trio.c
0,0 → 1,301
/*
* linux/arch/arm/drivers/char/console-dummy.c
*
* Modifications (C) 1996 Russell King
*/
 
/*
* This module exports the console io functions:
*
* 'int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)'
* 'unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)'
* 'void vcd_disallocate (struct vt *vt)'
* 'int vcd_resize (unsigned long lines, unsigned long cols)'
* 'void vcd_blankscreen (int nopowersave)'
* 'void vcd_unblankscreen (void)'
* 'void vcd_savestate (const struct vt *vt, int blanked)'
* 'void vcd_restorestate (const struct vt *vt)'
* 'void vcd_setup_graphics (const struct vt *vt)'
* 'int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)'
* 'int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)'
*
*
* 'int vc_allocate(unsigned int console)'
* 'int vc_cons_allocated (unsigned int console)'
* 'int vc_resize(unsigned long lines,unsigned long cols)'
* 'void vc_disallocate(unsigned int currcons)'
*
* 'unsigned long con_init(unsigned long)'
* S 'int con_open(struct tty_struct *tty,struct file *filp)'
* S 'void con_write(struct tty_struct *tty)'
* S 'void console_print(const char *b)'
* 'void update_screen(int new_console)'
*
* 'void blank_screen(void)'
* 'void unblank_screen(void)'
* 'void scrollback(int lines)' *
* 'void scrollfront(int lines)' *
* 'int do_screendump(int arg)'
*
* 'int con_get_font(char *)'
* 'int con_set_font(char *)'
* 'int con_get_trans(char *)'
* 'int con_set_trans(char *)'
*
* 'int mouse_reporting(void)'
*/
 
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/kd.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/irq.h>
 
#include "kbd_kern.h"
#include "consolemap.h"
#include "vt_kern.h"
#include "selection.h"
 
extern void register_console(void (*proc)(const char *));
static int printable; /* Is console ready for printing? */
 
int bytes_per_char_h;
int bytes_per_char_v;
 
#if 0
#define SERIAL_ECHO_PORT 0x2f8
#define SERIAL_ECHO_DIVISOR 3
 
#include "serialecho.c"
#endif
 
/*
* functions to handle /dev/fb
*/
int con_fb_read(char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_write(const char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_mmap(unsigned long vma_start, unsigned long vma_offset,
unsigned long vma_end, pgprot_t prot)
{
return -EINVAL;
}
 
void no_scroll (char *str, int *ints)
{
}
 
void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry)
{
}
 
int mouse_reporting (void)
{
return 0;
}
 
static inline unsigned long *bufferpos (const struct vt * const vt, int offset)
{
return NULL;
}
 
void invert_screen (const struct vt * const vt, unsigned int offset, unsigned int count)
{
}
 
void complement_pos (const struct vt * const vt, unsigned int offset)
{
}
 
unsigned long screen_word (const struct vt * const vt, unsigned int offset)
{
return 0;
}
 
int scrw2glyph (unsigned long scr_word)
{
return 0;
}
 
unsigned long *screen_pos (const struct vt * const vt, unsigned int offset)
{
return NULL;
}
 
void getconsxy (const struct vt * const vt, char *p)
{
p[0] = p[1] = 0;
}
 
void putconsxy (const struct vt * const vt, char *p)
{
}
 
void console_print(const char *b)
{
static int printing = 0;
 
if (!printable || printing)
return; /* console not yet initialized */
 
printing = 1;
#if 0
serial_echo_print (b);
#endif
printing = 0;
}
 
void update_scrmem (const struct vt * const vt, int start, int length)
{
}
 
void set_scrmem (const struct vt * const vt, long offset)
{
}
 
int con_set_font (char *arg)
{
return -EINVAL;
}
 
int con_get_font (char *arg)
{
return -EINVAL;
}
 
void con_reset_palette (const struct vt * const vt)
{
}
 
void con_set_palette (const struct vt * const vt)
{
}
 
/* == arm specific console code ============================================================== */
 
int do_screendump(int arg)
{
return -EINVAL;
}
 
/*===============================================================================================*/
 
int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)
{
return 0;
}
 
unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)
{
#if 0
serial_echo_init (SERIAL_ECHO_PORT);
#endif
printable = 1;
 
printk ("Console: dummy console driver\n");
register_console (console_print);
return kmem;
}
 
void vcd_disallocate (struct vt *vt)
{
}
 
int vcd_resize(unsigned long lines, unsigned long cols)
{/* TODO */
return -ENOMEM;
}
 
void vcd_blankscreen(int nopowersave)
{
}
 
void vcd_unblankscreen (void)
{
}
 
void vcd_savestate (const struct vt *vt, int blanked)
{
}
 
void vcd_restorestate (const struct vt *vt)
{
}
 
void vcd_setup_graphics (const struct vt *vt)
{
}
 
static char vcd_buffer[128];
int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)
{
int tmp = count;
while (tmp) {
int i;
 
i = tmp < 127 ? tmp : 127;
 
tmp -= i;
memcpy (vcd_buffer, buf, i);
buf += i;
 
vcd_buffer[i] = 0;
#if 0
serial_echo_print(vcd_buffer);
#endif
}
return count;
}
 
int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)
{
switch (cmd) {
case PIO_FONT:
case GIO_FONT:
case PIO_SCRNMAP:
case GIO_SCRNMAP:
case PIO_UNISCRNMAP:
case GIO_UNISCRNMAP:
case PIO_UNIMAPCLR:
case PIO_UNIMAP:
case GIO_UNIMAP:
return -EINVAL;
 
default:
return -ENOIOCTLCMD;
}
}
 
void console_map_init (void)
{
}
 
/*
* Report the current status of the vc. This is exported to modules (ARub)
*/
int con_get_info(int *mode, int *shift, int *col, int *row,
struct tty_struct **tty)
{
if (mode) *mode = 0;
if (shift) *shift = 0;
if (col) *col = 0;
if (row) *row = 0;
if (tty) *tty = NULL;
return 0;
}
/trioserial.c
0,0 → 1,1875
/* TRIO chip serial port driver
*
* Based on:
*
* drivers/char/68302serial.c
*/
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/config.h>
#include <linux/major.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/kernel.h>
 
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/arch-trio/irq.h>
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/bitops.h>
#include <asm/delay.h>
#if 0
#include <asm/kdebug.h>
#endif
 
#include "trioserial.h"
 
#define USE_INTS 1
#define US_NB 2
#define UART_CLOCK (ARM_CLK/16)
 
 
#define XMIT_SERIAL_SIZE PAGE_SIZE
#define RX_SERIAL_SIZE PAGE_SIZE
 
 
static struct uart_regs *uarts[US_NB] = {
(struct uart_regs*)USARTA_BASE, (struct uart_regs*)USARTB_BASE
};
static struct trio_serial trio_info[US_NB];
struct tty_struct trio_ttys[US_NB];
 
/* Console hooks... */
/*static int m68k_cons_chanout = 0;
static int m68k_cons_chanin = 0;*/
 
struct trio_serial *trio_consinfo = 0;
 
#if 0
static unsigned char kgdb_regs[16] = {
0, 0, 0, /* write 0, 1, 2 */
(Rx8 | RxENABLE), /* write 3 */
(X16CLK | SB1 | PAR_EVEN), /* write 4 */
(Tx8 | TxENAB), /* write 5 */
0, 0, 0, /* write 6, 7, 8 */
(NV), /* write 9 */
(NRZ), /* write 10 */
(TCBR | RCBR), /* write 11 */
0, 0, /* BRG time constant, write 12 + 13 */
(BRSRC | BRENABL), /* write 14 */
(DCDIE) /* write 15 */
};
#endif
 
DECLARE_TASK_QUEUE(tq_serial);
 
struct tq_struct serialpoll;
 
struct tty_driver serial_driver, callout_driver;
static int serial_refcount;
 
/* serial subtype definitions */
#define SERIAL_TYPE_NORMAL 1
#define SERIAL_TYPE_CALLOUT 2
/* number of characters left in xmit buffer before we ask for more */
#define WAKEUP_CHARS 256
 
/* Debugging... DEBUG_INTR is bad to use when one of the zs
* lines is your console ;(
*/
#undef SERIAL_DEBUG_INTR
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
 
#define RS_ISR_PASS_LIMIT 256
 
#define _INLINE_
 
static void serpoll(void *data);
 
static void change_speed(struct trio_serial *info);
 
static struct tty_struct *serial_table[US_NB];
static struct termios *serial_termios[US_NB];
static struct termios *serial_termios_locked[US_NB];
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
static char prompt0;
static void xmit_char(struct trio_serial* info, char ch);
static void xmit_string(struct trio_serial *info, char *p, int len);
static void start_rx(struct trio_serial *info);
static void wait_EOT(struct uart_regs*);
static void uart_init(struct trio_serial *info);
static void uart_speed(struct trio_serial *info, unsigned cflag);
 
static void tx_enable(struct uart_regs *uart);
static void rx_enable(struct uart_regs *uart);
static void tx_disable(struct uart_regs *uart);
static void rx_disable(struct uart_regs *uart);
static void tx_stop(struct uart_regs *uart);
static void tx_start(struct uart_regs *uart, int ints);
static void rx_stop(struct uart_regs *uart);
static void rx_start(struct uart_regs *uart, int ints);
static void set_ints_mode(int yes, struct trio_serial *info);
static void rs_interrupt(struct trio_serial *info);
extern void show_net_buffers(void);
extern void hard_reset_now(void);
 
 
static void _INLINE_ tx_enable(struct uart_regs *uart){
uart->ier |= US_TXRDY;
}
static void _INLINE_ rx_enable(struct uart_regs *uart){
uart->ier |= US_RXRDY;
}
static void _INLINE_ tx_disable(struct uart_regs *uart){
uart->idr |= US_TXRDY;
}
static void _INLINE_ rx_disable(struct uart_regs *uart){
uart->idr |= US_RXRDY;
}
static void _INLINE_ tx_stop(struct uart_regs *uart){
tx_disable(uart);
uart->cr |= US_RXEN;
}
static void _INLINE_ tx_start(struct uart_regs *uart, int ints){
if(ints)
tx_enable(uart);
uart->cr |= US_TXEN;
}
static void _INLINE_ rx_stop(struct uart_regs *uart){
rx_disable(uart);
uart->cr |= US_RXDIS;
}
static void _INLINE_ rx_start(struct uart_regs *uart, int ints){
if(ints)
rx_enable(uart);
uart->cr |= US_RXEN;
}
 
static void set_ints_mode(int yes, struct trio_serial *info){
info->use_ints = yes;
(yes)?unmask_irq(info->irq):mask_irq(info->irq);
}
 
/*
* tmp_buf is used as a temporary buffer by serial_write. We need to
* lock it in case the memcpy_fromfs blocks while swapping in a page,
* and some other program tries to do a serial write at the same time.
* Since the lock will only come under contention when the system is
* swapping and available memory is low, it makes sense to share one
* buffer across all the serial ports, since it significantly saves
* memory if large numbers of serial ports are open.
*/
static unsigned char tmp_buf[XMIT_SERIAL_SIZE]; /* This is cheating */
static struct semaphore tmp_buf_sem = MUTEX;
 
static inline int serial_paranoia_check(struct trio_serial *info,
dev_t device, const char *routine)
{
#ifdef SERIAL_PARANOIA_CHECK
static const char *badmagic =
"Warning: bad magic number for serial struct (%d, %d) in %s\n";
static const char *badinfo =
"Warning: null trio_serial for (%d, %d) in %s\n";
 
if (!info) {
printk(badinfo, MAJOR(device), MINOR(device), routine);
return 1;
}
if (info->magic != SERIAL_MAGIC) {
printk(badmagic, MAJOR(device), MINOR(device), routine);
return 1;
}
#endif
return 0;
}
 
/* Sets or clears DTR/RTS on the requested line */
static inline void trio_rtsdtr(struct trio_serial *ss, int set)
{
struct uart_regs *uart;
uart = ss->uart;
if(set) {
uart->mc |= US_DTR | US_RTS;
} else {
uart->mc &= ~(u_32)(US_DTR | US_RTS);
}
return;
}
 
static inline void kgdb_chaninit(struct trio_serial *ss, int intson, int bps)
{
#if 0
int brg;
 
if(intson) {
kgdb_regs[R1] = INT_ALL_Rx;
kgdb_regs[R9] |= MIE;
} else {
kgdb_regs[R1] = 0;
kgdb_regs[R9] &= ~MIE;
}
brg = BPS_TO_BRG(bps, ZS_CLOCK/16);
kgdb_regs[R12] = (brg & 255);
kgdb_regs[R13] = ((brg >> 8) & 255);
load_zsregs(ss->trio_channel, kgdb_regs);
#endif
}
 
 
/*
* ------------------------------------------------------------
* rs_stop() and rs_start()
*
* This routines are called before setting or resetting tty->stopped.
* They enable or disable transmitter interrupts, as necessary.
* ------------------------------------------------------------
*/
static void rs_stop(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
unsigned long flags;
 
if (serial_paranoia_check(info, tty->device, "rs_stop"))
return;
save_flags(flags); cli();
tx_stop(info->uart);
rx_stop(info->uart);
restore_flags(flags);
}
 
static void rs_put_char(struct trio_serial *info, char ch)
{
int flags = 0;
save_flags(flags); cli();
wait_EOT(info->uart);
xmit_char(info,ch);
wait_EOT(info->uart);
restore_flags(flags);
}
 
static void rs_start(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
unsigned long flags;
if (serial_paranoia_check(info, tty->device, "rs_start"))
return;
save_flags(flags); cli();
tx_start(info->uart, info->use_ints);
start_rx(info);
restore_flags(flags);
}
 
/* Drop into either the boot monitor or kadb upon receiving a break
* from keyboard/console input.
*/
static void batten_down_hatches(void)
{
/* If we are doing kadb, we call the debugger
* else we just drop into the boot monitor.
* Note that we must flush the user windows
* first before giving up control.
*/
#if 0
if((((unsigned long)linux_dbvec)>=DEBUG_FIRSTVADDR) &&
(((unsigned long)linux_dbvec)<=DEBUG_LASTVADDR))
sp_enter_debugger();
else
panic("trio_serial: batten_down_hatches");
return;
#endif
}
 
/*
* ----------------------------------------------------------------------
*
* Here starts the interrupt handling routines. All of the following
* subroutines are declared as inline and are folded into
* rs_interrupt(). They were separated out for readability's sake.
*
* Note: rs_interrupt() is a "fast" interrupt, which means that it
* runs with interrupts turned off. People who may want to modify
* rs_interrupt() should try to keep the interrupt handler as fast as
* possible. After you are done making modifications, it is not a bad
* idea to do:
*
* gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
*
* and look at the resulting assemble code in serial.s.
*
* - Ted Ts'o (tytso@mit.edu), 7-Mar-93
* -----------------------------------------------------------------------
*/
 
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
static _INLINE_ void rs_sched_event(struct trio_serial *info,
int event)
{
info->event |= 1 << event;
queue_task_irq_off(&info->tqueue, &tq_serial);
mark_bh(SERIAL_BH);
}
 
extern void breakpoint(void); /* For the KGDB frame character */
 
static _INLINE_ void receive_chars(struct trio_serial *info, u_32 status)
{
unsigned char ch;
int count;
struct uart_regs *uart = info->uart;
#if 0
// hack to receive chars by polling from anywhere
struct trio_serial * info1 = &trio_info;
struct tty_struct *tty = info1->tty;
if (!(info->flags & S_INITIALIZED))
return;
#else
struct tty_struct *tty = info->tty;
if (!(info->flags & S_INITIALIZED))
return;
#endif
count = uart->rcr;
// hack to receive chars by polling only BD fields
if (!(status & US_RXRDY) || !count){
return;
}
ch = info->rx_buf[0];
if(info->is_cons) {
if(status & US_RXBRK) { /* whee, break received */
batten_down_hatches();
/*rs_recv_clear(info->trio_channel);*/
return;
} else if (ch == 0x10) { /* ^P */
show_state();
show_free_areas();
show_buffers();
show_net_buffers();
return;
} else if (ch == 0x12) { /* ^R */
hard_reset_now();
return;
}
/* It is a 'keyboard interrupt' ;-) */
wake_up(&keypress_wait);
}
/* Look for kgdb 'stop' character, consult the gdb documentation
* for remote target debugging and arch/sparc/kernel/sparc-stub.c
* to see how all this works.
*/
/*if((info->kgdb_channel) && (ch =='\003')) {
breakpoint();
goto clear_and_exit;
}*/
 
if(!tty)
goto clear_and_exit;
 
if (tty->flip.count >= TTY_FLIPBUF_SIZE)
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
tty->flip.count++;
if(status & US_PARE)
*tty->flip.flag_buf_ptr++ = TTY_PARITY;
else if(status & US_OVRE)
*tty->flip.flag_buf_ptr++ = TTY_OVERRUN;
else if(status & US_FRAME)
*tty->flip.flag_buf_ptr++ = TTY_FRAME;
else
*tty->flip.flag_buf_ptr++ = 0; /* XXX */
*tty->flip.char_buf_ptr++ = ch;
 
queue_task_irq_off(&tty->flip.tqueue, &tq_timer);
 
clear_and_exit:
start_rx(info);
return;
}
 
static _INLINE_ void transmit_chars(struct trio_serial *info)
{
if (info->x_char) {
/* Send next char */
xmit_char(info, info->x_char);
info->x_char = 0;
goto clear_and_return;
}
 
if((info->xmit_cnt <= 0) || info->tty->stopped) {
/* That's peculiar... */
// tx_stop(0);
goto clear_and_return;
}
 
/* Send char */
xmit_char(info, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
 
if (info->xmit_cnt < WAKEUP_CHARS)
rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
if(info->xmit_cnt <= 0) {
// tx_stop(0);
goto clear_and_return;
}
 
clear_and_return:
/* Clear interrupt (should be auto)*/
return;
}
 
static _INLINE_ void status_handle(struct trio_serial *info, u_32 status)
{
#if 0
if(status & DCD) {
if((info->tty->termios->c_cflag & CRTSCTS) &&
((info->curregs[3] & AUTO_ENAB)==0)) {
info->curregs[3] |= AUTO_ENAB;
info->pendregs[3] |= AUTO_ENAB;
write_zsreg(info->trio_channel, 3, info->curregs[3]);
}
} else {
if((info->curregs[3] & AUTO_ENAB)) {
info->curregs[3] &= ~AUTO_ENAB;
info->pendregs[3] &= ~AUTO_ENAB;
write_zsreg(info->trio_channel, 3, info->curregs[3]);
}
}
#endif
/* Whee, if this is console input and this is a
* 'break asserted' status change interrupt, call
* the boot prom.
*/
if((status & US_RXBRK) && info->break_abort)
batten_down_hatches();
 
/* XXX Whee, put in a buffer somewhere, the status information
* XXX whee whee whee... Where does the information go...
*/
return;
}
 
 
/*
* This is the serial driver's generic interrupt routine
*/
void rs_interrupta(int irq, void *dev_id, struct pt_regs * regs){
rs_interrupt(&trio_info[0]);
}
void rs_interruptb(int irq, void *dev_id, struct pt_regs * regs){
rs_interrupt(&trio_info[1]);
}
static void rs_interrupt(struct trio_serial *info)
{
u_32 status;
status = info->uart->csr;
if (status & US_TXRDY) {
transmit_chars(info);
}
if (status & US_RXRDY){
receive_chars(info, status);
}
status_handle(info, status);
if(!info->use_ints){
serialpoll.data = (void *)info;
queue_task_irq_off(&serialpoll, &tq_timer);
}
return;
}
static void serpoll(void *data){
struct trio_serial * info = data;
rs_interrupt(info);
}
 
/*
* -------------------------------------------------------------------
* Here ends the serial interrupt routines.
* -------------------------------------------------------------------
*/
 
/*
* This routine is used to handle the "bottom half" processing for the
* serial driver, known also the "software interrupt" processing.
* This processing is done at the kernel interrupt level, after the
* rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON. This
* is where time-consuming activities which can not be done in the
* interrupt driver proper are done; the interrupt driver schedules
* them using rs_sched_event(), and they get done here.
*/
static void do_serial_bh(void)
{
run_task_queue(&tq_serial);
}
 
static void do_softint(void *private_)
{
struct trio_serial *info = (struct trio_serial *) private_;
struct tty_struct *tty;
tty = info->tty;
if (!tty)
return;
 
if (clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
wake_up_interruptible(&tty->write_wait);
}
}
 
/*
* This routine is called from the scheduler tqueue when the interrupt
* routine has signalled that a hangup has occurred. The path of
* hangup processing is:
*
* serial interrupt routine -> (scheduler tqueue) ->
* do_serial_hangup() -> tty->hangup() -> rs_hangup()
*
*/
static void do_serial_hangup(void *private_)
{
struct trio_serial *info = (struct trio_serial *) private_;
struct tty_struct *tty;
tty = info->tty;
if (!tty)
return;
 
tty_hangup(tty);
}
 
 
/*
* This subroutine is called when the RS_TIMER goes off. It is used
* by the serial driver to handle ports that do not have an interrupt
* (irq=0). This doesn't work at all for 16450's, as a sun has a Z8530.
*/
static void rs_timer(void)
{
panic("rs_timer called\n");
return;
}
static u_32 calcCD(u_32 br){
return(UART_CLOCK/br);
}
static void uart_init(struct trio_serial *info){
struct uart_regs* uart;
if(info){
uart = info->uart;
}else{
uart = uarts[0];
}
uart->cr = US_RSTRX|US_RSTTX|US_RSTSTA|US_TXDIS|US_RXDIS;
uart->mr = US_USCLKS(0)|US_CLK0|US_CHMODE(0)|US_NBSTOP(0)|US_PAR(4)|US_CHRL(3);
uart->ier = 0;
uart->idr = US_ALL_INTS;
uart->brgr = calcCD(9600);
uart->rtor = 100; // timeout = value * 4 * bit period
uart->ttgr = 0; // no guard time
uart->rpr = 0;
uart->rcr = 0;
uart->tpr = 0;
uart->tcr = 0;
uart->mc = 0;
}
 
static void uart_speed(struct trio_serial *info, unsigned cflag){
unsigned baud = info->baud;
struct uart_regs *uart = info->uart;
 
uart->cr = US_TXDIS|US_RXDIS;
uart->ier = 0;
uart->idr = US_ALL_INTS;
uart->brgr = calcCD(baud);
uart->rtor = 100; // timeout = value * 4 *bit period
uart->ttgr = 0; // no guard time
uart->rpr = 0;
uart->rcr = 0;
uart->tpr = 0;
uart->tcr =0;
uart->mc = 0;
if (cflag != 0xffff){
uart->mr = US_USCLKS(0)|US_CLK0|US_CHMODE(0)|US_PAR(0);
if ((cflag & CSIZE) == CS8)
uart->mr |= US_CHRL(3); // 8 bit char
else
uart->mr |= US_CHRL(2); // 7 bit char
if (cflag & CSTOPB)
uart->mr |= US_NBSTOP(2); // 2 stop bits
if (!(cflag & PARENB))
uart->mr |= US_PAR(4); // parity disabled
else
if (cflag & PARODD)
uart->mr |= US_PAR(1); // odd parity
}
tx_start(uart, info->use_ints);
start_rx(info);
}
static void wait_EOT(struct uart_regs *uart){
volatile u_32 status;
volatile struct uart_regs* puart;
puart = (volatile struct uart_regs*)uart;
while(1){
status = puart->csr;
if(status & US_TXRDY)
break;
}
}
static int startup(struct trio_serial * info)
{
unsigned long flags;
 
if (info->flags & S_INITIALIZED)
return 0;
 
if (!info->xmit_buf) {
info->xmit_buf = (unsigned char *) get_free_page(GFP_KERNEL);
if (!info->xmit_buf)
return -ENOMEM;
}
if (!info->rx_buf) {
info->rx_buf = (unsigned char *) get_free_page(GFP_KERNEL);
if (!info->rx_buf)
return -ENOMEM;
}
save_flags(flags); cli();
#ifdef SERIAL_DEBUG_OPEN
printk("starting up ttyS%d (irq %d)...\n", info->line, info->irq);
#endif
/*
* Clear the FIFO buffers and disable them
* (they will be reenabled in change_speed())
*/
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
 
/*
* and set the speed of the serial port
*/
 
uart_init(info);
set_ints_mode(1, info);
change_speed(info);
 
info->flags |= S_INITIALIZED;
restore_flags(flags);
return 0;
}
 
/*
* This routine will shutdown a serial port; interrupts are disabled, and
* DTR is dropped if the hangup on close termio flag is on.
*/
static void shutdown(struct trio_serial * info)
{
unsigned long flags;
 
tx_disable(info->uart);
rx_disable(info->uart);
rx_stop(info->uart); /* All off! */
if (!(info->flags & S_INITIALIZED))
return;
 
#ifdef SERIAL_DEBUG_OPEN
printk("Shutting down serial port %d (irq %d)....\n", info->line,
info->irq);
#endif
save_flags(flags); cli(); /* Disable interrupts */
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
info->xmit_buf = 0;
}
 
if (info->tty)
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~S_INITIALIZED;
restore_flags(flags);
}
 
/* rate = 1036800 / ((65 - prescale) * (1<<divider)) */
 
static int baud_table[] = {
0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
9600, 19200, 38400, 57600, 115200, 0 };
 
/*
* This routine is called to set the UART divisor registers to match
* the specified baud rate for a serial port.
*/
static void change_speed(struct trio_serial *info)
{
unsigned cflag;
int i;
 
if (!info->tty || !info->tty->termios)
return;
cflag = info->tty->termios->c_cflag;
 
/* First disable the interrupts */
tx_stop(info->uart);
rx_stop(info->uart);
/* set the baudrate */
i = cflag & CBAUD;
 
info->baud = baud_table[i];
uart_speed(info, cflag);
start_rx(info);
tx_start(info->uart, info->use_ints);
return;
}
static void start_rx(struct trio_serial *info){
struct uart_regs *uart = info->uart;
uart->rcr = (u_32)RX_SERIAL_SIZE;
uart->rpr = (u_32)info->rx_buf;
rx_start(uart, info->use_ints);
}
static void xmit_char(struct trio_serial *info, char ch){
prompt0 = ch;
xmit_string(info, &prompt0, 1);
}
static void xmit_string(struct trio_serial *info, char *p, int len){
info->uart->tcr = (u_32)len;
info->uart->tpr = (u_32)p;
tx_start(info->uart, info->use_ints);
}
 
#if 0
/* These are for receiving and sending characters under the kgdb
* source level kernel debugger.
*/
void putDebugChar(char kgdb_char)
{
struct sun_zschannel *chan = trio_kgdbchan;
 
while((chan->control & Tx_BUF_EMP)==0)
udelay(5);
 
chan->data = kgdb_char;
}
 
char getDebugChar(void)
{
struct sun_zschannel *chan = trio_kgdbchan;
 
while((chan->control & Rx_CH_AV)==0)
barrier();
return chan->data;
}
#endif
 
/*
* Fair output driver allows a process to speak.
*/
static void rs_fair_output( struct trio_serial *info)
{
int left; /* Output no more than that */
unsigned long flags;
char c;
 
if (info == 0) return;
if (info->xmit_buf == 0) return;
 
save_flags(flags); cli();
left = info->xmit_cnt;
while (left != 0) {
c = info->xmit_buf[info->xmit_tail];
info->xmit_tail = (info->xmit_tail+1) & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
restore_flags(flags);
 
rs_put_char(info, c);
 
save_flags(flags); cli();
left = MIN(info->xmit_cnt, left-1);
}
 
/* Last character is being transmitted now (hopefully). */
// udelay(20);
wait_EOT(info->uart);
restore_flags(flags);
return;
}
 
/*
* trio_console_print is registered for printk.
*/
static int console_initialized = 0;
static void init_console(void){
struct trio_serial *info;
info = &trio_info[0];
memset(info, 0, sizeof(struct trio_serial));
#if 0
info->uart = uarts[0];
#else
info->uart = (struct uart_regs*)USARTA_BASE;
#endif
info->tty = 0;
info->irqmask = AIC_UA;
info->irq = IRQ_USARTA;
info->port = 1;
info->use_ints = 0;
info->is_cons = 1;
console_initialized = 1;
}
void console_print_trio(const char *p)
{
char c;
struct trio_serial *info;
info = &trio_info[0];
 
// if (!(info->flags & S_INITIALIZED)){
if(!console_initialized){
init_console();
uart_init(info);
info->baud = 9600;
uart_speed(info,0xffff);
}
while((c=*(p++)) != 0) {
if(c == '\n')
rs_put_char(info, '\r');
rs_put_char(info, c);
}
 
/* Comment this if you want to have a strict interrupt-driven output */
// if (!info->use_ints)
// rs_fair_output(info);
 
return;
}
 
static void rs_set_ldisc(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
 
if (serial_paranoia_check(info, tty->device, "rs_set_ldisc"))
return;
 
info->is_cons = (tty->termios->c_line == N_TTY);
printk("ttyS%d console mode %s\n", info->line, info->is_cons ? "on" : "off");
}
 
static void rs_flush_chars(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
unsigned long flags;
 
if (serial_paranoia_check(info, tty->device, "rs_flush_chars"))
return;
if(!info->use_ints){
for(;;) {
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!info->xmit_buf)
return;
 
/* Enable transmitter */
save_flags(flags); cli();
tx_start(info->uart,info->use_ints);
}
}else{
if (info->xmit_cnt <= 0 || tty->stopped || tty->hw_stopped ||
!info->xmit_buf)
return;
 
/* Enable transmitter */
save_flags(flags); cli();
tx_start(info->uart, info->use_ints);
}
 
if(!info->use_ints)
wait_EOT(info->uart);
/* Send char */
xmit_char(info, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
restore_flags(flags);
}
 
extern void console_printn(const char * b, int count);
 
static int rs_write(struct tty_struct * tty, int from_user,
const unsigned char *buf, int count)
{
int c, total = 0;
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
unsigned long flags;
 
if (serial_paranoia_check(info, tty->device, "rs_write"))
return 0;
 
if (!tty || !info->xmit_buf)
return 0;
/*buf = "123456";
count = 6;
printk("Writing '%s' to serial port\n", buf);*/
 
/*printk("rs_write of %d bytes\n", count);*/
 
 
save_flags(flags);
while (1) {
cli();
c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
if (c <= 0)
break;
 
if (from_user) {
down(&tmp_buf_sem);
memcpy_fromfs(tmp_buf, buf, c);
#if 0 // HN already done
c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
SERIAL_XMIT_SIZE - info->xmit_head));
#endif
memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
up(&tmp_buf_sem);
} else
memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head = (info->xmit_head + c) & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt += c;
restore_flags(flags);
buf += c;
count -= c;
total += c;
}
 
if (info->xmit_cnt && !tty->stopped && !tty->hw_stopped ){
/* Enable transmitter */
cli();
/*printk("Enabling transmitter\n");*/
 
if(!info->use_ints){
while(info->xmit_cnt) {
wait_EOT(info->uart);
/* Send char */
xmit_char(info, info->xmit_buf[info->xmit_tail++]);
wait_EOT(info->uart);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
}
}else{
if (info->xmit_cnt){
/* Send char */
wait_EOT(info->uart);
xmit_string(info, &info->xmit_buf[info->xmit_tail], info->xmit_cnt);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt=0;
}
}
restore_flags(flags);
} else {
/*printk("Skipping transmit\n");*/
}
 
 
#if 0
printk("Enabling stuff anyhow\n");
tx_start(0);
 
if (SCC_EOT(0,0)) {
printk("TX FIFO empty.\n");
/* Send char */
trio_xmit_char(info->uart, info->xmit_buf[info->xmit_tail++]);
info->xmit_tail = info->xmit_tail & (SERIAL_XMIT_SIZE-1);
info->xmit_cnt--;
}
#endif
 
restore_flags(flags);
return total;
}
 
static int rs_write_room(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
int ret;
if (serial_paranoia_check(info, tty->device, "rs_write_room"))
return 0;
ret = SERIAL_XMIT_SIZE - info->xmit_cnt - 1;
if (ret < 0)
ret = 0;
return ret;
}
 
static int rs_chars_in_buffer(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
 
if (serial_paranoia_check(info, tty->device, "rs_chars_in_buffer"))
return 0;
return info->xmit_cnt;
}
 
static void rs_flush_buffer(struct tty_struct *tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "rs_flush_buffer"))
return;
cli();
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
sti();
wake_up_interruptible(&tty->write_wait);
if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
tty->ldisc.write_wakeup)
(tty->ldisc.write_wakeup)(tty);
}
 
/*
* ------------------------------------------------------------
* rs_throttle()
*
* This routine is called by the upper-layer tty layer to signal that
* incoming characters should be throttled.
* ------------------------------------------------------------
*/
static void rs_throttle(struct tty_struct * tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("throttle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
 
if (serial_paranoia_check(info, tty->device, "rs_throttle"))
return;
if (I_IXOFF(tty))
info->x_char = STOP_CHAR(tty);
 
/* Turn off RTS line (do this atomic) */
}
 
static void rs_unthrottle(struct tty_struct * tty)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
tty->ldisc.chars_in_buffer(tty));
#endif
 
if (serial_paranoia_check(info, tty->device, "rs_unthrottle"))
return;
if (I_IXOFF(tty)) {
if (info->x_char)
info->x_char = 0;
else
info->x_char = START_CHAR(tty);
}
 
/* Assert RTS line (do this atomic) */
}
 
/*
* ------------------------------------------------------------
* rs_ioctl() and friends
* ------------------------------------------------------------
*/
 
static int get_serial_info(struct trio_serial * info,
struct serial_struct * retinfo)
{
struct serial_struct tmp;
if (!retinfo)
return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = info->type;
tmp.line = info->line;
tmp.irq = info->irq;
tmp.port = info->port;
tmp.flags = info->flags;
tmp.baud_base = info->baud_base;
tmp.close_delay = info->close_delay;
tmp.closing_wait = info->closing_wait;
tmp.custom_divisor = info->custom_divisor;
memcpy_tofs(retinfo,&tmp,sizeof(*retinfo));
return 0;
}
 
static int set_serial_info(struct trio_serial * info,
struct serial_struct * new_info)
{
struct serial_struct new_serial;
struct trio_serial old_info;
int retval = 0;
 
if (!new_info)
return -EFAULT;
memcpy_fromfs(&new_serial,new_info,sizeof(new_serial));
old_info = *info;
 
if (!suser()) {
if ((new_serial.baud_base != info->baud_base) ||
(new_serial.type != info->type) ||
(new_serial.close_delay != info->close_delay) ||
((new_serial.flags & ~S_USR_MASK) !=
(info->flags & ~S_USR_MASK)))
return -EPERM;
info->flags = ((info->flags & ~S_USR_MASK) |
(new_serial.flags & S_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
goto check_and_exit;
}
 
if (info->count > 1)
return -EBUSY;
 
/*
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
 
info->baud_base = new_serial.baud_base;
info->flags = ((info->flags & ~S_FLAGS) |
(new_serial.flags & S_FLAGS));
info->type = new_serial.type;
info->close_delay = new_serial.close_delay;
info->closing_wait = new_serial.closing_wait;
 
check_and_exit:
retval = startup(info);
return retval;
}
 
/*
* get_lsr_info - get line status register info
*
* Purpose: Let user call ioctl() to get info when the UART physically
* is emptied. On bus types like RS485, the transmitter must
* release the bus after transmitting. This must be done when
* the transmit shift register is empty, not be done when the
* transmit holding register is empty. This functionality
* allows an RS485 driver to be written in user space.
*/
static int get_lsr_info(struct trio_serial * info, unsigned int *value)
{
unsigned char status;
 
cli();
status = info->uart->csr;
status &= US_TXEMPTY;
sti();
put_user(status,value);
return 0;
}
 
/*
* This routine sends a break character out the serial port.
*/
static void send_break( struct trio_serial * info, int duration)
{
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + duration;
cli();
info->uart->cr |= US_STTBRK;
if(!info->use_ints){
while(US_TXRDY != (info->uart->csr & US_TXRDY)){
; // this takes max 2ms at 9600
}
info->uart->cr |= US_STTBRK;
}
sti();
}
 
static int rs_ioctl(struct tty_struct *tty, struct file * file,
unsigned int cmd, unsigned long arg)
{
int error;
struct trio_serial * info = (struct trio_serial *)tty->driver_data;
int retval;
 
if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
return -ENODEV;
 
if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
(cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
(cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
}
switch (cmd) {
case TCSBRK: /* SVID version: non-zero arg --> no break */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
if (!arg)
send_break(info, HZ/4); /* 1/4 second */
return 0;
case TCSBRKP: /* support for POSIX tcsendbreak() */
retval = tty_check_change(tty);
if (retval)
return retval;
tty_wait_until_sent(tty, 0);
send_break(info, arg ? arg*(HZ/10) : HZ/4);
return 0;
case TIOCGSOFTCAR:
error = verify_area(VERIFY_WRITE, (void *) arg,sizeof(long));
if (error)
return error;
put_fs_long(C_CLOCAL(tty) ? 1 : 0,
(unsigned long *) arg);
return 0;
case TIOCSSOFTCAR:
arg = get_fs_long((unsigned long *) arg);
tty->termios->c_cflag =
((tty->termios->c_cflag & ~CLOCAL) |
(arg ? CLOCAL : 0));
return 0;
case TIOCGSERIAL:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct serial_struct));
if (error)
return error;
return get_serial_info(info,
(struct serial_struct *) arg);
case TIOCSSERIAL:
return set_serial_info(info,
(struct serial_struct *) arg);
case TIOCSERGETLSR: /* Get line status register */
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(unsigned int));
if (error)
return error;
else
return get_lsr_info(info, (unsigned int *) arg);
 
case TIOCSERGSTRUCT:
error = verify_area(VERIFY_WRITE, (void *) arg,
sizeof(struct trio_serial));
if (error)
return error;
memcpy_tofs((struct trio_serial *) arg,
info, sizeof(struct trio_serial));
return 0;
default:
return -ENOIOCTLCMD;
}
return 0;
}
 
static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct trio_serial *info = (struct trio_serial *)tty->driver_data;
 
if (tty->termios->c_cflag == old_termios->c_cflag)
return;
 
change_speed(info);
 
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
tty->hw_stopped = 0;
rs_start(tty);
}
}
 
/*
* ------------------------------------------------------------
* rs_close()
*
* This routine is called when the serial port gets closed. First, we
* wait for the last remaining data to be sent. Then, we unlink its
* S structure from the interrupt chain if necessary, and we free
* that IRQ if nothing is left in the chain.
* ------------------------------------------------------------
*/
static void rs_close(struct tty_struct *tty, struct file * filp)
{
struct trio_serial * info = (struct trio_serial *)tty->driver_data;
unsigned long flags;
 
if (!info || serial_paranoia_check(info, tty->device, "rs_close"))
return;
save_flags(flags); cli();
if (tty_hung_up_p(filp)) {
restore_flags(flags);
return;
}
#ifdef SERIAL_DEBUG_OPEN
printk("rs_close ttyS%d, count = %d\n", info->line, info->count);
#endif
if ((tty->count == 1) && (info->count != 1)) {
/*
* Uh, oh. tty->count is 1, which means that the tty
* structure will be freed. Info->count should always
* be one in these conditions. If it's greater than
* one, we've got real problems, since it means the
* serial port won't be shutdown.
*/
printk("rs_close: bad serial port count; tty->count is 1, "
"info->count is %d\n", info->count);
info->count = 1;
}
if (--info->count < 0) {
printk("rs_close: bad serial port count for ttyS%d: %d\n",
info->line, info->count);
info->count = 0;
}
if (info->count) {
restore_flags(flags);
return;
}
// closing port so disable interrupts
set_ints_mode(0, info);
info->flags |= S_CLOSING;
/*
* Save the termios structure, since this port may have
* separate termios for callout and dialin.
*/
if (info->flags & S_NORMAL_ACTIVE)
info->normal_termios = *tty->termios;
if (info->flags & S_CALLOUT_ACTIVE)
info->callout_termios = *tty->termios;
/*
* Now we wait for the transmit buffer to clear; and we notify
* the line discipline to only process XON/XOFF characters.
*/
tty->closing = 1;
if (info->closing_wait != S_CLOSING_WAIT_NONE)
tty_wait_until_sent(tty, info->closing_wait);
/*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
* line status register.
*/
 
shutdown(info);
if (tty->driver.flush_buffer)
tty->driver.flush_buffer(tty);
if (tty->ldisc.flush_buffer)
tty->ldisc.flush_buffer(tty);
tty->closing = 0;
info->event = 0;
info->tty = 0;
if (tty->ldisc.num != ldiscs[N_TTY].num) {
if (tty->ldisc.close)
(tty->ldisc.close)(tty);
tty->ldisc = ldiscs[N_TTY];
tty->termios->c_line = N_TTY;
if (tty->ldisc.open)
(tty->ldisc.open)(tty);
}
if (info->blocked_open) {
if (info->close_delay) {
current->state = TASK_INTERRUPTIBLE;
current->timeout = jiffies + info->close_delay;
schedule();
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(S_NORMAL_ACTIVE|S_CALLOUT_ACTIVE|
S_CLOSING);
wake_up_interruptible(&info->close_wait);
restore_flags(flags);
}
 
/*
* rs_hangup() --- called by tty_hangup() when a hangup is signaled.
*/
void rs_hangup(struct tty_struct *tty)
{
struct trio_serial * info = (struct trio_serial *)tty->driver_data;
if (serial_paranoia_check(info, tty->device, "rs_hangup"))
return;
rs_flush_buffer(tty);
shutdown(info);
info->event = 0;
info->count = 0;
info->flags &= ~(S_NORMAL_ACTIVE|S_CALLOUT_ACTIVE);
info->tty = 0;
wake_up_interruptible(&info->open_wait);
}
 
/*
* ------------------------------------------------------------
* rs_open() and friends
* ------------------------------------------------------------
*/
static int block_til_ready(struct tty_struct *tty, struct file * filp,
struct trio_serial *info)
{
struct wait_queue wait = { current, NULL };
int retval;
int do_clocal = 0;
 
/*
* If the device is in the middle of being closed, then block
* until it's done, and then try again.
*/
if (info->flags & S_CLOSING) {
interruptible_sleep_on(&info->close_wait);
#ifdef SERIAL_DO_RESTART
if (info->flags & S_HUP_NOTIFY)
return -EAGAIN;
else
return -ERESTARTSYS;
#else
return -EAGAIN;
#endif
}
 
/*
* If this is a callout device, then just make sure the normal
* device isn't being used.
*/
if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) {
if (info->flags & S_NORMAL_ACTIVE)
return -EBUSY;
if ((info->flags & S_CALLOUT_ACTIVE) &&
(info->flags & S_SESSION_LOCKOUT) &&
(info->session != current->session))
return -EBUSY;
if ((info->flags & S_CALLOUT_ACTIVE) &&
(info->flags & S_PGRP_LOCKOUT) &&
(info->pgrp != current->pgrp))
return -EBUSY;
info->flags |= S_CALLOUT_ACTIVE;
return 0;
}
/*
* If non-blocking mode is set, or the port is not enabled,
* then make the check up front and then exit.
*/
if ((filp->f_flags & O_NONBLOCK) ||
(tty->flags & (1 << TTY_IO_ERROR))) {
if (info->flags & S_CALLOUT_ACTIVE)
return -EBUSY;
info->flags |= S_NORMAL_ACTIVE;
return 0;
}
 
if (info->flags & S_CALLOUT_ACTIVE) {
if (info->normal_termios.c_cflag & CLOCAL)
do_clocal = 1;
} else {
if (tty->termios->c_cflag & CLOCAL)
do_clocal = 1;
}
/*
* Block waiting for the carrier detect and the line to become
* free (i.e., not in use by the callout). While we are in
* this loop, info->count is dropped by one, so that
* rs_close() knows when to free things. We restore it upon
* exit, either normal or abnormal.
*/
retval = 0;
add_wait_queue(&info->open_wait, &wait);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready before block: ttyS%d, count = %d\n",
info->line, info->count);
#endif
info->count--;
info->blocked_open++;
while (1) {
cli();
if (!(info->flags & S_CALLOUT_ACTIVE))
trio_rtsdtr(info, 1);
sti();
current->state = TASK_INTERRUPTIBLE;
if (tty_hung_up_p(filp) ||
!(info->flags & S_INITIALIZED)) {
#ifdef SERIAL_DO_RESTART
if (info->flags & S_HUP_NOTIFY)
retval = -EAGAIN;
else
retval = -ERESTARTSYS;
#else
retval = -EAGAIN;
#endif
break;
}
if (!(info->flags & S_CALLOUT_ACTIVE) &&
!(info->flags & S_CLOSING) && do_clocal)
break;
if (current->signal & ~current->blocked) {
retval = -ERESTARTSYS;
break;
}
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready blocking: ttyS%d, count = %d\n",
info->line, info->count);
#endif
schedule();
}
current->state = TASK_RUNNING;
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->count++;
info->blocked_open--;
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttyS%d, count = %d\n",
info->line, info->count);
#endif
if (retval)
return retval;
info->flags |= S_NORMAL_ACTIVE;
if(!info->use_ints){
serialpoll.data = (void *)info;
queue_task(&serialpoll, &tq_timer);
}
return 0;
}
 
/*
* This routine is called whenever a serial port is opened. It
* enables interrupts for a serial port, linking in its S structure into
* the IRQ chain. It also performs the serial-specific
* initialization for the tty structure.
*/
int rs_open(struct tty_struct *tty, struct file * filp)
{
struct trio_serial *info;
int retval, line;
 
line = MINOR(tty->device) - tty->driver.minor_start;
if (line != 0) /* we have exactly one */
return -ENODEV;
 
info = &trio_info[0];
#if 0
/* Is the kgdb running over this line? */
if (info->kgdb_channel)
return -ENODEV;
#endif
if (serial_paranoia_check(info, tty->device, "rs_open"))
return -ENODEV;
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line,
info->count);
#endif
info->count++;
tty->driver_data = info;
info->tty = tty;
 
/*
* Start up serial port
*/
retval = startup(info);
if (retval)
return retval;
retval = block_til_ready(tty, filp, info);
if (retval) {
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open returning after block_til_ready with %d\n",
retval);
#endif
return retval;
}
 
if ((info->count == 1) && (info->flags & S_SPLIT_TERMIOS)) {
if (tty->driver.subtype == SERIAL_TYPE_NORMAL)
*tty->termios = info->normal_termios;
else
*tty->termios = info->callout_termios;
change_speed(info);
}
 
info->session = current->session;
info->pgrp = current->pgrp;
 
#ifdef SERIAL_DEBUG_OPEN
printk("rs_open ttyS%d successful...\n", info->line);
#endif
return 0;
}
 
extern void register_console(void (*proc)(const char *));
#if 0
 
static inline void
rs_cons_check(struct trio_serial *ss, int channel)
{
int i, o, io;
static consout_registered = 0;
static msg_printed = 0;
 
i = o = io = 0;
 
/* Is this one of the serial console lines? */
if((trio_cons_chanout != channel) &&
(trio_cons_chanin != channel))
return;
trio_conschan = ss->trio_channel;
trio_consinfo = ss;
 
/* Register the console output putchar, if necessary */
if((trio_cons_chanout == channel)) {
o = 1;
/* double whee.. */
if(!consout_registered) {
register_console(trio_console_print);
consout_registered = 1;
}
}
 
/* If this is console input, we handle the break received
* status interrupt on this line to mean prom_halt().
*/
if(trio_cons_chanin == channel) {
ss->break_abort = 1;
i = 1;
}
if(o && i)
io = 1;
if(ss->baud != 9600)
panic("Console baud rate weirdness");
 
/* Set flag variable for this port so that it cannot be
* opened for other uses by accident.
*/
ss->is_cons = 1;
 
if(io) {
if(!msg_printed) {
printk("zs%d: console I/O\n", ((channel>>1)&1));
msg_printed = 1;
}
} else {
printk("zs%d: console %s\n", ((channel>>1)&1),
(i==1 ? "input" : (o==1 ? "output" : "WEIRD")));
}
}
#endif
 
static struct irqaction irq_usarta = { rs_interrupta, 0, 0, "usarta", NULL, NULL};
static struct irqaction irq_usartb = { rs_interruptb, 0, 0, "usartb", NULL, NULL};
 
extern int setup_arm_irq(int, struct irqaction *);
 
void interrupts_init(void)
{
setup_arm_irq(IRQ_USARTA, &irq_usarta);
setup_arm_irq(IRQ_USARTB, &irq_usartb);
}
 
/* rs_init inits the driver */
int rs_trio_init(void)
{
int flags,i;
struct trio_serial *info;
/* Setup base handler, and timer table. */
init_bh(SERIAL_BH, do_serial_bh);
timer_table[RS_TIMER].fn = rs_timer;
timer_table[RS_TIMER].expires = 0;
 
/* Initialize the tty_driver structure */
memset(&serial_driver, 0, sizeof(struct tty_driver));
serial_driver.magic = TTY_DRIVER_MAGIC;
serial_driver.name = "ttyS";
serial_driver.major = TTY_MAJOR;
serial_driver.minor_start = 64;
serial_driver.num = 1;
serial_driver.type = TTY_DRIVER_TYPE_SERIAL;
serial_driver.subtype = SERIAL_TYPE_NORMAL;
serial_driver.init_termios = tty_std_termios;
 
serial_driver.init_termios.c_cflag =
B9600 | CS8 | CREAD | HUPCL | CLOCAL;
serial_driver.flags = TTY_DRIVER_REAL_RAW;
serial_driver.refcount = &serial_refcount;
serial_driver.table = serial_table;
serial_driver.termios = serial_termios;
serial_driver.termios_locked = serial_termios_locked;
 
serial_driver.open = rs_open;
serial_driver.close = rs_close;
serial_driver.write = rs_write;
serial_driver.flush_chars = rs_flush_chars;
serial_driver.write_room = rs_write_room;
serial_driver.chars_in_buffer = rs_chars_in_buffer;
serial_driver.flush_buffer = rs_flush_buffer;
serial_driver.ioctl = rs_ioctl;
serial_driver.throttle = rs_throttle;
serial_driver.unthrottle = rs_unthrottle;
serial_driver.set_termios = rs_set_termios;
serial_driver.stop = rs_stop;
serial_driver.start = rs_start;
serial_driver.hangup = rs_hangup;
serial_driver.set_ldisc = rs_set_ldisc;
 
/*
* The callout device is just like normal device except for
* major number and the subtype code.
*/
callout_driver = serial_driver;
callout_driver.name = "cua";
callout_driver.major = TTYAUX_MAJOR;
callout_driver.subtype = SERIAL_TYPE_CALLOUT;
 
if (tty_register_driver(&serial_driver))
panic("Couldn't register serial driver\n");
if (tty_register_driver(&callout_driver))
panic("Couldn't register callout driver\n");
save_flags(flags); cli();
i=0;
while(i<US_NB){
info = &trio_info[i];
info->magic = SERIAL_MAGIC;
info->uart = uarts[i];
info->tty = 0;
info->irqmask = (i)?AIC_UB:AIC_UA;
info->irq = (i)?IRQ_USARTB:IRQ_USARTA;
info->port = i+1;
set_ints_mode(0,info);
info->custom_divisor = 16;
info->close_delay = 50;
info->closing_wait = 3000;
info->x_char = 0;
info->event = 0;
info->count = 0;
info->blocked_open = 0;
info->tqueue.routine = do_softint;
info->tqueue.data = info;
info->tqueue_hangup.routine = do_serial_hangup;
info->tqueue_hangup.data = info;
info->callout_termios =callout_driver.init_termios;
info->normal_termios = serial_driver.init_termios;
info->open_wait = 0;
info->close_wait = 0;
info->line = 0;
info->is_cons = (i)?0:1; /* Means shortcuts work */
i++;
}
interrupts_init();
restore_flags(flags);
// hack to do polling
serialpoll.routine = serpoll;
serialpoll.data = 0;
return 0;
}
 
/*
* register_serial and unregister_serial allows for serial ports to be
* configured at run-time, to support PCMCIA modems.
*/
/* SPARC: Unused at this time, just here to make things link. */
int register_serial(struct serial_struct *req)
{
return -1;
}
 
void unregister_serial(int line)
{
return;
}
 
#if 0
/* Hooks for running a serial console. con_init() calls this if the
* console is being run over one of the ttya/ttyb serial ports.
* 'chip' should be zero, as chip 1 drives the mouse/keyboard.
* 'channel' is decoded as 0=TTYA 1=TTYB, note that the channels
* are addressed backwards, channel B is first, then channel A.
*/
void
rs_cons_hook(int chip, int out, int channel)
{
if(chip)
panic("rs_cons_hook called with chip not zero");
if(!trio_chips[chip]) {
trio_chips[chip] = get_zs(chip);
/* Two channels per chip */
trio_channels[(chip*2)] = &trio_chips[chip]->channelA;
trio_channels[(chip*2)+1] = &trio_chips[chip]->channelB;
}
trio_info[channel].trio_channel = trio_channels[channel];
trio_info[channel].change_needed = 0;
trio_info[channel].clk_divisor = 16;
trio_info[channel].trio_baud = get_zsbaud(&trio_info[channel]);
rs_cons_check(&trio_info[channel], channel);
if(out)
trio_cons_chanout = ((chip * 2) + channel);
else
trio_cons_chanin = ((chip * 2) + channel);
 
}
 
/* This is called at boot time to prime the kgdb serial debugging
* serial line. The 'tty_num' argument is 0 for /dev/ttya and 1
* for /dev/ttyb which is determined in setup_arch() from the
* boot command line flags.
*/
void
rs_kgdb_hook(int tty_num)
{
int chip = 0;
 
if(!trio_chips[chip]) {
trio_chips[chip] = get_zs(chip);
/* Two channels per chip */
trio_channels[(chip*2)] = &trio_chips[chip]->channelA;
trio_channels[(chip*2)+1] = &trio_chips[chip]->channelB;
}
trio_info[tty_num].trio_channel = trio_channels[tty_num];
trio_kgdbchan = trio_info[tty_num].trio_channel;
trio_info[tty_num].change_needed = 0;
trio_info[tty_num].clk_divisor = 16;
trio_info[tty_num].trio_baud = get_zsbaud(&trio_info[tty_num]);
trio_info[tty_num].kgdb_channel = 1; /* This runs kgdb */
trio_info[tty_num ^ 1].kgdb_channel = 0; /* This does not */
/* Turn on transmitter/receiver at 8-bits/char */
kgdb_chaninit(&trio_info[tty_num], 0, 9600);
ZS_CLEARERR(trio_kgdbchan);
udelay(5);
ZS_CLEARFIFO(trio_kgdbchan);
}
#endif
/mem.c
0,0 → 1,447
/*
* linux/arch/arm/drivers/char/mem.c
*
* Copyright (C) 1991, 1992 Linus Torvalds
*
* Modifications for ARM Copyright (C) 1995, 1996 Russell King
*/
 
#include <linux/config.h>
#include <linux/types.h>
#include <linux/errno.h>
#include <linux/sched.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/tty.h>
#include <linux/miscdevice.h>
#include <linux/tpqic02.h>
#include <linux/malloc.h>
#include <linux/mman.h>
#include <linux/mm.h>
#include <linux/random.h>
 
#include <asm/segment.h>
#include <asm/io.h>
#include <asm/pgtable.h>
 
extern int con_fb_read(char *buf, unsigned long pos, int count);
extern int con_fb_write(const char *buf, unsigned long pos, int count);
extern int con_fb_mmap(unsigned long vma_start, unsigned long vma_offset,
unsigned long vma_end, pgprot_t prot);
 
#ifdef CONFIG_SOUND
void soundcard_init(void);
#endif
 
static int read_ram(struct inode * inode, struct file * file, char * buf, int count)
{
return -EIO;
}
 
static int write_ram(struct inode * inode, struct file * file, const char * buf, int count)
{
return -EIO;
}
 
static int read_mem(struct inode * inode, struct file * file, char * buf, int count)
{
unsigned long p = file->f_pos;
 
p += PAGE_OFFSET;
if (count < 0)
return -EINVAL;
if (MAP_NR(p) >= max_mapnr)
return 0;
if (count > high_memory - p)
count = high_memory - p;
memcpy_tofs(buf,(void *) p,count);
file->f_pos += count;
return count;
}
 
static int write_mem(struct inode * inode, struct file * file, const char * buf, int count)
{
unsigned long p = file->f_pos;
 
p += PAGE_OFFSET;
if (count < 0)
return -EINVAL;
if (MAP_NR(p) >= max_mapnr)
return 0;
if (count > high_memory - p)
count = high_memory - p;
memcpy_fromfs((void *) p,buf,count);
file->f_pos += count;
return count;
}
 
static int mmap_mem(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
#ifndef NO_MM
if (vma->vm_offset & ~PAGE_MASK)
return -ENXIO;
 
/*
* This disables memory caching/buffering
*/
#if defined(CONFIG_CPU_ARM6) || defined(CONFIG_CPU_SA110)
if (MAP_NR(vma->vm_offset) >= max_mapnr)
pgprot_val(vma->vm_page_prot) &= ~(PTE_CACHEABLE | PTE_BUFFERABLE);
#endif
 
if (remap_page_range(vma->vm_start, vma->vm_offset, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
vma->vm_inode = inode;
inode->i_count++;
#else
vma->vm_start = file->f_pos+PAGE_OFFSET+vma->vm_offset;
#endif /* !NO_MM */
return 0;
}
 
static int read_fb(struct inode * inode, struct file *file, char *buf, int count)
{
if (count < 0)
return -EINVAL;
count = con_fb_read(buf, file->f_pos, count);
if (count > 0)
file->f_pos += count;
return count;
}
 
static int write_fb(struct inode * inode, struct file * file, const char * buf, int count)
{
if (count < 0)
return -EINVAL;
count = con_fb_write(buf, file->f_pos, count);
if (count > 0)
file->f_pos += count;
return count;
}
 
static int mmap_fb(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
int ret;
#ifndef NO_MM
 
if (vma->vm_offset & ~PAGE_MASK)
return -ENXIO;
 
ret = con_fb_mmap(vma->vm_start, vma->vm_offset, vma->vm_end, vma->vm_page_prot);
if (ret)
return ret;
vma->vm_inode = inode;
inode->i_count ++;
#else
ret = con_fb_mmap(vma->vm_start, vma->vm_offset, vma->vm_end, __pgprot(0));
if (ret)
return ret;
#endif
return 0;
}
 
static int read_kmem(struct inode *inode, struct file *file, char *buf, int count)
{
int read1, read2;
 
read1 = read_mem(inode, file, buf, count);
if (read1 < 0)
return read1;
read2 = vread(buf + read1, (char *) ((unsigned long) file->f_pos), count - read1);
if (read2 < 0)
return read2;
file->f_pos += read2;
return read1 + read2;
}
 
static int read_port(struct inode * inode, struct file * file,char * buf, int count)
{
unsigned int i = file->f_pos;
char * tmp = buf;
 
while (count-- > 0 && (i < 65536 || (i > 0x80000000 && i < 0x84000000))) {
put_user(inb(i),tmp);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
 
static int write_port(struct inode * inode, struct file * file, const char * buf, int count)
{
unsigned int i = file->f_pos;
const char * tmp = buf;
 
while (count-- > 0 && (i < 65536 || (i > 0x80000000 && i < 0x84000000))) {
outb(get_user(tmp),i);
i++;
tmp++;
}
file->f_pos = i;
return tmp-buf;
}
 
static int read_null(struct inode * node, struct file * file, char * buf, int count)
{
return 0;
}
 
static int write_null(struct inode * inode, struct file * file, const char * buf, int count)
{
return count;
}
 
static int read_zero(struct inode * node, struct file * file, char * buf, int count)
{
int left;
 
for (left = count; left > 0; left--) {
put_user(0,buf);
buf++;
if (need_resched)
schedule();
}
return count;
}
 
static int mmap_zero(struct inode * inode, struct file * file, struct vm_area_struct * vma)
{
#ifndef NO_MM
if (vma->vm_flags & VM_SHARED)
return -EINVAL;
if (zeromap_page_range(vma->vm_start, vma->vm_end - vma->vm_start, vma->vm_page_prot))
return -EAGAIN;
return 0;
#else /* !NO_MM */
/* FIXME: trivial calloc() implementation is feasible */
return -ENOSYS;
#endif /* !NO_MM */
 
}
 
static int read_full(struct inode * node, struct file * file, char * buf,int count)
{
file->f_pos += count;
return count;
}
 
static int write_full(struct inode * inode, struct file * file, const char * buf, int count)
{
return -ENOSPC;
}
 
/*
* Special lseek() function for /dev/null and /dev/zero. Most notably, you can fopen()
* both devices with "a" now. This was previously impossible. SRB.
*/
 
static int null_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
return file->f_pos=0;
}
/*
* The memory devices use the full 32/64 bits of the offset, and so we cannot
* check against negative addresses: they are ok. The return value is weird,
* though, in that case (0).
*
* also note that seeking relative to the "end of file" isn't supported:
* it has no meaning, so it returns -EINVAL.
*/
static int memory_lseek(struct inode * inode, struct file * file, off_t offset, int orig)
{
switch (orig) {
case 0:
file->f_pos = offset;
return file->f_pos;
case 1:
file->f_pos += offset;
return file->f_pos;
default:
return -EINVAL;
}
if (file->f_pos < 0)
return 0;
return file->f_pos;
}
 
#define write_kmem write_mem
#define mmap_kmem mmap_mem
#define zero_lseek null_lseek
#define write_zero write_null
 
static struct file_operations ram_fops = {
memory_lseek,
read_ram,
write_ram,
NULL, /* ram_readdir */
NULL, /* ram_select */
NULL, /* ram_ioctl */
NULL, /* ram_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations mem_fops = {
memory_lseek,
read_mem,
write_mem,
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
mmap_mem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations fb_fops = {
memory_lseek,
read_fb,
write_fb,
NULL, /* mem_readdir */
NULL, /* mem_select */
NULL, /* mem_ioctl */
mmap_fb,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations kmem_fops = {
memory_lseek,
read_kmem,
write_kmem,
NULL, /* kmem_readdir */
NULL, /* kmem_select */
NULL, /* kmem_ioctl */
mmap_kmem,
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations null_fops = {
null_lseek,
read_null,
write_null,
NULL, /* null_readdir */
NULL, /* null_select */
NULL, /* null_ioctl */
NULL, /* null_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations port_fops = {
memory_lseek,
read_port,
write_port,
NULL, /* port_readdir */
NULL, /* port_select */
NULL, /* port_ioctl */
NULL, /* port_mmap */
NULL, /* no special open code */
NULL, /* no special release code */
NULL /* fsync */
};
 
static struct file_operations zero_fops = {
zero_lseek,
read_zero,
write_zero,
NULL, /* zero_readdir */
NULL, /* zero_select */
NULL, /* zero_ioctl */
mmap_zero,
NULL, /* no special open code */
NULL /* no special release code */
};
 
static struct file_operations full_fops = {
memory_lseek,
read_full,
write_full,
NULL, /* full_readdir */
NULL, /* full_select */
NULL, /* full_ioctl */
NULL, /* full_mmap */
NULL, /* no special open code */
NULL /* no special release code */
};
 
static int memory_open(struct inode * inode, struct file * filp)
{
switch (MINOR(inode->i_rdev)) {
case 0:
filp->f_op = &ram_fops;
break;
case 1:
filp->f_op = &mem_fops;
break;
case 2:
filp->f_op = &kmem_fops;
break;
case 3:
filp->f_op = &null_fops;
break;
case 4:
filp->f_op = &port_fops;
break;
case 5:
filp->f_op = &zero_fops;
break;
case 7:
filp->f_op = &full_fops;
break;
case 8:
filp->f_op = &random_fops;
break;
case 9:
filp->f_op = &urandom_fops;
break;
case 128:
filp->f_op = &fb_fops;
break;
default:
return -ENXIO;
}
if (filp->f_op && filp->f_op->open)
return filp->f_op->open(inode,filp);
return 0;
}
 
static struct file_operations memory_fops = {
NULL, /* lseek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
memory_open, /* just a selector for the real open */
NULL, /* release */
NULL /* fsync */
};
 
int chr_dev_init(void)
{
if (register_chrdev(MEM_MAJOR,"mem",&memory_fops))
printk("unable to get major %d for memory devs\n", MEM_MAJOR);
rand_initialize();
tty_init();
 
#ifdef CONFIG_PRINTER
lp_init();
#endif
#if defined(CONFIG_MOUSE) || defined(CONFIG_SOFT_WATCHDOG)
misc_init();
#endif
#ifdef CONFIG_SOUND
soundcard_init();
#endif
return 0;
}
/keyb_ps2.c
0,0 → 1,318
/*
* linux/arch/arm/drivers/block/keyb_ps2.c
*
* Keyboard driver for RPC ARM Linux.
*
* Note!!! This driver talks directly to the keyboard.
*/
 
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/random.h>
#include <linux/ctype.h>
 
#include <asm/bitops.h>
#include <asm/irq.h>
#include <asm/hardware.h>
#include <asm/io.h>
#include <asm/system.h>
 
extern void kbd_keyboardkey(unsigned int keycode, unsigned int up_flag);
extern void kbd_reset(void);
extern void kbd_setregs(struct pt_regs *regs);
 
#define IRQ_KEYBOARDRX 15
 
#define VERSION 100
 
#define KBD_REPORT_ERR
#define KBD_REPORT_UNKN
 
#define KBD_ESCAPEE0 0xe0 /* in */
#define KBD_ESCAPEE1 0xe1 /* in */
 
#define ESCE0(x) (0xe000|(x))
#define ESCE1(x) (0xe100|(x))
 
#define KBD_BAT 0xaa /* in */
#define KBD_SETLEDS 0xed /* out */
#define KBD_ECHO 0xee /* in/out */
#define KBD_BREAK 0xf0 /* in */
#define KBD_TYPRATEDLY 0xf3 /* out */
#define KBD_SCANENABLE 0xf4 /* out */
#define KBD_DEFDISABLE 0xf5 /* out */
#define KBD_DEFAULT 0xf6 /* out */
#define KBD_ACK 0xfa /* in */
#define KBD_DIAGFAIL 0xfd /* in */
#define KBD_RESEND 0xfe /* in/out */
#define KBD_RESET 0xff /* out */
 
#define CODE_BREAK 1
#define CODE_ESCAPEE0 2
#define CODE_ESCAPEE1 4
#define CODE_ESCAPE12 8
 
#define K_NONE 0x7f
#define K_ESC 0x00
#define K_F1 0x01
#define K_F2 0x02
#define K_F3 0x03
#define K_F4 0x04
#define K_F5 0x05
#define K_F6 0x06
#define K_F7 0x07
#define K_F8 0x08
#define K_F9 0x09
#define K_F10 0x0a
#define K_F11 0x0b
#define K_F12 0x0c
#define K_PRNT 0x0d
#define K_SCRL 0x0e
#define K_BRK 0x0f
#define K_AGR 0x10
#define K_1 0x11
#define K_2 0x12
#define K_3 0x13
#define K_4 0x14
#define K_5 0x15
#define K_6 0x16
#define K_7 0x17
#define K_8 0x18
#define K_9 0x19
#define K_0 0x1a
#define K_MINS 0x1b
#define K_EQLS 0x1c
#define K_BKSP 0x1e
#define K_INS 0x1f
#define K_HOME 0x20
#define K_PGUP 0x21
#define K_NUML 0x22
#define KP_SLH 0x23
#define KP_STR 0x24
#define KP_MNS 0x3a
#define K_TAB 0x26
#define K_Q 0x27
#define K_W 0x28
#define K_E 0x29
#define K_R 0x2a
#define K_T 0x2b
#define K_Y 0x2c
#define K_U 0x2d
#define K_I 0x2e
#define K_O 0x2f
#define K_P 0x30
#define K_LSBK 0x31
#define K_RSBK 0x32
#define K_ENTR 0x47
#define K_DEL 0x34
#define K_END 0x35
#define K_PGDN 0x36
#define KP_7 0x37
#define KP_8 0x38
#define KP_9 0x39
#define KP_PLS 0x4b
#define K_CAPS 0x5d
#define K_A 0x3c
#define K_S 0x3d
#define K_D 0x3e
#define K_F 0x3f
#define K_G 0x40
#define K_H 0x41
#define K_J 0x42
#define K_K 0x43
#define K_L 0x44
#define K_SEMI 0x45
#define K_SQOT 0x46
#define K_HASH 0x1d
#define KP_4 0x48
#define KP_5 0x49
#define KP_6 0x4a
#define K_LSFT 0x4c
#define K_BSLH 0x33
#define K_Z 0x4e
#define K_X 0x4f
#define K_C 0x50
#define K_V 0x51
#define K_B 0x52
#define K_N 0x53
#define K_M 0x54
#define K_COMA 0x55
#define K_DOT 0x56
#define K_FSLH 0x57
#define K_RSFT 0x58
#define K_UP 0x59
#define KP_1 0x5a
#define KP_2 0x5b
#define KP_3 0x5c
#define KP_ENT 0x67
#define K_LCTL 0x3b
#define K_LALT 0x5e
#define K_SPCE 0x5f
#define K_RALT 0x60
#define K_RCTL 0x61
#define K_LEFT 0x62
#define K_DOWN 0x63
#define K_RGHT 0x64
#define KP_0 0x65
#define KP_DOT 0x66
 
static unsigned char keycode_translate[256] =
{
/* 00 */ K_NONE, K_F9 , K_NONE, K_F5 , K_F3 , K_F1 , K_F2 , K_F12 ,
/* 08 */ K_NONE, K_F10 , K_F8 , K_F6 , K_F4 , K_TAB , K_AGR , K_NONE,
/* 10 */ K_NONE, K_LALT, K_LSFT, K_NONE, K_LCTL, K_Q , K_1 , K_NONE,
/* 18 */ K_NONE, K_NONE, K_Z , K_S , K_A , K_W , K_2 , K_NONE,
/* 20 */ K_NONE, K_C , K_X , K_D , K_E , K_4 , K_3 , K_NONE,
/* 28 */ K_NONE, K_SPCE, K_V , K_F , K_T , K_R , K_5 , K_NONE,
/* 30 */ K_NONE, K_N , K_B , K_H , K_G , K_Y , K_6 , K_NONE,
/* 38 */ K_NONE, K_NONE, K_M , K_J , K_U , K_7 , K_8 , K_NONE,
/* 40 */ K_NONE, K_COMA, K_K , K_I , K_O , K_0 , K_9 , K_NONE,
/* 48 */ K_NONE, K_DOT , K_FSLH, K_L , K_SEMI, K_P , K_MINS, K_NONE,
/* 50 */ K_NONE, K_NONE, K_SQOT, K_NONE, K_LSBK, K_EQLS, K_NONE, K_NONE,
/* 58 */ K_CAPS, K_RSFT, K_ENTR, K_RSBK, K_NONE, K_HASH, K_NONE, K_NONE,
/* 60 */ K_NONE, K_BSLH, K_NONE, K_NONE, K_NONE, K_NONE, K_BKSP, K_NONE,
/* 68 */ K_NONE, KP_1 , K_NONE, KP_4 , KP_7 , K_NONE, K_NONE, K_NONE,
/* 70 */ KP_0 , KP_DOT, KP_2 , KP_5 , KP_6 , KP_8 , K_ESC , K_NUML,
/* 78 */ K_F11 , KP_PLS, KP_3 , KP_MNS, KP_STR, KP_9 , K_SCRL, K_NONE,
K_NONE, K_NONE, K_NONE, K_F7 , K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE,
K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE, K_NONE
};
 
static void kbd_drv_key(unsigned int keycode, unsigned int up_flag)
{
kbd_keyboardkey(keycode, up_flag);
}
 
static inline void kbd_drv_sendbyte(unsigned char val)
{
while(!(inb(IOMD_KCTRL) & (1 << 7)));
outb(val, IOMD_KARTTX);
}
 
static unsigned char status;
static unsigned char ncodes;
static unsigned char bi;
static unsigned char buffer[4];
 
static inline void kbd_drv_reset(void)
{
status = 0;
kbd_reset();
}
 
static void handle_rawcode(int keyval)
{
int keysym;
 
if (keyval > 0x83) {
switch (keyval) {
case KBD_ESCAPEE0:
ncodes = 2;
bi = 0;
break;
case KBD_ESCAPEE1:
ncodes = 3;
bi = 0;
break;
case KBD_BREAK:
status |= CODE_BREAK;
default:
return;
}
}
 
if (ncodes) {
buffer[bi++] = keyval;
ncodes -= 1;
if (ncodes)
return;
keysym = K_NONE;
switch (buffer[0] << 8 | buffer[1]) {
case ESCE0(0x11): keysym = K_RALT; break;
case ESCE0(0x14): keysym = K_RCTL; break;
case ESCE0(0x4a): keysym = KP_SLH; break;
case ESCE0(0x5a): keysym = KP_ENT; break;
case ESCE0(0x69): keysym = K_END; break;
case ESCE0(0x6b): keysym = K_LEFT; break;
case ESCE0(0x6c): keysym = K_HOME; break;
case ESCE0(0x70): keysym = K_INS; break;
case ESCE0(0x71): keysym = K_DEL; break;
case ESCE0(0x72): keysym = K_DOWN; break;
case ESCE0(0x74): keysym = K_RGHT; break;
case ESCE0(0x75): keysym = K_UP; break;
case ESCE0(0x7a): keysym = K_PGDN; break;
case ESCE0(0x7c): keysym = K_PRNT; break;
case ESCE0(0x7d): keysym = K_PGUP; break;
case ESCE1(0x14):
if (buffer[2] == 0x77)
keysym = K_BRK;
break;
case ESCE0(0x12): /* ignore escaped shift key */
status = 0;
return;
}
} else {
bi = 0;
keysym = keycode_translate[keyval];
}
 
if (keysym != K_NONE)
kbd_drv_key(keysym, status & CODE_BREAK);
status = 0;
}
 
void kbd_drv_setleds(unsigned int leds)
{
kbd_drv_sendbyte(KBD_SETLEDS);
kbd_drv_sendbyte(leds);
kbd_drv_sendbyte(KBD_SCANENABLE);
}
 
static void kbd_drv_rx(int irq, void *dev_id, struct pt_regs *regs)
{
kbd_setregs(regs);
 
while (inb(IOMD_KCTRL) & (1 << 5))
handle_rawcode(inb(IOMD_KARTRX));
mark_bh(KEYBOARD_BH);
}
 
static void kbd_drv_tx(int irq, void *dev_id, struct pt_regs *regs)
{
}
 
int kbd_drv_init (void)
{
unsigned long flags;
 
save_flags_cli (flags);
if (request_irq (IRQ_KEYBOARDRX, kbd_drv_rx, 0, "keyboard", NULL) != 0)
panic("Could not allocate keyboard receive IRQ!");
if (request_irq (IRQ_KEYBOARDTX, kbd_drv_tx, 0, "keyboard", NULL) != 0)
panic("Could not allocate keyboard transmit IRQ!");
disable_irq (IRQ_KEYBOARDTX);
(void)IOMD_KARTRX;
restore_flags (flags);
 
printk (KERN_INFO "Keyboard driver v%d.%02d\n", VERSION/100, VERSION%100);
return 0;
}
/keyb_arc.c
0,0 → 1,418
/*
* linux/arch/arm/drivers/block/keyboard.c
*
* Keyboard driver for ARM Linux.
*/
 
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/ptrace.h>
#include <linux/signal.h>
#include <linux/timer.h>
#include <linux/random.h>
#include <linux/ctype.h>
 
#include <asm/bitops.h>
#include <asm/irq.h>
#include <asm/hardware.h>
 
#include "kbd_kern.h"
#include "diacr.h"
#include "vt_kern.h"
 
#define IRQ_KEYBOARDRX 15
#define IRQ_KEYBOARDTX 14
 
extern void kbd_keyboardkey(unsigned int keycode, unsigned int up_flag);
extern void kbd_reset(void);
extern void kbd_setregs(struct pt_regs *regs);
 
#define VERSION 107
 
#define KBD_REPORT_ERR
#define KBD_REPORT_UNKN
 
#include <asm/io.h>
#include <asm/system.h>
 
static char kbd_txval;
static int kbd_id = -1;
static struct wait_queue *kbd_waitq;
 
/*
* Protocol codes to send the keyboard.
*/
#define HRST 0xff /* reset keyboard */
#define RAK1 0xfe /* reset response */
#define RAK2 0xfd /* reset response */
#define BACK 0x3f /* Ack for first keyboard pair */
#define SMAK 0x33 /* Last data byte ack (key scanning + mouse movement scanning) */
#define MACK 0x32 /* Last data byte ack (mouse movement scanning) */
#define SACK 0x31 /* Last data byte ack (key scanning) */
#define NACK 0x30 /* Last data byte ack (no scanning, mouse data) */
#define RQMP 0x22 /* Request mouse data */
#define PRST 0x21 /* nothing */
#define RQID 0x20 /* Request ID */
 
#define UP_FLAG 1
 
/*
* This array converts the scancode that we get from the keyboard to the
* real rows/columns on the A5000 keyboard. This might be keyboard specific...
*
* It is these values that we use to maintain the key down array. That way, we
* should pick up on the ghost key presses (which is what happens when you press
* three keys, and the keyboard thinks you have pressed four!)
*
* Row 8 (0x80+c) is actually a column with one key per row. It is isolated from
* the other keys, and can't cause these problems (its used for shift, ctrl, alt etc).
*
* Illegal scancodes are denoted by an 0xff (in other words, we don't know about
* them, and can't process them for ghosts). This does however, cause problems with
* autorepeat processing...
*/
static unsigned char scancode_2_colrow[256] = {
0x01, 0x42, 0x32, 0x33, 0x43, 0x56, 0x5a, 0x6c, 0x7c, 0x5c, 0x5b, 0x6b, 0x7b, 0x84, 0x70, 0x60,
0x11, 0x51, 0x62, 0x63, 0x44, 0x54, 0x55, 0x45, 0x46, 0x4a, 0x3c, 0x4b, 0x59, 0x49, 0x69, 0x79,
0x83, 0x40, 0x30, 0x3b, 0x39, 0x38, 0x31, 0x61, 0x72, 0x73, 0x64, 0x74, 0x75, 0x65, 0x66, 0x6a,
0x1c, 0x2c, 0x7a, 0x36, 0x48, 0x68, 0x78, 0x20, 0x2b, 0x29, 0x28, 0x81, 0x71, 0x22, 0x23, 0x34,
0x24, 0x25, 0x35, 0x26, 0x3a, 0x0c, 0x2a, 0x76, 0x10, 0x1b, 0x19, 0x18, 0x82, 0xff, 0x21, 0x12,
0x13, 0x14, 0x04, 0x05, 0x15, 0x16, 0x1a, 0x0a, 0x85, 0x77, 0x00, 0x0b, 0x09, 0x02, 0x80, 0x03,
0x87, 0x86, 0x06, 0x17, 0x27, 0x07, 0x37, 0x08, 0xff,
};
 
#define BITS_PER_SHORT (8*sizeof(unsigned short))
static unsigned short ghost_down[128/BITS_PER_SHORT];
 
static void kbd_drv_key(unsigned int keycode, unsigned int up_flag)
{
unsigned int real_keycode;
 
if (keycode > 0x72) {
#ifdef KBD_REPORT_UNKN
printk ("kbd: unknown scancode 0x%04x\n", keycode);
#endif
return;
}
if (keycode >= 0x70) {
#ifdef CONFIG_KBDMOUSE
unsigned char buttons;
switch (keycode) {
case 0x70: /* Left mouse button */
buttons = add_mouse_buttonchange(4, up_flag ? 4 : 0);
break;
 
case 0x71: /* Middle mouse button */
buttons = add_mouse_buttonchange(2, up_flag ? 2 : 0);
break;
 
case 0x72:/* Right mouse button */
buttons = add_mouse_buttonchange(1, up_flag ? 1 : 0);
break;
default:
buttons = 0;
}
add_mouse_randomness (buttons << 16);
#endif
return;
}
 
/*
* We have to work out if we accept this key press as a real key, or
* if it is a ghost. IE. If you press three keys, the keyboard will think
* that you've pressed a fouth: (@ = key down, # = ghost)
*
* 0 1 2 3 4 5 6 7
* | | | | | | | |
* 0-+-+-+-+-+-+-+-+-
* | | | | | | | |
* 1-+-@-+-+-+-@-+-+-
* | | | | | | | |
* 2-+-+-+-+-+-+-+-+-
* | | | | | | | |
* 3-+-@-+-+-+-#-+-+-
* | | | | | | | |
*
* This is what happens when you have a matrix keyboard...
*/
 
real_keycode = scancode_2_colrow[keycode];
 
if ((real_keycode & 0x80) == 0) {
int rr, kc = (real_keycode >> 4) & 7;
int cc;
unsigned short res, kdownkc;
 
kdownkc = ghost_down[kc] | (1 << (real_keycode & 15));
 
for (rr = 0; rr < 128/BITS_PER_SHORT; rr++)
if (rr != kc && (res = ghost_down[rr] & kdownkc)) {
/*
* we have found a second row with at least one key pressed in the
* same column.
*/
for (cc = 0; res; res >>= 1)
cc += (res & 1);
if (cc > 1)
return; /* ignore it */
}
if (up_flag)
clear_bit (real_keycode, ghost_down);
else
set_bit (real_keycode, ghost_down);
}
 
kbd_keyboardkey(keycode, up_flag);
}
 
static inline void kbd_drv_sendbyte(unsigned char val)
{
kbd_txval = val;
enable_irq(IRQ_KEYBOARDTX);
}
 
static inline void kbd_drv_reset(void)
{
int i;
 
for (i = 0; i < 128/BITS_PER_SHORT; i++)
ghost_down[i] = 0;
 
kbd_reset();
}
 
void kbd_drv_setleds(unsigned int leds)
{
leds = ((leds & (1<<VC_SCROLLOCK))?4:0) | ((leds & (1<<VC_NUMLOCK))?2:0) |
((leds & (1<<VC_CAPSLOCK))?1:0);
kbd_drv_sendbyte(leds);
}
 
/*
* Keyboard states:
* 0 initial reset condition, sent HRST, wait for HRST
* 1 Send HRST, wait for HRST acknowledge
* 2 Sent RAK1, wait for RAK1
* 3 Sent RAK2, wait for RAK2
* 4 Sent SMAK, wait for anything
* 5 Wait for second keyboard nibble for key pressed
* 6 Wait for second keyboard nibble for key released
* 7 Wait for second part of mouse data
*/
#define KBD_INITRST 0
#define KBD_RAK1 1
#define KBD_RAK2 2
#define KBD_ID 3
#define KBD_IDLE 4
#define KBD_KEYDOWN 5
#define KBD_KEYUP 6
#define KBD_MOUSE 7
 
static int handle_rawcode(unsigned int keyval)
{
static signed char kbd_mousedx = 0;
signed char kbd_mousedy;
static unsigned char kbd_state = KBD_INITRST;
static unsigned char kbd_keyhigh=0;
 
switch(kbd_state) {
case KBD_INITRST: /* hard reset - sent HRST */
if (keyval == HRST) {
kbd_drv_reset ();
kbd_drv_sendbyte (RAK1);
kbd_state = KBD_RAK1;
} else
goto kbd_wontreset;
break;
 
case KBD_RAK1:/* Sent RAK1 */
switch (keyval) {
case HRST:
kbd_drv_sendbyte (RAK1);
kbd_state = KBD_INITRST;
break;
case RAK1:
kbd_drv_sendbyte (RAK2);
kbd_state = KBD_RAK2;
break;
default:
goto kbd_wontreset;
}
break;
 
case KBD_RAK2:/* Sent RAK2 */
switch (keyval) {
case HRST:
kbd_drv_sendbyte (HRST);
kbd_state = KBD_INITRST;
break;
case RAK2:
if (kbd_id == -1) {
kbd_drv_sendbyte (NACK);
kbd_drv_sendbyte (RQID);
kbd_state = KBD_ID;
} else {
kbd_drv_sendbyte (SMAK);
kbd_state = KBD_IDLE;
}
break;
default:
goto kbd_wontreset;
}
break;
 
case KBD_ID:
if (keyval == HRST) {
kbd_drv_sendbyte (HRST);
kbd_state = KBD_INITRST;
kbd_id = -2;
wake_up(&kbd_waitq);
break;
} else
if ((keyval & 0xc0) == 0x80) {
kbd_id = keyval & 0x3f;
kbd_drv_sendbyte (SMAK);
kbd_state = KBD_IDLE;
wake_up(&kbd_waitq);
break;
}
break;
 
case KBD_IDLE:/* Send SMAK, ready for any reply */
if (keyval == HRST) {
kbd_drv_sendbyte (HRST);
kbd_state = KBD_INITRST;
} else
if (keyval & 0x80) {
if (!(keyval & 0x40)) {
if (kbd_id == -1)
kbd_id = keyval & 0x3f;
else {
kbd_state = KBD_INITRST;
kbd_drv_sendbyte (HRST);
}
break;
}
switch (keyval & 0xf0) {
case 0xc0:
kbd_keyhigh = keyval;
kbd_state = KBD_KEYDOWN;
kbd_drv_sendbyte (BACK);
break;
 
case 0xd0:
kbd_keyhigh = keyval;
kbd_state = KBD_KEYUP;
kbd_drv_sendbyte (BACK);
break;
 
default:
kbd_state = KBD_INITRST;
kbd_drv_sendbyte (HRST);
}
} else {
kbd_mousedx = keyval & 0x40 ? keyval|0x80 : keyval;
kbd_state = KBD_MOUSE;
kbd_drv_sendbyte (BACK);
}
break;
 
case KBD_KEYDOWN:
if ((keyval & 0xf0) != 0xc0)
goto kbd_error;
else {
kbd_state = KBD_IDLE;
kbd_drv_sendbyte (SMAK);
if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
kbd_drv_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), 0);
return 1;
}
break;
 
case KBD_KEYUP:
if ((keyval & 0xf0) != 0xd0)
goto kbd_error;
else {
kbd_state = KBD_IDLE;
kbd_drv_sendbyte (SMAK);
if (((kbd_keyhigh ^ keyval) & 0xf0) == 0)
kbd_drv_key ((keyval & 0x0f) | ((kbd_keyhigh << 4) & 0xf0), UP_FLAG);
return 1;
}
break;
 
case KBD_MOUSE:
if (keyval & 0x80)
goto kbd_error;
else {
kbd_state = KBD_IDLE;
kbd_drv_sendbyte (SMAK);
kbd_mousedy = (char)(keyval & 0x40 ? keyval | 0x80 : keyval);
#ifdef CONFIG_KBDMOUSE
add_mouse_movement((int)kbd_mousedx, (int)kbd_mousedy);
add_mouse_randomness((kbd_mousedy << 8) + kbd_mousedx);
#endif
}
return 1;
}
return 0;
 
kbd_wontreset:
#ifdef KBD_REPORT_ERR
printk ("kbd: keyboard won't reset (kbdstate %d, keyval %02X)\n",
kbd_state, keyval);
#endif
kbd_drv_sendbyte (HRST);
kbd_state = KBD_INITRST;
return 0;
 
kbd_error:
#ifdef KBD_REPORT_ERR
printk ("kbd: keyboard out of sync - resetting\n");
#endif
kbd_drv_sendbyte (HRST);
kbd_state = KBD_INITRST;
return 0;
}
 
static void kbd_drv_rx(int irq, void *dev_id, struct pt_regs *regs)
{
if (handle_rawcode(inb(IOC_KARTRX)))
mark_bh (KEYBOARD_BH);
kbd_setregs(regs);
}
 
static void kbd_drv_tx(int irq, void *dev_id, struct pt_regs *regs)
{
outb (kbd_txval, IOC_KARTTX);
}
 
int kbd_drv_init (void)
{
unsigned long flags;
 
save_flags_cli (flags);
if (request_irq (IRQ_KEYBOARDRX, kbd_drv_rx, 0, "keyboard", NULL) != 0)
panic("Could not allocate keyboard receive IRQ!");
if (request_irq (IRQ_KEYBOARDTX, kbd_drv_tx, 0, "keyboard", NULL) != 0)
panic("Could not allocate keyboard transmit IRQ!");
disable_irq (IRQ_KEYBOARDTX);
(void)inb(IOC_KARTRX);
restore_flags (flags);
 
kbd_drv_sendbyte (HRST);
 
current->timeout = jiffies + HZ; /* wait 1s for keyboard to initialise */
interruptible_sleep_on(&kbd_waitq);
 
printk (KERN_INFO "Keyboard driver v%d.%02d. (", VERSION/100, VERSION%100);
if (kbd_id != -1)
printk ("id=%d ", kbd_id);
printk ("English)\n");
return 0;
}
/vt-dummy.c
0,0 → 1,255
/*
* linux/arch/arm/drivers/char/vt-dummy.c
*
* VT routines
*
* Changelog:
* 05-Sep-1996 RMK Fixed race condition between VT switch & initialisation
* 08-Sep-1996 RMK Adapted Brad Pepers (ramparts@agt.net) console buffering code
* (vt_put_char & vt_flush_chars).
* 20-Sep-1996 RMK Cut down version for StrongARM eval board.
*/
 
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/major.h>
 
#include <asm/segment.h>
 
#include "kbd_kern.h"
#include "vt_kern.h"
 
/*
* VCD functions
*/
extern int vcd_init (struct vt *, int kmallocok, unsigned long *kmem);
extern int vcd_ioctl (struct vt *, int cmd, unsigned long arg);
extern unsigned long vcd_pre_init (unsigned long kmem, struct vt *);
 
static int vt_refcount;
static struct tty_driver vt_driver;
static struct tty_struct *vt_table[MAX_NR_CONSOLES];
static struct termios *vt_termios[MAX_NR_CONSOLES];
static struct termios *vt_termios_locked[MAX_NR_CONSOLES];
 
int shift_state = 0;
struct vt_data vtdata;
struct vt vt_con_data[1];
 
extern void vt_do_blankscreen (int nopowersave);
extern void vt_do_unblankscreen (void);
 
/*
* last_console is the last used console
*/
struct vt *last_console;
 
int vt_deallocate (int arg)
{
return 0;
}
 
void vt_pokeblankedconsole (void)
{
}
 
/*
* for tty_io.c
*/
void vt_do_unblankscreen (void)
{
}
 
int sel_loadlut (const unsigned long arg)
{
return 0;
}
 
int set_selection (const unsigned long arg, struct tty_struct *tty)
{
return 0;
}
 
int paste_selection (struct tty_struct *tty)
{
return 0;
}
 
/*
* for panic.c
*/
void do_unblank_screen (void)
{
}
 
static int vt_open (struct tty_struct *tty, struct file *filp)
{
return 0;
}
 
static int vt_write (struct tty_struct *tty, int from_user,
const unsigned char *buf, int count)
{
return vcd_write (NULL, from_user, buf, count);
}
 
static void vt_put_char (struct tty_struct *tty, unsigned char ch)
{
vcd_write (NULL, 0, &ch, 1);
}
 
static int vt_write_room (struct tty_struct *tty)
{
return 4096;
}
 
static int vt_chars_in_buffer (struct tty_struct *tty)
{
return 0;
}
 
static int vt_ioctl (struct tty_struct *tty, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct vt *vt = tty->driver_data;
int perm;
 
if (!vt_allocated (vt)) /* impossible ? */
return -ENOIOCTLCMD;
/*
* To have permissions to do most of the vt ioctls, we either
* have to be the owner of the tty, or super-user.
*/
perm = 0;
if (current->tty == tty || suser ())
perm = 1;
 
#define PERM if (!perm) return -EPERM
 
switch (cmd) {
case KDGETMODE:
case KDSETMODE:
case VT_GETMODE:
case VT_SETMODE:
case VT_GETSTATE:
case VT_OPENQRY:
case VT_ACTIVATE:
case VT_WAITACTIVE:
case VT_RELDISP:
case VT_GETSCRINFO:
return -EINVAL;
 
case VT_RESIZE:
case KIOCSOUND:
case KDMKTONE:
PERM;
case VT_DISALLOCATE:
return 0;
 
case KDMAPDISP:
case KDUNMAPDISP:
case KDSKBMODE:
case KDSKBMETA:
case KDSETKEYCODE:
case KDSKBENT:
case KDSKBSENT:
case KDSKBDIACR:
case KDSKBLED:
case KDSETLED:
case KDSIGACCEPT:
case KDGKBTYPE:
case KDADDIO:
case KDDELIO:
case KDENABIO:
case KDDISABIO:
case KDGKBMODE:
case KDGKBMETA:
case KDGETKEYCODE:
case KDGKBENT:
case KDGKBSENT:
case KDGKBDIACR:
case KDGKBLED:
case KDGETLED:
return -EINVAL;
 
case VT_SETPALETTE:
case PIO_FONT:
case PIO_SCRNMAP:
case PIO_UNISCRNMAP:
case PIO_UNIMAPCLR:
case PIO_UNIMAP:
PERM;
 
case VT_GETPALETTE:
case GIO_FONT:
case GIO_SCRNMAP:
case GIO_UNISCRNMAP:
case GIO_UNIMAP:
return vcd_ioctl (vt, cmd, arg);
}
return -ENOIOCTLCMD;
}
 
static void vt_dummy (struct tty_struct *tty)
{
}
 
int vcs_init(void)
{
return 0;
}
 
unsigned long vt_pre_init (unsigned long kmem)
{
kmem = vcd_pre_init (kmem, NULL);
 
memset (vt_con_data, 0, sizeof (vt_con_data));
 
vtdata.blanked = NULL;
vtdata.fgconsole = vt_con_data;
 
vt_con_data->tty = &vt_table[0];
vt_con_data->num = 1;
 
return kmem;
}
 
/*
* This is the post initialisation. We have kmalloc setup so we can use it...
*/
void vt_post_init (void)
{
memset (&vt_driver, 0, sizeof (struct tty_driver));
vt_driver.magic = TTY_DRIVER_MAGIC;
vt_driver.name = "tty";
vt_driver.name_base = 1;
vt_driver.major = TTY_MAJOR;
vt_driver.minor_start = 1;
vt_driver.num = MAX_NR_CONSOLES;
vt_driver.type = TTY_DRIVER_TYPE_CONSOLE;
vt_driver.init_termios = tty_std_termios;
vt_driver.flags = TTY_DRIVER_REAL_RAW;
vt_driver.refcount = &vt_refcount;
vt_driver.table = vt_table;
vt_driver.termios = vt_termios;
vt_driver.termios_locked = vt_termios_locked;
vt_driver.open = vt_open;
vt_driver.write = vt_write;
vt_driver.put_char = vt_put_char;
vt_driver.flush_chars = vt_dummy;
vt_driver.write_room = vt_write_room;
vt_driver.chars_in_buffer = vt_chars_in_buffer;
vt_driver.ioctl = vt_ioctl;
vt_driver.stop = vt_dummy;
vt_driver.start = vt_dummy;
vt_driver.throttle = vt_dummy;
vt_driver.unthrottle = vt_dummy;
 
if (tty_register_driver (&vt_driver))
panic ("Couldn't register console driver");
}
/trioserial.h
0,0 → 1,240
/* trioserial.h: Definitions for the Aplio TRIO serial driver.
*
* Copyright (C) 1998 Kenneth Albanowski <kjahds@kjahds.com>,
* D. Jeff Dionne <jeff@ryeham.ee.ryerson.ca>,
* The Silver Hammer Group, Ltd.
*
* Based on:
*
* drivers/char/68328serial.h
*
* Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
*/
 
#ifndef _TRIO_SERIAL_H
#define _TRIO_SERIAL_H
 
#include <linux/config.h>
#include <asm/page.h>
#include <asm/arch-trio/hardware.h>
 
struct serial_struct {
int type;
int line;
int port;
int irq;
int flags;
int xmit_fifo_size;
int custom_divisor;
int baud_base;
unsigned short close_delay;
char reserved_char[2];
int hub6; /* FIXME: We don't have AT&T Hub6 boards! */
unsigned short closing_wait; /* time to wait before closing */
unsigned short closing_wait2; /* no longer used... */
int reserved[4];
};
 
/*
* For the close wait times, 0 means wait forever for serial port to
* flush its output. 65535 means don't wait at all.
*/
#define S_CLOSING_WAIT_INF 0
#define S_CLOSING_WAIT_NONE 65535
 
/*
* Definitions for S_struct (and serial_struct) flags field
*/
#define S_HUP_NOTIFY 0x0001 /* Notify getty on hangups and closes
on the callout port */
#define S_FOURPORT 0x0002 /* Set OU1, OUT2 per AST Fourport settings */
#define S_SAK 0x0004 /* Secure Attention Key (Orange book) */
#define S_SPLIT_TERMIOS 0x0008 /* Separate termios for dialin/callout */
 
#define S_SPD_MASK 0x0030
#define S_SPD_HI 0x0010 /* Use 56000 instead of 38400 bps */
 
#define S_SPD_VHI 0x0020 /* Use 115200 instead of 38400 bps */
#define S_SPD_CUST 0x0030 /* Use user-specified divisor */
 
#define S_SKIP_TEST 0x0040 /* Skip UART test during autoconfiguration */
#define S_AUTO_IRQ 0x0080 /* Do automatic IRQ during autoconfiguration */
#define S_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
#define S_PGRP_LOCKOUT 0x0200 /* Lock out cua opens based on pgrp */
#define S_CALLOUT_NOHUP 0x0400 /* Don't do hangups for cua device */
 
#define S_FLAGS 0x0FFF /* Possible legal S flags */
#define S_USR_MASK 0x0430 /* Legal flags that non-privileged
* users can set or reset */
 
/* Internal flags used only by kernel/chr_drv/serial.c */
#define S_INITIALIZED 0x80000000 /* Serial port was initialized */
#define S_CALLOUT_ACTIVE 0x40000000 /* Call out device is active */
#define S_NORMAL_ACTIVE 0x20000000 /* Normal device is active */
#define S_BOOT_AUTOCONF 0x10000000 /* Autoconfigure port on bootup */
#define S_CLOSING 0x08000000 /* Serial port is closing */
#define S_CTS_FLOW 0x04000000 /* Do CTS flow control */
#define S_CHECK_CD 0x02000000 /* i.e., CLOCAL */
 
/* Software state per channel */
 
#ifdef __KERNEL__
/*
* This is our internal structure for each serial port's state.
*
* Many fields are paralleled by the structure used by the serial_struct
* structure.
*
* For definitions of the flags field, see tty.h
*/
 
struct trio_serial {
char soft_carrier; /* Use soft carrier on this channel */
char break_abort; /* Is serial console in, so process brk/abrt */
#if 0
char cons_keyb; /* Channel runs the keyboard */
char cons_mouse; /* Channel runs the mouse */
char kgdb_channel; /* Kgdb is running on this channel */
#endif
char is_cons; /* Is this our console. */
 
/* We need to know the current clock divisor
* to read the bps rate the chip has currently
* loaded.
*/
unsigned char clk_divisor; /* May be 1, 16, 32, or 64 */
int baud;
int magic;
int baud_base;
int port;
int irq;
int irqmask;
int flags; /* defined in tty.h */
int type; /* UART type */
int use_ints;
struct uart_regs *uart;
struct tty_struct *tty;
int read_status_mask;
int ignore_status_mask;
int timeout;
int xmit_fifo_size;
int custom_divisor;
int x_char; /* xon/xoff character */
int close_delay;
unsigned short closing_wait;
unsigned short closing_wait2;
unsigned long event;
unsigned long last_active;
int line;
int count; /* # of fd on device */
int blocked_open; /* # of blocked opens */
long session; /* Session of opening process */
long pgrp; /* pgrp of opening process */
unsigned char *xmit_buf;
unsigned char *rx_buf;
int xmit_head;
int xmit_tail;
int xmit_cnt;
struct tq_struct tqueue;
struct tq_struct tqueue_hangup;
struct termios normal_termios;
struct termios callout_termios;
struct wait_queue *open_wait;
struct wait_queue *close_wait;
};
 
 
#define SERIAL_MAGIC 0x5301
 
/*
* The size of the serial xmit buffer is 1 page, or 4096 bytes
*/
#define SERIAL_XMIT_SIZE PAGE_SIZE
#define SERIAL_RX_SIZE PAGE_SIZE
 
/*
* Events are used to schedule things to happen at timer-interrupt
* time, instead of at rs interrupt time.
*/
#define RS_EVENT_WRITE_WAKEUP 0
 
#endif /* __KERNEL__ */
 
#if defined(CONFIG_M68328) || defined(CONFIG_M68EZ328)
 
#define BASE 0xfffff900
#define VSP(X) (*(volatile unsigned short *)(X))
#define VCP(X) (*(volatile unsigned char *)(X))
 
/* UART status and control */
#define USTCNT VSP(BASE)
#define USTCNT_EN (1 << 15)
#define USTCNT_RXEN (1 << 14)
#define USTCNT_TXEN (1 << 13)
#define USTCNT_CLKMODE (1 << 12)
#define USTCNT_PARITY (1 << 11)
#define USTCNT_ODDEVEN (1 << 10)
#define USTCNT_STOP (1 << 9)
#define USTCNT_WLEN (1 << 8)
 
#define USTCNT_CTSEN (1 << 6)
#define USTCNT_RXFULLEN (1 << 5)
#define USTCNT_RXHALFEN (1 << 4)
#define USTCNT_READYEN (1 << 3)
#define USTCNT_TXEMPTYEN (1 << 2)
#define USTCNT_TXHALFEN (1 << 1)
#define USTCNT_TXAVAILEN (1 << 0)
 
/* UART baud control */
#define UBAUD VSP(BASE+2)
#define UBAUD_GPIOF (1 << 15)
#define UBAUD_GPIOSC (1 << 14)
#define UBAUD_GPIODDR (1 << 13)
#define UBAUD_GPIOSRC (1 << 12)
#define UBAUD_BAUDSRC (1 << 11)
#define UBAUD_DIVMASK (0x7 << 8)
#define UBAUD_GETDIV(X) ((0x7 & (X)) >> 8)
#define UBAUD_SETDIV(X) ((0x7 & (X)) << 8)
#define UBAUD_PRESCALEMASK (0x3f << 0)
#define UBAUD_PRESCALE(X) ((0x3f & (X)) << 0)
 
/* UART RX and status */
#define URX VSP(BASE + 4)
#define URX_FIFOFULL (1<<15)
#define URX_FIFOHALF (1<<14)
#define URX_DATARDY (1<<13)
#define URX_OVERRUN (1<<11)
#define URX_FRAMEERR (1<<10)
#define URX_BREAK (1<< 9)
#define URX_PARITYERR (1<< 8)
#define URX_CHARSTAT (URX_OVERRUN |URX_FRAMEERR |URX_BREAK |URX_PARITYERR)
#define URX_CHARMASK (0xff << 0)
#define URX_CHAR(X) ((0xff & (X)) << 0)
 
/* UART TX and status */
#define UTX VCP(BASE + 6)
#define UTX_FIFOEMPTY (1<< 7)
#define UTX_FIFOHALF (1<< 6)
#define UTX_TXAVAIL (1<< 5)
#define UTX_SENDBREAK (1<< 4)
#define UTX_IGNCTS (1<< 3)
#define UTX_CTSSTAT (1<< 1)
#define UTX_CTSDELTA (1<< 0)
 
#define UTX_CHARMASK (0xff << 0)
#define UTX_CHAR VCP(BASE + 7)
 
/* UART misc */
#define UMISC VSP(BASE + 8)
#define UMISC_CLKSRC (1<<14)
#define UMISC_GENPTERR (1<<13)
#define UMISC_LOOP (1<<12)
#define UMISC_RTSCTL (1<< 7)
#define UMISC_RTS (1<< 6)
#define UMISC_IRDAEN (1<< 5)
#define UMISC_LOOPIR (1<< 4)
#define UMISC_RXPOL (1<< 3)
#define UMISC_TXPOL (1<< 2)
#endif /* CONFIG_MC68328 */
 
#endif /* !(_MC683XX_SERIAL_H) */
/selection.c
0,0 → 1,296
/*
* linux/arch/arm/drivers/char/console/selection.c
*
* This module exports the functions:
*
* 'int set_selection(const unsigned long arg)'
* 'void clear_selection(void)'
* 'int paste_selection(struct tty_struct *tty)'
* 'int sel_loadlut(const unsigned long arg)'
*
* Now that /dev/vcs exists, most of this can disappear again.
*/
 
#include <linux/tty.h>
#include <linux/sched.h>
#include <linux/mm.h>
#include <linux/malloc.h>
#include <linux/types.h>
 
#include <asm/segment.h>
 
#include "vt_kern.h"
#include "consolemap.h"
#include "selection.h"
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
/* Don't take this from <ctype.h>: 011-015 in the buffer aren't spaces */
#define isspace(c) ((c) == ' ')
 
#define sel_pos(n) inverse_translate(scrw2glyph(screen_word(vtdata.select.vt, n)))
 
/*
* clear_selection, highlight and highlight_pointer can be called
* from interrupt (via scrollback/front)
*/
 
/*
* set reverse video on characters s-e of console with selection.
*/
static inline void highlight (const int s, const int e)
{
invert_screen(vtdata.select.vt, s, e - s);
}
 
/*
* use complementary color to show the pointer
*/
static inline void highlight_pointer (int where)
{
complement_pos(vtdata.select.vt, where);
}
 
/*
* Remove the current selection highlight, if any,
* from the console holding selection.
*/
void clear_selection (void)
{
highlight_pointer(-1); /* hide the pointer */
if (vtdata.select.start != -1) {
highlight(vtdata.select.start, vtdata.select.end);
vtdata.select.start = -1;
}
}
 
/*
* User settable table: what characters are to be considered alphabetic?
* 256 bits
*/
static u32 inwordLut[8]={
0x00000000, /* control chars */
0x03FF0000, /* digits */
0x87FFFFFE, /* uppercase and '_' */
0x07FFFFFE, /* lowercase */
0x00000000,
0x00000000,
0xFF7FFFFF, /* latin-1 accented letters, not multiplication sign */
0xFF7FFFFF /* latin-1 accented letters, not division sign */
};
 
static inline int inword(const unsigned char c)
{
return (inwordLut[c>>5] >> (c & 31)) & 1;
}
 
/*
* set inwordLut contents. Invoked by ioctl().
*/
int sel_loadlut(const unsigned long arg)
{
int i = verify_area(VERIFY_READ, (char *) arg, 36);
if (i)
return i;
memcpy_fromfs(inwordLut, (u32 *)(arg+4), 32);
return 0;
}
 
/*
* does buffer offset p correspond to character at LH/RH edge of screen?
*/
static inline int atedge(const int p)
{
return (!(p % vtdata.numcolumns) || !((p + 1) % vtdata.numcolumns));
}
 
/*
* constrain v such that v <= u
*/
static inline int limit (const int v, const int u)
{
return ((v > u) ? u : v);
}
 
/*
* set the current selection. Invoked by ioctl().
*/
int set_selection (const unsigned long arg, struct tty_struct *tty)
{
struct vt * vt = vtdata.fgconsole;
int sel_mode, new_sel_start, new_sel_end, spc;
char *bp, *obp;
int i, ps, pe;
 
vt_do_unblankscreen ();
 
{
unsigned short *args, xs, ys, xe, ye;
 
args = (unsigned short *)(arg + 1);
xs = get_user (args ++) - 1;
ys = get_user (args ++) - 1;
xe = get_user (args ++) - 1;
ye = get_user (args ++) - 1;
sel_mode = get_user (args);
 
xs = limit (xs, vtdata.numcolumns - 1);
ys = limit (ys, vtdata.numrows - 1);
xe = limit (xe, vtdata.numcolumns - 1);
ye = limit (ye, vtdata.numrows - 1);
ps = ys * vtdata.numcolumns + xs;
pe = ye * vtdata.numcolumns + xe;
 
if (sel_mode == 4) {
/* useful for screendump without selection highlights */
clear_selection ();
return 0;
}
 
if (vt->vcd->report_mouse && sel_mode & 16) {
mouse_report (tty, sel_mode & 15, xs, ys);
return 0;
}
}
 
if (ps > pe) { /* make sel_start <= sel_end */
ps ^= pe;
pe ^= ps;
ps ^= pe;
}
 
if (vt != vtdata.select.vt) {
clear_selection ();
vtdata.select.vt = vt;
}
 
switch (sel_mode) {
case 0: /* character-by-character selection */
new_sel_start = ps;
new_sel_end = pe;
break;
case 1: /* word-by-word selection */
spc = isspace (sel_pos (ps));
for (new_sel_start = ps; ; ps --) {
if (( spc && !isspace (sel_pos (ps))) ||
(!spc && !inword (sel_pos (ps))))
break;
new_sel_start = ps;
if (!(ps % vtdata.numcolumns))
break;
}
spc = isspace (sel_pos (pe));
for (new_sel_end = pe; ; pe ++) {
if (( spc && !isspace (sel_pos (pe))) ||
(!spc && !inword (sel_pos (pe))))
break;
new_sel_end = pe;
if (!((pe + 1) % vtdata.numcolumns))
break;
}
break;
case 2: /* line-by-line selection */
new_sel_start = ps - ps % vtdata.numcolumns;
new_sel_end = pe + vtdata.numcolumns - pe % vtdata.numcolumns - 1;
break;
case 3:
highlight_pointer (pe);
return 0;
default:
return -EINVAL;
}
 
/* remove the pointer */
highlight_pointer (-1);
 
/* select to end of line if on trailing space */
if (new_sel_end > new_sel_start && !atedge(new_sel_end) && isspace(sel_pos(new_sel_end))) {
for (pe = new_sel_end + 1; ; pe ++)
if (!isspace (sel_pos (pe)) || atedge (pe))
break;
if (isspace (sel_pos (pe)))
new_sel_end = pe;
}
if (vtdata.select.start == -1) /* no current selection */
highlight (new_sel_start, new_sel_end);
else if (new_sel_start == vtdata.select.start) {
if (new_sel_end == vtdata.select.end) /* no action required */
return 0;
else if (new_sel_end > vtdata.select.end) /* extend to right */
highlight (vtdata.select.end + 1, new_sel_end);
else /* contract from right */
highlight (new_sel_end + 1, vtdata.select.end);
} else if (new_sel_end == vtdata.select.end) {
if (new_sel_start < vtdata.select.start) /* extend to left */
highlight (new_sel_start, vtdata.select.start - 1);
else /* contract from left */
highlight (vtdata.select.start, new_sel_start - 1);
} else { /* some other case; start selection from scratch */
clear_selection ();
highlight (new_sel_start, new_sel_end);
}
vtdata.select.start = new_sel_start;
vtdata.select.end = new_sel_end;
 
if (vtdata.select.buffer)
kfree (vtdata.select.buffer);
vtdata.select.buffer = kmalloc (vtdata.select.end - vtdata.select.start + 1, GFP_KERNEL);
if (!vtdata.select.buffer) {
printk ("selection: kmalloc() failed\n");
clear_selection ();
return -ENOMEM;
}
 
obp = bp = vtdata.select.buffer;
for (i = vtdata.select.start; i <= vtdata.select.end; i++) {
*bp = sel_pos (i);
if (!isspace (*bp++))
obp = bp;
if (!((i + 1) % vtdata.numcolumns)) {
/* strip trailing blanks from line and add newline,
* unless non-space at end of line.
*/
if (obp != bp) {
bp = obp;
*bp++ = '\r';
}
obp = bp;
}
}
vtdata.select.length = bp - vtdata.select.buffer;
return 0;
}
 
/* Insert the contents of the selection buffer into the queue of the
* tty associated with the current console. Invoked by ioctl().
*/
int paste_selection (struct tty_struct *tty)
{
struct wait_queue wait = { current, NULL };
struct vt_struct *vt = ((struct vt *)tty->driver_data)->vtd;
char *bp = vtdata.select.buffer;
int c = vtdata.select.length;
int l;
 
if (!bp || !c)
return 0;
vt_do_unblankscreen ();
add_wait_queue(&vt->paste_wait, &wait);
do {
current->state = TASK_INTERRUPTIBLE;
if (test_bit(TTY_THROTTLED, &tty->flags)) {
schedule();
continue;
}
l = MIN(c, tty->ldisc.receive_room(tty));
tty->ldisc.receive_buf(tty, bp, 0, l);
c -= l;
bp += l;
} while (c);
remove_wait_queue(&vt->paste_wait, &wait);
current->state = TASK_RUNNING;
return 0;
}
 
/serial-atomwide.c
0,0 → 1,20
/*
* linux/arch/arm/drivers/char/serial-atomwide.c
*
* Copyright (c) 1996 Russell King.
*
* Changelog:
* 02-05-1996 RMK Created
* 07-05-1996 RMK Altered for greater number of cards.
* 30-07-1996 RMK Now uses generic card code.
*/
 
#define MY_CARD_LIST { MANU_ATOMWIDE, PROD_ATOMWIDE_3PSERIAL }
#define MY_NUMPORTS 3
#define MY_BAUD_BASE (7372800 / 16)
#define MY_INIT atomwide_serial_init
#define MY_BASE_ADDRESS(ec) \
ecard_address ((ec), ECARD_IOC, ECARD_SLOW) + (0x2000 >> 2)
#define MY_PORT_ADDRESS(port,cardaddr) \
((cardaddr) + 0x200 - (port) * 0x100)
#include "serial-card.c"
/serial-card.c
0,0 → 1,107
/*
* linux/arch/arm/drivers/char/serial-module.c
*
* Copyright (c) 1996 Russell King.
*
* A generic handler of serial expansion cards that use 16550s or
* the like.
*
* Definitions:
* MY_PRODS Product numbers to identify this card by
* MY_MANUS Manufacturer numbers to identify this card by
* MY_NUMPORTS Number of ports per card
* MY_BAUD_BASE Baud base for the card
* MY_INIT Initialisation routine name
* MY_BASE_ADDRESS(ec) Return base address for ports
* MY_PORT_ADDRESS
* (port,cardaddr) Return address for port using base address
* from above.
*
* Changelog:
* 30-07-1996 RMK Created
*/
#include <linux/module.h>
#include <linux/serial.h>
#include <linux/errno.h>
#include <asm/ecard.h>
 
#ifndef NUM_SERIALS
#define NUM_SERIALS MY_NUMPORTS * MAX_ECARDS
#endif
 
#ifdef MODULE
static int __serial_ports[NUM_SERIALS];
static int __serial_pcount;
static struct expansion_card *expcard[MAX_ECARDS];
#define ADD_ECARD(ec,card) expcard[(card)] = (ec)
#define ADD_PORT(port) __serial_ports[__serial_pcount++] = (port)
#define REGISTER(ss) register_serial(ss)
#undef MY_INIT
#define MY_INIT init_module
#else
#define ADD_ECARD(ec,card)
#define ADD_PORT(port)
#define REGISTER(ss) register_pre_init_serial(ss)
#endif
 
static const card_ids serial_cids[] = { MY_CARD_LIST, { 0xffff, 0xffff } };
 
static inline int serial_register_onedev (unsigned long port, int irq)
{
struct serial_struct req;
 
req.baud_base = MY_BAUD_BASE;
req.irq = irq;
req.port = port;
 
return REGISTER (&req);
}
 
int MY_INIT (void)
{
int card = 0;
 
ecard_startfind ();
 
do {
struct expansion_card *ec;
unsigned long cardaddr;
int port;
 
ec = ecard_find (0, serial_cids);
if (!ec)
break;
 
cardaddr = MY_BASE_ADDRESS(ec);
 
for (port = 0; port < MY_NUMPORTS; port ++) {
int line;
 
line = serial_register_onedev (MY_PORT_ADDRESS(port, cardaddr), ec->irq);
if (line < 0)
break;
ADD_PORT(line);
}
 
if (port) {
ecard_claim (ec);
ADD_ECARD(ec, card);
} else
break;
} while (++card < MAX_ECARDS);
return card ? 0 : -ENODEV;
}
 
#ifdef MODULE
void cleanup_module (void)
{
int i;
 
for (i = 0; i < __serial_pcount; i++)
unregister_serial (__serial_ports[i]);
 
for (i = 0; i < MAX_ECARDS; i++)
if (expcard[i])
ecard_release (expcard[i]);
}
#endif
/mouse.c
0,0 → 1,209
/*
* linux/arch/arm/drivers/char/mouse.c
*
* Copyright (C) 1995, 1996 Russell King
*
* Medium-level interface for quadrature mouse.
*/
 
#include <linux/module.h>
#include <linux/config.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/signal.h>
#include <linux/errno.h>
#include <linux/mm.h>
#include <linux/miscdevice.h>
#include <linux/random.h>
 
#include <asm/system.h>
#include <asm/segment.h>
#include <asm/io.h>
 
#include "mouse.h"
 
#ifdef CONFIG_RPCMOUSE
extern void mouse_rpc_init (void);
#endif
 
static struct wait_queue *mouse_wait;
static struct fasync_struct *fasyncptr;
static char mouse_active;
static char mouse_buttons;
static char mouse_ready;
static int mouse_present;
static int mouse_dxpos;
static int mouse_dypos;
 
/*
* a mouse driver just has to interface with these functions
*/
void add_mouse_movement(int dx, int dy)
{
mouse_dxpos += dx;
mouse_dypos += dy;
mouse_ready = 1;
wake_up(&mouse_wait);
}
 
int add_mouse_buttonchange(int set, int value)
{
mouse_buttons = (mouse_buttons & ~set) ^ value;
mouse_ready = 1;
wake_up(&mouse_wait);
return mouse_buttons;
}
 
static int fasync_mouse(struct inode *inode, struct file *filp, int on)
{
int retval;
 
retval = fasync_helper(inode, filp, on, &fasyncptr);
if (retval < 0)
return retval;
return 0;
}
 
static void close_mouse(struct inode *inode, struct file *file)
{
fasync_mouse (inode, file, 0);
if (--mouse_active)
return;
mouse_ready = 0;
 
MOD_DEC_USE_COUNT;
}
 
static int open_mouse(struct inode *inode,struct file *file)
{
unsigned long flags;
 
if (!mouse_present)
return -EINVAL;
 
if (mouse_active++)
return 0;
 
MOD_INC_USE_COUNT;
 
save_flags_cli (flags);
mouse_active = 1;
mouse_ready = 0;
mouse_dxpos = 0;
mouse_dypos = 0;
mouse_buttons = 7;
restore_flags (flags);
 
return 0;
}
 
static int write_mouse(struct inode *inode,struct file *file,const char *buffer,int count)
{
return -EINVAL;
}
 
static int read_mouse(struct inode *inode,struct file *file,char *buffer,int count)
{
unsigned long flags;
int dxpos, dypos, i, buttons;
 
if (count < 3)
return -EINVAL;
if ((i = verify_area(VERIFY_WRITE, buffer, count)))
return i;
if (!mouse_ready)
return -EAGAIN;
 
save_flags_cli (flags);
 
dxpos = mouse_dxpos;
dypos = mouse_dypos;
buttons = mouse_buttons;
 
if (dxpos < -127)
dxpos =- 127;
if (dxpos > 127)
dxpos = 127;
if (dypos < -127)
dypos =- 127;
if (dypos > 127)
dypos = 127;
 
mouse_dxpos -= dxpos;
mouse_dypos -= dypos;
mouse_ready = 0;
 
restore_flags (flags);
 
put_user ((char) buttons | 128, buffer);
put_user ((char) dxpos, buffer + 1);
put_user ((char) dypos, buffer + 2);
for(i = 3; i < count; i++)
put_user (0, buffer + i);
return i;
}
 
static int select_mouse(struct inode *inode,struct file *file,int sel_type,select_table *wait)
{
if (sel_type == SEL_IN) {
if (mouse_ready)
return 1;
select_wait (&mouse_wait,wait);
}
return 0;
}
 
struct file_operations kbd_mouse_fops=
{
NULL, /* mouse_seek */
read_mouse,
write_mouse,
NULL, /* mouse_readdir */
select_mouse,
NULL, /* mouse_ioctl */
NULL, /* mouse_mmap */
open_mouse,
close_mouse,
NULL,
fasync_mouse,
};
 
static struct miscdevice mouse_misc = {
6, "mouse", &kbd_mouse_fops, NULL, NULL
};
 
int misc_mouse_init(void)
{
unsigned long flags;
 
save_flags_cli (flags);
 
mouse_buttons = 0;
mouse_dxpos = 0;
mouse_dypos = 0;
mouse_present = 1;
mouse_ready = 0;
mouse_active = 0;
mouse_wait = NULL;
 
restore_flags (flags);
misc_register (&mouse_misc);
 
#ifdef CONFIG_RPCMOUSE
mouse_rpc_init();
#endif
return 0;
}
 
#ifdef MODULE
int init_module(void)
{
return misc_mouse_init();
}
 
void cleanup_module(void)
{
misc_deregister (&mouse_misc);
}
#endif
/Config.in
0,0 → 1,37
#
# Character device configuration
#
mainmenu_option next_comment
comment 'Character devices'
 
bool 'Echo console messages on /dev/ttyS0' CONFIG_SERIAL_ECHO
tristate 'TRIO serial support' CONFIG_SERIAL_TRIO
tristate 'Parallel printer support' CONFIG_PRINTER
tristate 'Standard/generic serial support' CONFIG_SERIAL
if [ "$CONFIG_ARCH_EBSA110" != "y" ]; then
if [ "$CONFIG_SERIAL" != "n" ]; then
tristate ' Atomwide serial port support' CONFIG_ATOMWIDE_SERIAL
tristate ' Dual serial port support' CONFIG_DUALSP_SERIAL
fi
 
bool 'Mouse support' CONFIG_MOUSE
fi
 
bool 'Support for user misc device modules' CONFIG_UMISC
 
bool 'Watchdog Timer Support' CONFIG_WATCHDOG
if [ "$CONFIG_WATCHDOG" != "n" ]; then
bool ' Disable watchdog shutdown on close' CONFIG_WATCHDOG_NOWAYOUT
define_bool CONFIG_SOFT_WATCHDOG y
fi
 
if [ "$CONFIG_MOUSE" = "y" ]; then
if [ "$CONFIG_ARCH_ARC" = "y" -o "$CONFIG_ARCH_A5K" = "y" ]; then
define_bool CONFIG_KBDMOUSE y
fi
if [ "$CONFIG_ARCH_RPC" = "y" ]; then
define_bool CONFIG_RPCMOUSE y
fi
fi
endmenu
 
/vt.c
0,0 → 1,1084
/*
* linux/arch/arm/drivers/char/vt.c
*
* VT routines
*
* Changelog:
* 05-Sep-1996 RMK Fixed race condition between VT switch & initialisation
* 08-Sep-1996 RMK Adapted Brad Pepers (ramparts@agt.net) console buffering code
* (vt_put_char & vt_flush_chars).
* 02-Sep-1997 RMK Added in VT switch disable
*/
 
#include <linux/config.h>
#include <linux/sched.h>
#include <linux/tty.h>
#include <linux/kd.h>
#include <linux/errno.h>
#include <linux/malloc.h>
#include <linux/mm.h>
#include <linux/tty.h>
#include <linux/major.h>
 
#include <asm/segment.h>
#include <asm/hardware.h>
 
#include "kbd_kern.h"
#include "vt_kern.h"
 
#define CON_XMIT_SIZE 2048
 
#ifndef MIN
#define MIN(a,b) ((a) < (b) ? (a) : (b))
#endif
 
/*
* VCD functions
*/
extern void vcd_blankscreen (int nopowersave);
extern void vcd_disallocate (struct vt *);
extern int vcd_init (struct vt *, int kmallocok, unsigned long *kmem);
extern int vcd_ioctl (struct vt *, int cmd, unsigned long arg);
extern unsigned long vcd_pre_init (unsigned long kmem, struct vt *);
extern int vcd_resize (int rows, int columns);
extern void vcd_restorestate (const struct vt *);
extern void vcd_savestate (const struct vt *, int blanked);
extern void vcd_unblankscreen (void);
extern int vcd_write (const struct vt *, int from_user, const unsigned char *buf, int count);
extern void vcd_setup_graphics (const struct vt *);
 
/*
* Console functions
*/
extern void con_reset_palette (const struct vt *vt);
extern void con_set_palette (const struct vt *vt);
extern int con_init (void);
 
static int vt_refcount;
int do_poke_blanked_console;
static struct tty_driver vt_driver;
static struct tty_struct *vt_table[MAX_NR_CONSOLES];
static struct termios *vt_termios[MAX_NR_CONSOLES];
static struct termios *vt_termios_locked[MAX_NR_CONSOLES];
 
struct vt_data vtdata;
struct vt vt_con_data[MAX_NR_CONSOLES];
 
extern void vt_do_blankscreen (int nopowersave);
extern void vt_do_unblankscreen (void);
 
/* set this to the sound driver's wave output trigger */
void (*mksound_hook)(unsigned int freq, unsigned int vol, unsigned int duration);
/*
* last_console is the last used console
*/
struct vt *last_console;
struct vt *want_console;
static char vt_dont_switch = 0;
 
/*
* Sometimes we want to wait until a particular VT has been activated. We
* do it in a very simple manner. Everybody waits on a single queue and
* get woken up at once. Those that are satisfied go on with their business,
* while those not ready go back to sleep. Seems overkill to add a wait
* to each vt just for this - usually this does nothing!
*/
static struct wait_queue *vt_activate_queue = NULL;
 
/*
* Sleeps until a vt is activated, or the task is interrupted. Returns
* 0 if activation, -1 if interrupted.
*/
static int vt_waitonactivate (void)
{
interruptible_sleep_on (&vt_activate_queue);
return (current->signal & ~current->blocked) ? -1 : 0;
}
 
#define vt_wake_waitactive() wake_up(&vt_activate_queue)
 
void vt_reset (const struct vt *vt)
{
vt->kbd->kbdmode = VC_XLATE;
vt->vtd->vc_mode = KD_TEXT;
vt->vtd->vt_mode.mode = VT_AUTO;
vt->vtd->vt_mode.waitv = 0;
vt->vtd->vt_mode.relsig = 0;
vt->vtd->vt_mode.acqsig = 0;
vt->vtd->vt_mode.frsig = 0;
vt->vtd->vt_pid = -1;
vt->vtd->vt_newvt = NULL;
con_reset_palette (vt);
}
 
static int vt_allocate (struct vt *vt)
{
if (!vt_allocated(vt)) {
void *data, *p;
int r;
 
data = kmalloc (sizeof (*vt->vcd) +
sizeof (*vt->kbd) +
sizeof (*vt->vtd) + CON_XMIT_SIZE, GFP_KERNEL);
if (!data)
return -ENOMEM;
 
vt->vcd = data; p = ((struct con_struct *)data + 1);
vt->kbd = p; p = ((struct kbd_struct *)p + 1);
vt->vtd = p; p = ((struct vt_struct *)p + 1);
vt->vtd->xmit_buf = p;
 
if ((r = kbd_struct_init (vt, 1)) < 0) {
vt->vcd = NULL;
vt->kbd = NULL;
vt->vtd = NULL;
kfree (data);
return r;
}
 
if ((r = vcd_init (vt, 1, NULL)) < 0) {
vt->vcd = NULL;
vt->kbd = NULL;
vt->vtd = NULL;
kfree (data);
return r;
}
vt_reset (vt);
vt->vtd->xmitting = vt->vtd->xmit_cnt =
vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
vt->allocinit = 1;
}
return 0;
}
 
static int vt_disallocate (struct vt *vt)
{
if (vt_allocated (vt)) {
void *data = vt->vcd;
 
vt->allocinit = 0;
vcd_disallocate (vt);
vt->vcd = NULL;
vt->kbd = NULL;
vt->vtd = NULL;
 
kfree (data);
}
return 0;
}
 
void vt_updatescreen (const struct vt *newvt)
{
static int lock = 0;
 
if (newvt == vtdata.fgconsole || lock)
return;
 
if (!vt_allocated (newvt)) {
printk ("updatescreen: tty %d not allocated ??\n", newvt->num);
return;
}
 
lock = 1;
 
vcd_savestate (vtdata.fgconsole, vtdata.blanked != NULL);
 
vtdata.fgconsole = (struct vt *)newvt;
 
vcd_restorestate (vtdata.fgconsole);
compute_shiftstate ();
 
lock = 0;
}
 
/*
* Performs the back end of a vt switch
*/
void vt_completechangeconsole (const struct vt *new_console)
{
unsigned char old_vt_mode;
struct vt *old_vt = vtdata.fgconsole;
 
if (new_console == old_vt || (vt_dont_switch))
return;
if (!vt_allocated (new_console))
return;
last_console = old_vt;
 
/*
* If we're switching, we could be going from KD_GRAPHICS to
* KD_TEXT mode or vice versa, which means we need to blank or
* unblank the screen later.
*/
old_vt_mode = old_vt->vtd->vc_mode;
vt_updatescreen (new_console);
 
/*
* If this new console is under process control, send it a signal
* telling it that it has acquired. Also check if it has died and
* clean up (similar to logic employed in vt_changeconsole())
*/
if (new_console->vtd->vt_mode.mode == VT_PROCESS) {
/*
* Send the signal as privileged - kill_proc() will
* tell us if the process has gone or something else
* is awry
*/
if (kill_proc(new_console->vtd->vt_pid,
new_console->vtd->vt_mode.acqsig,
1) != 0) {
/*
* The controlling process has died, so we revert back to
* normal operation. In this case, we'll also change back
* to KD_TEXT mode. I'm not sure if this is strictly correct
* but it saves the agony when the X server dies and the screen
* remains blanked due to KD_GRAPHICS! It would be nice to do
* this outside of VT_PROCESS but there is no single process
* to account for and tracking tty count may be undesirable.
*/
vt_reset (new_console);
}
}
 
/*
* We do this here because the controlling process above may have
* gone, and so there is now a new vc_mode
*/
if (old_vt_mode != new_console->vtd->vc_mode) {
if (new_console->vtd->vc_mode == KD_TEXT)
vt_do_unblankscreen ();
else {
vt_do_blankscreen (1);
vcd_setup_graphics (new_console);
}
}
 
/* Set the colour palette for this VT */
if (new_console->vtd->vc_mode == KD_TEXT)
con_set_palette (new_console);
/*
* Wake anyone waiting for their VT to activate
*/
vt_wake_waitactive();
return;
}
 
/*
* Performs the front-end of a vt switch
*/
void vt_changeconsole (struct vt *new_console)
{
struct vt *old_vt = vtdata.fgconsole;
 
if (new_console == old_vt || (vt_dont_switch))
return;
if (!vt_allocated (new_console))
return;
 
/*
* If this vt is in process mode, then we need to handshake with
* that process before switching. Essentially, we store where that
* vt wants to switch to and wait for it to tell us when it's done
* (via VT_RELDISP ioctl).
*
* We also check to see if the controlling process still exists.
* If it doesn't, we reset this vt to auto mode and continue.
* This is a cheap way to track process control. The worst thing
* that can happen is: we send a signal to a process, it dies, and
* the switch gets "lost" waiting for a response; hopefully, the
* user will try again, we'll detect the process is gone (unless
* the user waits just the right amount of time :-) and revert the
* vt to auto control.
*/
if (old_vt->vtd->vt_mode.mode == VT_PROCESS) {
/*
* Send the signal as privileged - kill_proc() will
* tell us if the process has gone or something else
* is awry
*/
if (kill_proc(old_vt->vtd->vt_pid, old_vt->vtd->vt_mode.relsig, 1) == 0) {
/*
* It worked. Mark the vt to switch to and
* return. The process needs to send us a
* VT_RELDISP ioctl to complete the switch.
*/
old_vt->vtd->vt_newvt = new_console;
return;
}
 
/*
* The controlling process has died, so we revert back to
* normal operation. In this case, we'll also change back
* to KD_TEXT mode. I'm not sure if this is strictly correct
* but it saves the agony when the X server dies and the screen
* remains blanked due to KD_GRAPHICS! It would be nice to do
* this outside of VT_PROCESS but there is no single process
* to account for and tracking tty count may be undesirable.
*/
vt_reset (old_vt);
 
/*
* Fall through to normal (VT_AUTO) handling of the switch...
*/
}
 
/*
* Ignore all switches in KD_GRAPHICS+VT_AUTO mode
*/
if (old_vt->vtd->vc_mode == KD_GRAPHICS)
return;
 
vt_completechangeconsole (new_console);
}
 
/*
* If a vt is under process control, the kernel will not switch to it
* immediately, but postpone the operation until the process calls this
* ioctl, allowing the switch to complete.
*
* According to the X sources this is the behavior:
* 0: pending switch-from not OK
* 1: pending switch-from OK
* 2: completed switch-to OK
*/
static inline int vt_reldisp (const struct vt *old_vt, int arg)
{
int i;
 
if (old_vt->vtd->vt_mode.mode != VT_PROCESS)
return -EINVAL;
 
if (old_vt->vtd->vt_newvt) {
/*
* Switching-from response
*/
if (arg == 0)
/*
* Switch disallowed, so forget we were trying
* to do it.
*/
old_vt->vtd->vt_newvt = NULL;
else {
/*
* The current vt has been released, so complete the
* switch.
*/
struct vt *new_vt = old_vt->vtd->vt_newvt;
old_vt->vtd->vt_newvt = NULL;
i = vt_allocate (new_vt);
if (i)
return i;
vt_completechangeconsole (new_vt);
}
} else {
/*
* Switched-to response
*/
if (arg != VT_ACKACQ)
return -EINVAL;
}
return 0;
}
 
/*
* Set the mode of a VT.
*/
static inline int vt_kdsetmode (const struct vt *vt, int mode)
{
/*
* Currently, setting the mode from KD_TEXT to KD_GRAPHICS
* doesn't do a whole lot. I'm not sure if it should do any
* restoration of modes or what...
*/
switch (mode) {
case KD_TEXT0:
case KD_TEXT1:
mode = KD_TEXT;
case KD_GRAPHICS:
case KD_TEXT:
break;
default:
return -EINVAL;
}
 
if (vt->vtd->vc_mode == mode)
return 0;
 
vt->vtd->vc_mode = mode;
if (vt != vtdata.fgconsole)
return 0;
 
/*
* explicitly blank/unblank the screen if switching modes
*/
if (mode == KD_TEXT) {
vt_do_unblankscreen ();
vcd_restorestate (vt);
} else {
vt_do_blankscreen (1);
vcd_setup_graphics (vt);
}
return 0;
}
 
static inline int vt_setmode (const struct vt *vt, struct vt_mode *vtmode)
{
if (vtmode->mode != VT_AUTO && vtmode->mode != VT_PROCESS)
return -EINVAL;
 
vt->vtd->vt_mode = *vtmode;
vt->vtd->vt_mode.frsig = 0;
vt->vtd->vt_pid = current->pid;
vt->vtd->vt_newvt = NULL;
return 0;
}
 
void vt_mksound (unsigned int freq, unsigned int vol, unsigned int ticks)
{
if (mksound_hook)
mksound_hook (freq, vol, ticks);
}
 
/*
* Deallocate memory associated to VT (but leave VT1)
*/
int vt_deallocate (int arg)
{
int i;
 
if (arg == 0) {
/*
* deallocate all unused consoles, but leave 0
*/
for (i = 1; i < MAX_NR_CONSOLES; i++)
if (!VT_BUSY (i))
vt_disallocate (vt_con_data + i);
} else {
arg -= 1;
if (VT_BUSY (arg))
return -EBUSY;
if (arg)
vt_disallocate (vt_con_data + arg);
}
return 0;
}
 
int vt_resize (int columns, int rows)
{
return vcd_resize (rows, columns);
}
 
void vt_pokeblankedconsole (void)
{
timer_active &= ~(1 << BLANK_TIMER);
if (vtdata.fgconsole->vtd->vc_mode == KD_GRAPHICS)
return;
if (vtdata.blanked) {
timer_table[BLANK_TIMER].expires = 0;
timer_active |= 1 << BLANK_TIMER;
} else if (vtdata.screen.blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
timer_active |= 1 << BLANK_TIMER;
}
}
 
static void vt_blankscreen (void);
 
void vt_do_unblankscreen (void)
{
if (!vtdata.blanked)
return;
 
if (!vt_allocated (vtdata.fgconsole)) {
/* impossible... */
printk ("unblank_screen: tty %d not allocated ??\n", vtdata.fgconsole->num);
return;
}
 
timer_table[BLANK_TIMER].fn = vt_blankscreen;
 
if (vtdata.screen.blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
timer_active |= 1 << BLANK_TIMER;
}
 
vtdata.blanked = NULL;
 
vcd_unblankscreen ();
}
 
/*
* for panic.c
*/
void do_unblank_screen (void)
{
vt_do_unblankscreen ();
}
 
void vt_do_blankscreen (int nopowersave)
{
if (vtdata.blanked)
return;
 
timer_active &= ~(1 << BLANK_TIMER);
timer_table[BLANK_TIMER].fn = do_unblank_screen;
 
vtdata.blanked = vtdata.fgconsole;
 
vcd_blankscreen (nopowersave);
}
 
static void vt_blankscreen (void)
{
vt_do_blankscreen (0);
}
 
static int vt_open (struct tty_struct *tty, struct file *filp)
{
struct vt *vt;
unsigned int idx;
int i;
 
idx = MINOR(tty->device) - tty->driver.minor_start;
 
vt = vt_con_data + idx;
 
if ((i = vt_allocate (vt)) < 0)
return i;
 
tty->driver_data = vt;
 
if (!tty->winsize.ws_row && !tty->winsize.ws_col) {
tty->winsize.ws_row = vtdata.numrows;
tty->winsize.ws_col = vtdata.numcolumns;
}
return 0;
}
 
static void vt_vtd_flush_chars (const struct vt *vt, struct vt_struct *vtd)
{
unsigned long flags;
 
save_flags (flags);
if (vtd->xmitting)
return;
 
vtd->xmitting = 1;
 
while (1) {
int c;
 
cli ();
c = MIN(vtd->xmit_cnt, CON_XMIT_SIZE - vtd->xmit_out);
 
if (c <= 0)
break;
 
restore_flags (flags);
 
c = vcd_write (vt, 0, vtd->xmit_buf + vtd->xmit_out, c);
 
cli ();
 
if (c <= 0)
break;
vtd->xmit_out = (vtd->xmit_out + c) & (CON_XMIT_SIZE - 1);
vtd->xmit_cnt -= c;
}
 
vtd->xmitting = 0;
restore_flags (flags);
if (*vt->tty)
wake_up_interruptible (&(*vt->tty)->write_wait);
}
 
static int vt_write (struct tty_struct *tty, int from_user,
const unsigned char *buf, int count)
{
static unsigned long last_error = 0;
const struct vt *vt = (struct vt *)tty->driver_data;
 
if (vt_allocated (vt)) {
if (vt->vtd->xmit_cnt)
vt_vtd_flush_chars (vt, vt->vtd);
return vcd_write (vt, from_user, buf, count);
}
 
if (jiffies > last_error + 10*HZ) {
printk ("vt_write: tty %d not allocated\n", vt->num);
last_error = jiffies;
}
return 0;
}
 
static void vt_put_char (struct tty_struct *tty, unsigned char ch)
{
const struct vt *vt = (struct vt *)tty->driver_data;
unsigned long flags;
 
if (!vt_allocated (vt))
return;
 
save_flags_cli (flags);
if (vt->vtd->xmit_cnt < CON_XMIT_SIZE - 1) {
vt->vtd->xmit_buf[vt->vtd->xmit_in++] = ch;
vt->vtd->xmit_in &= CON_XMIT_SIZE - 1;
vt->vtd->xmit_cnt ++;
}
restore_flags (flags);
}
 
static void vt_flush_chars (struct tty_struct *tty)
{
const struct vt *vt = (struct vt *)tty->driver_data;
 
if (!vt_allocated (vt) || !vt->vtd->xmit_cnt || tty->stopped)
return;
 
vt_vtd_flush_chars (vt, vt->vtd);
}
 
static int vt_write_room (struct tty_struct *tty)
{
const struct vt *vt = (struct vt *)tty->driver_data;
int r;
 
if (!vt_allocated (vt))
return 0;
 
r = CON_XMIT_SIZE - vt->vtd->xmit_cnt - 8; /* allow 8 char overflow */
if (r < 0)
return 0;
else
return r;
}
 
static int vt_chars_in_buffer (struct tty_struct *tty)
{
const struct vt *vt = (struct vt *)tty->driver_data;
 
if (!vt_allocated (vt))
return CON_XMIT_SIZE;
return vt->vtd->xmit_cnt;
}
#if 0
static void vt_flush_buffer (struct tty_struct *tty)
{
const struct vt *vt = (struct vt *)tty->driver_data;
 
if (!vt_allocated (vt))
return;
 
cli ();
vt->vtd->xmit_cnt = vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
sti ();
}
#endif
static int vt_ioctl (struct tty_struct *tty, struct file *filp,
unsigned int cmd, unsigned long arg)
{
struct vt *vt = tty->driver_data;
int perm, i;
 
if (!vt_allocated (vt)) /* impossible ? */
return -ENOIOCTLCMD;
/*
* To have permissions to do most of the vt ioctls, we either
* have to be the owner of the tty, or super-user.
*/
perm = 0;
if (current->tty == tty || suser ())
perm = 1;
 
#define PERM if (!perm) return -EPERM
 
switch (cmd) {
case KDGETMODE:
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
if (!i)
put_user (vt->vtd->vc_mode, (unsigned long *)arg);
return i;
 
case KDSETMODE:
PERM;
return vt_kdsetmode (vt, arg);
 
case VT_GETMODE:
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (struct vt_mode));
if (i)
return i;
memcpy_tofs ((void *)arg, &vt->vtd->vt_mode, sizeof (struct vt_mode));
return 0;
 
case VT_SETMODE: {
struct vt_mode vtmode;
 
PERM;
i = verify_area (VERIFY_READ, (void *)arg, sizeof (struct vt_mode));
if (i)
return i;
memcpy_fromfs (&vtmode, (void *)arg, sizeof (struct vt_mode));
return vt_setmode (vt, &vtmode);
}
 
case VT_GETSTATE: {
/*
* Returns global VT state. Note that VT 0 is always open, since
* it's an alias for the current VT, and people can't use it here.
* We cannot return state for more than 16 VTs, since v_state is short.
*/
struct vt_stat *vtstat = (struct vt_stat *)arg;
unsigned short state = 1, mask = 2;
 
i = verify_area (VERIFY_WRITE, vtstat, sizeof (struct vt_stat));
if (i)
return i;
 
for (i = 0; i < MAX_NR_CONSOLES && mask; ++i, mask <<= 1)
if (VT_IS_IN_USE(i))
state |= mask;
 
put_user (vtdata.fgconsole->num, &vtstat->v_active);
put_user (state, &vtstat->v_state);
return 0;
}
 
case VT_OPENQRY:
/*
* Returns the first available (non-open) console
*/
i = verify_area (VERIFY_WRITE, (void *)arg, sizeof (unsigned long));
if (i)
return i;
 
for (i = 0; i < MAX_NR_CONSOLES; i++)
if (!VT_IS_IN_USE(i))
break;
if (i < MAX_NR_CONSOLES)
put_user (i + 1, (unsigned long *)arg);
else
put_user (-1, (unsigned long *)arg);
return 0;
 
case VT_ACTIVATE:
PERM;
/*
* VT activate will cause us to switch to vt #num with num >= 1
* (switches to vt0, our console, are not allowed, just to preserve
* sanity)
*/
if (arg == 0 || arg > MAX_NR_CONSOLES)
return -ENXIO;
vt = vt_con_data + (arg - 1);
i = vt_allocate (vt);
if (i)
return i;
 
vt_changeconsole (vt);
return 0;
 
case VT_WAITACTIVE:
PERM;
/*
* wait until the specified VT has been activated
*/
if (arg == 0 || arg > MAX_NR_CONSOLES)
return -ENXIO;
vt = vt_con_data + (arg - 1);
while (vt != vtdata.fgconsole) {
if (vt_waitonactivate () < 0)
return -EINTR;
}
return 0;
 
case VT_RELDISP:
PERM;
return vt_reldisp (vt, arg);
 
case VT_RESIZE: {
struct vt_sizes *vtsizes = (struct vt_sizes *)arg;
ushort ll, cc;
 
PERM;
 
i = verify_area (VERIFY_READ, (void *)vtsizes, sizeof (struct vt_sizes));
if (i)
return i;
ll = get_user (&vtsizes->v_rows);
cc = get_user (&vtsizes->v_cols);
return vt_resize (cc, ll);
}
 
case KIOCSOUND:
PERM;
vt_mksound (arg, 72, 5);
return 0;
 
case KDMKTONE: {
unsigned int freq = get_user ((unsigned long *)arg);
unsigned int vol = get_user ((unsigned long *)(arg + 4));
unsigned int ticks = get_user ((unsigned long *)(arg + 8));
 
PERM;
/*
* Generate the tone for the apropriate number of ticks.
* If time is zero, turn off sound ourselves.
*/
vt_mksound (freq, vol, ticks);
return 0;
}
 
case VT_DISALLOCATE:
if (arg > MAX_NR_CONSOLES)
return -ENXIO;
return vt_deallocate (arg);
 
case VT_GETSCRINFO:
/*
* Get screen dimentions
*/
i = verify_area (VERIFY_WRITE, (void *)arg, 5 * sizeof (unsigned long));
if (!i) {
put_user (0, (unsigned long *)arg);
put_user (vtdata.numcolumns * 8, (unsigned long *)(arg + 4));
put_user (vtdata.numrows * 8, (unsigned long *)(arg + 8));
put_user (vtdata.screen.bitsperpix, (unsigned long *)(arg + 12)); /* bpp */
#ifndef HAS_VIDC20
put_user (4, (unsigned long *)(arg + 16)); /* depth */
#else
put_user (8, (unsigned long *)(arg + 16)); /* depth */
#endif
}
return i;
 
case VT_LOCKSWITCH:
if (!suser())
return -EPERM;
vt_dont_switch = 1;
return 0;
 
case VT_UNLOCKSWITCH:
if (!suser())
return -EPERM;
vt_dont_switch = 0;
return 0;
 
case KDMAPDISP:
case KDUNMAPDISP:
case KDSKBMODE:
case KDSKBMETA:
case KDSETKEYCODE:
case KDSKBENT:
case KDSKBSENT:
case KDSKBDIACR:
case KDSKBLED:
case KDSETLED:
case KDSIGACCEPT:
PERM;
 
case KDGKBTYPE:
case KDADDIO:
case KDDELIO:
case KDENABIO:
case KDDISABIO:
case KDGKBMODE:
case KDGKBMETA:
case KDGETKEYCODE:
case KDGKBENT:
case KDGKBSENT:
case KDGKBDIACR:
case KDGKBLED:
case KDGETLED:
return kbd_ioctl (vt, cmd, arg);
 
case VT_SETPALETTE:
case PIO_FONT:
case PIO_SCRNMAP:
case PIO_UNISCRNMAP:
case PIO_UNIMAPCLR:
case PIO_UNIMAP:
PERM;
 
case VT_GETPALETTE:
case GIO_FONT:
case GIO_SCRNMAP:
case GIO_UNISCRNMAP:
case GIO_UNIMAP:
return vcd_ioctl (vt, cmd, arg);
}
return -ENOIOCTLCMD;
}
/*
* Turn the Scroll-Lock LED on when the tty is stopped
*/
static void vt_stop (struct tty_struct *tty)
{
if (tty) {
const struct vt *vt = (struct vt *)tty->driver_data;
if (vt_allocated (vt)) {
set_vc_kbd_led (vt->kbd, VC_SCROLLOCK);
set_leds ();
}
}
}
 
/*
* Turn the Scroll-Lock LED off when the tty is started
*/
static void vt_start (struct tty_struct *tty)
{
if (tty) {
const struct vt *vt = (struct vt *)tty->driver_data;
if (vt_allocated (vt)) {
clr_vc_kbd_led (vt->kbd, VC_SCROLLOCK);
set_leds ();
}
if (vt->vtd->xmit_cnt)
vt_vtd_flush_chars (vt, vt->vtd);
}
}
 
/*
* vt_throttle and vt_unthrottle are only used for
* paste_selection(), which has to stuff in a large number
* of characters...
*/
static void vt_throttle (struct tty_struct *tty)
{
}
 
static void vt_unthrottle (struct tty_struct *tty)
{
const struct vt *vt = (struct vt *)tty->driver_data;
 
if (vt_allocated (vt))
wake_up_interruptible (&vt->vtd->paste_wait);
}
 
/*
* This is the console switching bottom half handler.
*
* Doing console switching in a bottom half handler allows
* us to do ths switches asynchronously (needed when we want
* to switch due to a keyboard interrupt), while still giving
* us the option to easily disable it to avoid races when we
* need to write to the console.
*/
static void console_bh(void)
{
if (want_console) {
if (want_console != vtdata.fgconsole) {
vt_changeconsole(want_console);
/* we only changed when the console had already
been allocated - a new console is not created
in an interrupt routine */
}
want_console = NULL;
}
if (do_poke_blanked_console) { /* do not unblank for a LED change */
do_poke_blanked_console = 0;
vt_pokeblankedconsole();
}
}
 
/*
* We don't have kmalloc setup yet. We just want to get enough up so that printk
* can work.
*
* Basically, we setup the VT structures for tty1.
*/
unsigned long vt_pre_init (unsigned long kmem)
{
struct vt *vt = vt_con_data;
 
memset (vt_con_data, 0, sizeof (vt_con_data));
 
vtdata.numcolumns = ORIG_VIDEO_COLS;
vtdata.numrows = ORIG_VIDEO_LINES;
vtdata.blanked = NULL;
vtdata.fgconsole = vt_con_data;
vtdata.screen.blankinterval = 10*60*HZ;
vtdata.select.vt = NULL;
vtdata.select.start = -1;
vtdata.select.end = 0;
vtdata.select.buffer = NULL;
 
vt->vcd = (struct con_struct *)kmem; kmem += sizeof (struct con_struct);
vt->kbd = (struct kbd_struct *)kmem; kmem += sizeof (struct kbd_struct);
vt->vtd = (struct vt_struct *)kmem; kmem += sizeof (struct vt_struct);
vt->vtd->xmit_buf = (unsigned char *)kmem; kmem += CON_XMIT_SIZE;
vt->vtd->xmitting = vt->vtd->xmit_cnt =
vt->vtd->xmit_out = vt->vtd->xmit_in = 0;
vt->tty = &vt_table[0];
vt->num = 1;
 
/*
* vcd_init is called inside vcd_pre_init
*/
kbd_struct_init (vt, 0);
vt_reset (vt);
vt->allocinit = 1;
 
kmem = vcd_pre_init (kmem, vt);
 
init_bh(CONSOLE_BH, console_bh);
 
return kmem;
}
 
/*
* This is the post initialisation. We have kmalloc setup so we can use it...
*/
void vt_post_init (void)
{
int i;
 
memset (&vt_driver, 0, sizeof (struct tty_driver));
vt_driver.magic = TTY_DRIVER_MAGIC;
vt_driver.name = "tty";
vt_driver.name_base = 1;
vt_driver.major = TTY_MAJOR;
vt_driver.minor_start = 1;
vt_driver.num = MAX_NR_CONSOLES;
vt_driver.type = TTY_DRIVER_TYPE_CONSOLE;
vt_driver.init_termios = tty_std_termios;
vt_driver.flags = TTY_DRIVER_REAL_RAW;
vt_driver.refcount = &vt_refcount;
vt_driver.table = vt_table;
vt_driver.termios = vt_termios;
vt_driver.termios_locked = vt_termios_locked;
vt_driver.open = vt_open;
vt_driver.write = vt_write;
vt_driver.put_char = vt_put_char;
vt_driver.flush_chars = vt_flush_chars;
vt_driver.write_room = vt_write_room;
vt_driver.chars_in_buffer = vt_chars_in_buffer;
#if 0
vt_driver.flush_buffer = vt_flush_buffer;
#endif
vt_driver.ioctl = vt_ioctl;
vt_driver.stop = vt_stop;
vt_driver.start = vt_start;
vt_driver.throttle = vt_throttle;
vt_driver.unthrottle = vt_unthrottle;
 
for (i = 1; i < MAX_NR_CONSOLES; i++) {
struct vt *vt = vt_con_data + i;
vt->tty = &vt_table[i];
vt->num = i + 1;
vt->allocinit = 0;
if (i < MIN_NR_CONSOLES)
vt_allocate (vt);
}
 
if (tty_register_driver (&vt_driver))
panic ("Couldn't register console driver");
 
timer_table[BLANK_TIMER].fn = vt_blankscreen;
if (vtdata.screen.blankinterval) {
timer_table[BLANK_TIMER].expires = jiffies + vtdata.screen.blankinterval;
timer_active |= 1 << BLANK_TIMER;
} else
timer_table[BLANK_TIMER].expires = 0;
}
/selection.h
0,0 → 1,23
/*
* selection.h
*
* Interface between console.c, tty_io.c, vt.c, vc_screen.c and selection.c
*/
 
extern void clear_selection (void);
extern int set_selection (const unsigned long arg, struct tty_struct *tty);
extern int paste_selection (struct tty_struct *tty);
extern int sel_loadlut (const unsigned long arg);
extern int mouse_reporting (void);
extern void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry);
 
extern void vt_do_unblankscreen (void);
extern unsigned long *screen_pos (const struct vt *const vt, unsigned int offset);
extern unsigned long screen_word (const struct vt *const vt, unsigned int offset);
extern int scrw2glyph (unsigned long scr_word);
extern void complement_pos (const struct vt * const vt, unsigned int offset);
extern void invert_screen (const struct vt *const vt, unsigned int offset, unsigned int count);
 
extern void getconsxy (const struct vt *const vt, char *p);
extern void putconsxy (const struct vt *const vt, char *p);
extern void update_scrmem (const struct vt *const vt, int offset, int length);
/misc.c
0,0 → 1,214
/*
* linux/arch/arm/drivers/char/mouse.c
*
* Generic misc open routine by Johan Myreen
*
* Based on code from Linus
*
* Teemu Rantanen's Microsoft Busmouse support and Derrick Cole's
* changes incorporated into 0.97pl4
* by Peter Cervasio (pete%q106fm.uucp@wupost.wustl.edu) (08SEP92)
* See busmouse.c for particulars.
*
* Made things a lot mode modular - easy to compile in just one or two
* of the misc drivers, as they are now completely independent. Linus.
*
* Support for loadable modules. 8-Sep-95 Philip Blundell <pjb27@cam.ac.uk>
*
* Fixed a failing symbol register to free the device registration
* Alan Cox <alan@lxorguk.ukuu.org.uk> 21-Jan-96
*
* Dynamic minors and /proc/mice by Alessandro Rubini. 26-Mar-96
*
* Renamed to misc and miscdevice to be more accurate. Alan Cox 26-Mar-96
*
* Handling of mouse minor numbers for kerneld:
* Idea by Jacques Gelinas <jack@solucorp.qc.ca>,
* adapted by Bjorn Ekwall <bj0rn@blox.se>
* corrected by Alan Cox <alan@lxorguk.ukuu.org.uk>
*/
 
#include <linux/config.h>
#include <linux/module.h>
 
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/miscdevice.h>
#include <linux/kernel.h>
#include <linux/major.h>
#include <linux/malloc.h>
#include <linux/proc_fs.h>
#include <linux/stat.h>
 
#include <linux/tty.h> /* needed by selection.h */
#include "vt_kern.h"
#include "selection.h" /* export its symbols */
#ifdef CONFIG_KERNELD
#include <linux/kerneld.h>
#endif
 
#include "mouse.h"
 
/*
* Head entry for the doubly linked miscdevice list
*/
static struct miscdevice misc_list = { 0, "head", NULL, &misc_list, &misc_list };
 
/*
* Assigned numbers, used for dynamic minors
*/
#define DYNAMIC_MINORS 64 /* like dynamic majors used to do */
static unsigned char misc_minors[DYNAMIC_MINORS / 8];
 
#ifndef MODULE
extern void watchdog_init(void);
extern int arch_mouse_init(void);
extern int con_get_info(int *mode, int *shift, int *col, int *row,
struct tty_struct **tty);
 
#ifdef CONFIG_PROC_FS
static int proc_misc_read(char *buf, char **start, off_t offset, int len, int unused)
{
struct miscdevice *p;
 
len=0;
for (p = misc_list.next; p != &misc_list; p = p->next)
len += sprintf(buf+len, "%3i %s\n",p->minor, p->name ?: "");
return len;
}
 
#endif /* PROC_FS */
#endif /* !MODULE */
 
static int misc_open(struct inode * inode, struct file * file)
{
int minor = MINOR(inode->i_rdev);
struct miscdevice *c = misc_list.next;
file->f_op = NULL;
 
while ((c != &misc_list) && (c->minor != minor))
c = c->next;
if (c == &misc_list) {
#ifdef CONFIG_KERNELD
char modname[20];
sprintf(modname, "char-major-%d-%d", MISC_MAJOR, minor);
request_module(modname);
c = misc_list.next;
while ((c != &misc_list) && (c->minor != minor))
c = c->next;
if (c == &misc_list)
#endif
return -ENODEV;
}
 
if ((file->f_op = c->fops))
return file->f_op->open(inode,file);
else
return -ENODEV;
}
 
static struct file_operations misc_fops = {
NULL, /* seek */
NULL, /* read */
NULL, /* write */
NULL, /* readdir */
NULL, /* select */
NULL, /* ioctl */
NULL, /* mmap */
misc_open,
NULL /* release */
};
 
int misc_register(struct miscdevice * misc)
{
if (misc->next || misc->prev)
return -EBUSY;
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = DYNAMIC_MINORS;
while (--i >= 0)
if ( (misc_minors[i>>3] & (1 << (i&7))) == 0)
break;
if (i<0) return -EBUSY;
misc->minor = i;
}
if (misc->minor < DYNAMIC_MINORS)
misc_minors[misc->minor >> 3] |= 1 << (misc->minor & 7);
MOD_INC_USE_COUNT;
misc->next = &misc_list;
misc->prev = misc_list.prev;
misc->prev->next = misc;
misc->next->prev = misc;
return 0;
}
 
int misc_deregister(struct miscdevice * misc)
{
int i = misc->minor;
if (!misc->next || !misc->prev)
return -EINVAL;
MOD_DEC_USE_COUNT;
misc->prev->next = misc->next;
misc->next->prev = misc->prev;
misc->next = NULL;
misc->prev = NULL;
if (i < DYNAMIC_MINORS && i>0) {
misc_minors[i>>3] &= ~(1 << (misc->minor & 7));
}
return 0;
}
 
#ifdef MODULE
 
#define misc_init init_module
 
void cleanup_module(void)
{
unregister_chrdev(MISC_MAJOR, "misc");
}
 
#endif
 
static struct symbol_table misc_syms = {
/* Should this be surrounded with "#ifdef CONFIG_MODULES" ? */
#include <linux/symtab_begin.h>
X(misc_register),
X(misc_deregister),
#ifndef MODULE
X(set_selection), /* used by the kmouse module, can only */
X(paste_selection), /* be exported if misc.c is in linked in */
X(con_get_info),
#endif
#include <linux/symtab_end.h>
};
 
int misc_init(void)
{
#ifndef MODULE
#ifdef CONFIG_PROC_FS
proc_register_dynamic(&proc_root, &(struct proc_dir_entry) {
0, 4, "misc",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL /* ops -- default to array */,
&proc_misc_read /* get_info */,
});
#endif /* PROC_FS */
#ifdef CONFIG_MOUSE
misc_mouse_init();
#endif
#ifdef CONFIG_SOFT_WATCHDOG
watchdog_init();
#endif
#endif /* !MODULE */
if (register_chrdev(MISC_MAJOR,"misc",&misc_fops)) {
printk("unable to get major %d for misc devices\n",
MISC_MAJOR);
return -EIO;
}
 
if(register_symtab(&misc_syms)!=0)
{
unregister_chrdev(MISC_MAJOR, "misc");
return -EIO;
}
return 0;
}
/console-dummy.c
0,0 → 1,290
/*
* linux/arch/arm/drivers/char/console-dummy.c
*
* Modifications (C) 1996 Russell King
*/
 
/*
* This module exports the console io functions:
*
* 'int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)'
* 'unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)'
* 'void vcd_disallocate (struct vt *vt)'
* 'int vcd_resize (unsigned long lines, unsigned long cols)'
* 'void vcd_blankscreen (int nopowersave)'
* 'void vcd_unblankscreen (void)'
* 'void vcd_savestate (const struct vt *vt, int blanked)'
* 'void vcd_restorestate (const struct vt *vt)'
* 'void vcd_setup_graphics (const struct vt *vt)'
* 'int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)'
* 'int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)'
*
*
* 'int vc_allocate(unsigned int console)'
* 'int vc_cons_allocated (unsigned int console)'
* 'int vc_resize(unsigned long lines,unsigned long cols)'
* 'void vc_disallocate(unsigned int currcons)'
*
* 'unsigned long con_init(unsigned long)'
* S 'int con_open(struct tty_struct *tty,struct file *filp)'
* S 'void con_write(struct tty_struct *tty)'
* S 'void console_print(const char *b)'
* 'void update_screen(int new_console)'
*
* 'void blank_screen(void)'
* 'void unblank_screen(void)'
* 'void scrollback(int lines)' *
* 'void scrollfront(int lines)' *
* 'int do_screendump(int arg)'
*
* 'int con_get_font(char *)'
* 'int con_set_font(char *)'
* 'int con_get_trans(char *)'
* 'int con_set_trans(char *)'
*
* 'int mouse_reporting(void)'
*/
 
#include <linux/sched.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/kernel.h>
#include <linux/errno.h>
#include <linux/kd.h>
#include <linux/major.h>
#include <linux/mm.h>
#include <linux/malloc.h>
 
#include <asm/segment.h>
#include <asm/irq.h>
 
#include "kbd_kern.h"
#include "consolemap.h"
#include "vt_kern.h"
#include "selection.h"
 
extern void register_console(void (*proc)(const char *));
static int printable; /* Is console ready for printing? */
 
#define SERIAL_ECHO_PORT 0x2f8
#define SERIAL_ECHO_DIVISOR 3
 
#include "serialecho.c"
 
/*
* functions to handle /dev/fb
*/
int con_fb_read(char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_write(const char *buf, unsigned long pos, int count)
{
return -EIO;
}
 
int con_fb_mmap(unsigned long vma_start, unsigned long vma_offset,
unsigned long vma_end, pgprot_t prot)
{
return -EINVAL;
}
 
void no_scroll (char *str, int *ints)
{
}
 
void mouse_report (struct tty_struct *tty, int butt, int mrx, int mry)
{
}
 
int mouse_reporting (void)
{
return 0;
}
 
static inline unsigned long *bufferpos (const struct vt * const vt, int offset)
{
return NULL;
}
 
void invert_screen (const struct vt * const vt, unsigned int offset, unsigned int count)
{
}
 
void complement_pos (const struct vt * const vt, unsigned int offset)
{
}
 
unsigned long screen_word (const struct vt * const vt, unsigned int offset)
{
return 0;
}
 
int scrw2glyph (unsigned long scr_word)
{
return 0;
}
 
unsigned long *screen_pos (const struct vt * const vt, unsigned int offset)
{
return NULL;
}
 
void getconsxy (const struct vt * const vt, char *p)
{
p[0] = p[1] = 0;
}
 
void putconsxy (const struct vt * const vt, char *p)
{
}
 
void console_print(const char *b)
{
static int printing = 0;
 
if (!printable || printing)
return; /* console not yet initialized */
 
printing = 1;
serial_echo_print (b);
printing = 0;
}
 
void update_scrmem (const struct vt * const vt, int start, int length)
{
}
 
void set_scrmem (const struct vt * const vt, long offset)
{
}
 
int con_set_font (char *arg)
{
return -EINVAL;
}
 
int con_get_font (char *arg)
{
return -EINVAL;
}
 
void con_reset_palette (const struct vt * const vt)
{
}
 
void con_set_palette (const struct vt * const vt)
{
}
 
/* == arm specific console code ============================================================== */
 
int do_screendump(int arg)
{
return -EINVAL;
}
 
/*===============================================================================================*/
 
int vcd_init (struct vt *vt, int kmallocok, unsigned long *kmem)
{
return 0;
}
 
unsigned long vcd_pre_init (unsigned long kmem, struct vt *vt)
{
serial_echo_init (SERIAL_ECHO_PORT);
printable = 1;
 
printk ("Console: dummy console driver\n");
register_console (console_print);
return kmem;
}
 
void vcd_disallocate (struct vt *vt)
{
}
 
int vcd_resize(unsigned long lines, unsigned long cols)
{/* TODO */
return -ENOMEM;
}
 
void vcd_blankscreen(int nopowersave)
{
}
 
void vcd_unblankscreen (void)
{
}
 
void vcd_savestate (const struct vt *vt, int blanked)
{
}
 
void vcd_restorestate (const struct vt *vt)
{
}
 
void vcd_setup_graphics (const struct vt *vt)
{
}
 
static char vcd_buffer[128];
int vcd_write (const struct vt *vt, int from_user, const unsigned char *buf, int count)
{
int tmp = count;
while (tmp) {
int i;
 
i = tmp < 127 ? tmp : 127;
 
tmp -= i;
memcpy (vcd_buffer, buf, i);
buf += i;
 
vcd_buffer[i] = 0;
serial_echo_print(vcd_buffer);
}
return count;
}
 
int vcd_ioctl (const struct vt *vt, int cmd, unsigned long arg)
{
switch (cmd) {
case PIO_FONT:
case GIO_FONT:
case PIO_SCRNMAP:
case GIO_SCRNMAP:
case PIO_UNISCRNMAP:
case GIO_UNISCRNMAP:
case PIO_UNIMAPCLR:
case PIO_UNIMAP:
case GIO_UNIMAP:
return -EINVAL;
 
default:
return -ENOIOCTLCMD;
}
}
 
void console_map_init (void)
{
}
 
/*
* Report the current status of the vc. This is exported to modules (ARub)
*/
int con_get_info(int *mode, int *shift, int *col, int *row,
struct tty_struct **tty)
{
if (mode) *mode = 0;
if (shift) *shift = 0;
if (col) *col = 0;
if (row) *row = 0;
if (tty) *tty = NULL;
return 0;
}
/mouse_rpc.c
0,0 → 1,41
#include <linux/module.h>
#include <linux/ptrace.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
 
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/io.h>
 
#include "mouse.h"
 
static short old_x, old_y, old_b;
 
void mouse_rpc_irq (int irq, void *dev_id, struct pt_regs *regs)
{
short x, y, b, dx, dy, db;
 
x = (short)inl(IOMD_MOUSEX);
y = (short)inl(IOMD_MOUSEY);
b = (inl (0x800C4000) >> 4) & 7;
 
dx = x - old_x;
old_x = x;
dy = y - old_y;
old_y = y;
db = b ^ old_b;
old_b = b;
 
if (dx || dy)
add_mouse_movement(dx, dy);
if (db)
add_mouse_buttonchange(7, b);
}
 
void mouse_rpc_init(void)
{
old_x = (short)inl(IOMD_MOUSEX);
old_y = (short)inl(IOMD_MOUSEY);
old_b = (inl (0x800C4000) >> 4) & 7;
request_irq (IRQ_VSYNCPULSE, mouse_rpc_irq, SA_SHIRQ, "mouse", NULL);
}
/mouse.h
0,0 → 1,13
/*
* linux/arch/arm/drivers/char/mouse.h
*
* Prototypes for mouse device driver
*/
#ifndef MOUSE_H
#define MOUSE_H
 
extern void add_mouse_movement (int dx, int dy);
extern int add_mouse_buttonchange (int set, int value);
extern int misc_mouse_init (void);
 
#endif

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.