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 |