From b9a923b66abdc8f16839bc324d4927aa4d5ea1f6 Mon Sep 17 00:00:00 2001 From: Oliver Schinagl Date: Wed, 16 Mar 2011 14:07:34 +0000 Subject: Keyoard, serial, ps2 and i2c drivers for the W90N745 --- uClinux-2.4.20-uc1/drivers/char/Config.in | 35 + uClinux-2.4.20-uc1/drivers/char/Makefile | 14 +- uClinux-2.4.20-uc1/drivers/char/keyboard.c | 33 + uClinux-2.4.20-uc1/drivers/char/tty_io.c | 5 +- uClinux-2.4.20-uc1/drivers/char/vdd.c | 472 ++ uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c | 600 +++ uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h | 123 + uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c | 253 ++ uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h | 57 + uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c | 809 ++++ uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h | 115 + uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c | 4650 +++++++++++++++++++ uClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c | 4719 ++++++++++++++++++++ uClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c | 4668 +++++++++++++++++++ uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c | 4669 +++++++++++++++++++ uClinux-2.4.20-uc1/drivers/char/w90n745_usi.c | 331 ++ uClinux-2.4.20-uc1/drivers/char/w90n745_usi.h | 73 + uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c | 374 ++ uClinux-2.4.20-uc1/drivers/char/winbond_keymap.map | 357 ++ .../include/asm-armnommu/arch-W90N745/keyboard.h | 73 + 20 files changed, 22428 insertions(+), 2 deletions(-) create mode 100644 uClinux-2.4.20-uc1/drivers/char/vdd.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c create mode 100644 uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h create mode 100644 uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c create mode 100644 uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h create mode 100644 uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c create mode 100644 uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_usi.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/w90n745_usi.h create mode 100644 uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c create mode 100755 uClinux-2.4.20-uc1/drivers/char/winbond_keymap.map create mode 100644 uClinux-2.4.20-uc1/include/asm-armnommu/arch-W90N745/keyboard.h diff --git a/uClinux-2.4.20-uc1/drivers/char/Config.in b/uClinux-2.4.20-uc1/drivers/char/Config.in index ad0fe88..82f7560 100644 --- a/uClinux-2.4.20-uc1/drivers/char/Config.in +++ b/uClinux-2.4.20-uc1/drivers/char/Config.in @@ -150,6 +150,41 @@ if [ "$CONFIG_CPU_S3C4530" = "y" ]; then fi fi +if [ "$CONFIG_BOARD_W90N745" = "y" ]; then + bool 'Vitural debug device support' CONFIG_VDD + bool 'Winbond W90N745 serial port support' CONFIG_SERIAL_WINBOND + if [ "$CONFIG_SERIAL_WINBOND" = "y" ]; then + bool ' Support for console on Winbond W90N745 serial port' CONFIG_SERIAL_WINBOND_CONSOLE + fi + bool 'Winbond W90N745 serial port 1' CONFIG_W90N7451_SERIAL + if [ "$CONFIG_W90N7451_SERIAL" = "y" ] && [ "$CONFIG_W90N7452_SERIAL" != "y" ] && [ "$CONFIG_W90N745_PS2" != "y" ]; then + bool 'Enable serial port 1 CTS/RTS pins (Please touch w90n745_uart_1.c if this setting changed)' CONFIG_UART1_CTSRTS + fi + + if [ "$CONFIG_UART1_CTSRTS" != "y" ] && [ "$CONFIG_W90N745_PS2" != "y" ]; then + bool 'Winbond W90N745 serial port 2' CONFIG_W90N7452_SERIAL + fi + if [ "$CONFIG_SOUND_W90N745" != "y" ]; then + bool 'Winbond W90N745 serial port 3' CONFIG_W90N7453_SERIAL + fi + if [ "$CONFIG_UART1_CTSRTS" != "y" ] && [ "$CONFIG_W90N7452_SERIAL" != "y" ]; then + bool 'Winbond W90N745 ps/2 port support (Virtual terminal needs to be enabled as well)' CONFIG_W90N745_PS2 + fi +fi + +if [ "$CONFIG_ETH_W90N745" != "y" ]; then + bool 'Winbond W90N745 keypad' CONFIG_W90N745_KEYPAD +fi + +if [ "$CONFIG_BOARD_W90N745" = "y" ]; then + if [ "$CONFIG_WINBOND_USI" != "y" ]; then + bool 'Winbond W90N745 I2C module' CONFIG_WINBOND_I2C + fi + if [ "$CONFIG_WINBOND_I2C" != "y" ]; then + bool 'Winbond W90N745 USI' CONFIG_WINBOND_USI + fi +fi + if [ "$CONFIG_ARCH_DSC21" = "y" ]; then bool 'DSC21 serial port support' CONFIG_SERIAL_DSC21 if [ "$CONFIG_SERIAL_DSC21" = "y" ]; then diff --git a/uClinux-2.4.20-uc1/drivers/char/Makefile b/uClinux-2.4.20-uc1/drivers/char/Makefile index b503e47..3244583 100644 --- a/uClinux-2.4.20-uc1/drivers/char/Makefile +++ b/uClinux-2.4.20-uc1/drivers/char/Makefile @@ -31,7 +31,7 @@ mod-subdirs := joystick ftape drm drm-4.0 pcmcia list-multi := -KEYMAP =defkeymap.o +KEYMAP =winbond_keymap.o KEYBD =pc_keyb.o CONSOLE =console.o SERIAL =serial.o @@ -218,6 +218,15 @@ endif # # uClinux drivers # +obj-$(CONFIG_VDD) += vdd.o +obj-$(CONFIG_SERIAL_WINBOND) += w90n745_uart.o +obj-$(CONFIG_W90N7451_SERIAL) += w90n745_uart_1.o +obj-$(CONFIG_W90N7452_SERIAL) += w90n745_uart_2.o +obj-$(CONFIG_W90N7453_SERIAL) += w90n745_uart_3.o +obj-$(CONFIG_W90N745_PS2) += w90n745_ps2.o +obj-$(CONFIG_W90N745_KEYPAD) += w90n745_keypad.o +obj-$(CONFIG_WINBOND_USI) += w90n745_usi.o +obj-$(CONFIG_WINBOND_I2C) += w90n745_i2c.o obj-$(CONFIG_68328_SERIAL) += 68328serial.o obj-$(CONFIG_SERIAL_DSC21) += serial_dsc21.o obj-$(CONFIG_SERIAL_ATMEL) += serial_atmel.o @@ -384,5 +393,8 @@ consolemap_deftbl.o: consolemap_deftbl.c $(TOPDIR)/include/linux/types.h defkeymap.c: defkeymap.map set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ +winbond_keymap.c: winbond_keymap.map + set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ + qtronixmap.c: qtronixmap.map set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@ diff --git a/uClinux-2.4.20-uc1/drivers/char/keyboard.c b/uClinux-2.4.20-uc1/drivers/char/keyboard.c index ac8795d..09242a3 100644 --- a/uClinux-2.4.20-uc1/drivers/char/keyboard.c +++ b/uClinux-2.4.20-uc1/drivers/char/keyboard.c @@ -21,6 +21,7 @@ * * 27-05-97: Added support for the Magic SysRq Key (Martin Mares) * 30-07-98: Dead keys redone, aeb@cwi.nl. + * 01-09-05: Winbond modifications by PC34 MCLi */ #include @@ -191,6 +192,12 @@ int getkeycode(unsigned int scancode) return kbd_getkeycode(scancode); } +#ifdef CONFIG_BOARD_W90N745 +extern unsigned char IsExt; +extern unsigned char gvLEDCode; +extern struct tty_struct *key_tty; +#endif + void handle_scancode(unsigned char scancode, int down) { unsigned char keycode; @@ -200,7 +207,12 @@ void handle_scancode(unsigned char scancode, int down) pm_access(pm_kbd); add_keyboard_randomness(scancode | up_flag); +#ifndef CONFIG_BOARD_W90N745 tty = ttytab? ttytab[fg_console]: NULL; +#else + tty = key_tty; +#endif + if (tty && (!tty->driver_data)) { /* * We touch the tty structure via the ttytab array @@ -282,7 +294,14 @@ void handle_scancode(unsigned char scancode, int down) ushort *key_map = key_maps[shift_final]; if (key_map != NULL) { +#ifndef CONFIG_BOARD_W90N745 keysym = key_map[keycode]; +#else + if(shift_state && !IsExt && (keycode==0x4A)) + keysym=0xF03F; + else + keysym = key_map[keycode]; +#endif type = KTYP(keysym); if (type >= 0xf0) { @@ -297,7 +316,13 @@ void handle_scancode(unsigned char scancode, int down) keysym = key_map[keycode]; } } +#ifndef CONFIG_BOARD_W90N745 (*key_handler[type])(keysym & 0xff, up_flag); +#else + if(keysym!=0xF303) + (*key_handler[type])(keysym & 0xff, up_flag); +#endif + if (type != KT_SLOCK) kbd->slockstate = 0; } else { @@ -907,11 +932,19 @@ static void kbd_bh(unsigned long dummy) { unsigned char leds = getleds(); +#ifndef CONFIG_BOARD_W90N745 if (leds != ledstate) { ledstate = leds; kbd_leds(leds); if (kbd_ledfunc) kbd_ledfunc(leds); } +#else + if (gvLEDCode != ledstate) { + ledstate = gvLEDCode; + kbd_leds(gvLEDCode); + if (kbd_ledfunc) kbd_ledfunc(gvLEDCode); + } +#endif } EXPORT_SYMBOL(keyboard_tasklet); diff --git a/uClinux-2.4.20-uc1/drivers/char/tty_io.c b/uClinux-2.4.20-uc1/drivers/char/tty_io.c index a9836ed..d773c6d 100644 --- a/uClinux-2.4.20-uc1/drivers/char/tty_io.c +++ b/uClinux-2.4.20-uc1/drivers/char/tty_io.c @@ -2289,11 +2289,14 @@ void __init console_init(void) samsung_console_init(); #endif #ifdef CONFIG_SERIAL_S3C3410_CONSOLE - s3c3410_console_init(); + qs3c3410_console_init(); #endif #ifdef CONFIG_SERIAL_S3C4530_CONSOLE s3c4530_console_init(); #endif +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE + winbond_console_init(); +#endif #ifdef CONFIG_SERIAL_TX3912_CONSOLE tx3912_console_init(); #endif diff --git a/uClinux-2.4.20-uc1/drivers/char/vdd.c b/uClinux-2.4.20-uc1/drivers/char/vdd.c new file mode 100644 index 0000000..ff6c6d9 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/vdd.c @@ -0,0 +1,472 @@ +/* + * linux/drivers/char/vdd.c + * + * Winbond uclinux virtual debug device. + * yyang1|pc32 030528 + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef _DEBUG +#define _DEBUG +#endif +#undef _DEBUG + +#ifndef EOF +#define EOF (-1) +#endif + +typedef unsigned char BYTE; +typedef unsigned int BOOL; +#define TRUE 1 +#define FALSE 0 + +#define MALLOC(x) kmalloc((x), GFP_KERNEL) +#define FREE kfree +#define MEMCPY memcpy //or use "copy_to_user" + +#define DEBUGBUF_SIZE (1024 * 2) + + +#define VDD_CLEAR 0x5901 //clear current queue +#define MAX_VDD_DEV 2 +#define VDD_MAJOR 99 + +typedef struct Queue +{ + void * queue; + size_t MaxSize; + size_t begin; + size_t end; + size_t usage; +} Queue_t; + +#ifdef CONFIG_DEVFS_FS +#include +static devfs_handle_t devfs_vdd_handle[MAX_VDD_DEV]; +static DECLARE_MUTEX(vdd_table_mutex); +#endif + +static Queue_t debug_queue = {NULL, DEBUGBUF_SIZE + 1, 0, 0, 0}; + +static int vdd_open(struct inode *inode, struct file *filep); +static int vdd_release(struct inode *inode, struct file *filep); +static ssize_t vdd_read(struct file *filp, char * buf, size_t count, loff_t *f_pos); +static ssize_t vdd_write(struct file *filp, const char * buf, size_t count, loff_t *f_pos); +static loff_t vdd_llseek (struct file *, loff_t, int); +static int vdd_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg); + + +static struct file_operations vdd_ops = +{ + open: vdd_open, + read: vdd_read, + write: vdd_write, + llseek: vdd_llseek, + release: vdd_release, + ioctl: vdd_ioctl, +}; + +//----------------------------queue funcs----------------------------------// +#define _Capacity() (debug_queue.MaxSize - 1) + +#define _IsEmpty() (debug_queue.begin == debug_queue.end) +#define _IsFull() (((debug_queue.end + 1) % debug_queue.MaxSize == debug_queue.begin) ? TRUE : FALSE) +#define _Free() (_Capacity() - _Size()) + +#define _Begin() (debug_queue.begin) +#define _End() (debug_queue.end) +#define _nIterator(x) ((_Begin() + (x)) % debug_queue.MaxSize) + +#define _First() (((BYTE *)debug_queue.queue)[(debug_queue.begin + 1) % debug_queue.MaxSize]) +#define _Last() (((BYTE *)debug_queue.queue)[debug_queue.end]) + +#define _Usage() (debug_queue.usage) + +static size_t _Size() +{ + long diff_ = (long)debug_queue.end - (long)debug_queue.begin; + return (diff_ >= 0) ? diff_ : (debug_queue.MaxSize + diff_); +} + +static size_t _Distance(size_t begin, size_t end) +{ + long diff_ = (long)end - (long)begin; + return (diff_ >= 0) ? diff_ : (debug_queue.MaxSize + diff_); +} + +static BYTE _nElement(size_t index) // index = 0 --> *(begin + 1) +{ +// ASSERT((index >= 0) && (index < _Size())); + return ((BYTE *)debug_queue.queue)[_nIterator(index + 1)]; +} + +static void _Delete() +{ + if (_IsEmpty()) + return; + + debug_queue.begin = (debug_queue.begin + 1) % debug_queue.MaxSize; +} + +static void _Add(BYTE x) +{ + if (_IsFull()) + _Delete(); + + debug_queue.end = (debug_queue.end + 1) % debug_queue.MaxSize; + ((BYTE *)debug_queue.queue)[debug_queue.end] = x; +} + +//random and memcpy to optimize.. +static size_t N_copy(void *buf, size_t begin, size_t end) +{ + void * src_, *dst_ = buf; + size_t copy_num = 0; + +// ASSERT(buf && (begin < debug_queue.MaxSize) && (end < debug_queue.MaxSize)); + + if(end == begin) + return 0; + + if(end < begin) + { + size_t split_diff = _Capacity() - begin; + if(split_diff) + { + (BYTE *)src_ = (BYTE *)debug_queue.queue + (begin + 1) % _Capacity(); + MEMCPY(dst_, src_, split_diff); + copy_num += split_diff; + (BYTE *)dst_ += copy_num; + } + + src_ = debug_queue.queue; + MEMCPY(dst_, src_, end + 1); + copy_num += end + 1; + } + else // greater than + { + copy_num = end - begin; + (BYTE *)src_ = (BYTE *)debug_queue.queue + begin + 1; + MEMCPY(dst_, src_, copy_num); + } + + return copy_num; +} + +static void N_Delete(size_t n) +{ + if (_Size() < n) + { + debug_queue.begin = debug_queue.end; + return; + } + + debug_queue.begin = (debug_queue.begin + n) % debug_queue.MaxSize; +} + +static void N_Add(const void *buf, size_t count) +{ + size_t freed_; + size_t valid_ = (count > _Capacity()) ? _Capacity() : count; + void * src_, *dst_; + src_ = (BYTE *)buf + count - valid_; + dst_ = (BYTE *)debug_queue.queue + (debug_queue.end + 1) % debug_queue.MaxSize; + +#ifdef _DEBUG + printk("***N_Add(buf = 0x%x, count = %d).\n", buf, count); +#endif + + freed_ = _Free(); + if (freed_ < valid_) + { +#ifdef _DEBUG + printk("Free: %d, valid %d.\n", _Free(), valid_); +#endif + + N_Delete(valid_ - freed_); + } + + if(debug_queue.end >= debug_queue.begin) + { + size_t split_diff = _Capacity() - debug_queue.end; + if(valid_ > split_diff) + { + MEMCPY(dst_, src_, split_diff); + + debug_queue.end = _Capacity(); + + (BYTE *)src_ += split_diff; + valid_ -= split_diff; + dst_ = debug_queue.queue; + } + } + + MEMCPY(dst_, src_, valid_); + debug_queue.end = ((BYTE *)dst_ - (BYTE *)debug_queue.queue) + valid_ - 1; +} + +//----------------------------driver funcs----------------------------------// +static int vdd_ioctl (struct inode * inode, struct file * filp, unsigned int cmd, unsigned long arg) +{ + + +#ifdef _DEBUG + printk("***vdd_ioctl: cmd = 0x%x, arg = 0x%x\n", cmd, arg); +#endif + + switch(cmd) + { + case VDD_CLEAR: + { + debug_queue.begin = debug_queue.end = 0; + +#ifdef _DEBUG + printk("***vdd_ioctl[VDD_CLEAR], now queue size = %d.\n", _Size()); +#endif + + break; + } + default: + return -EINVAL; + } + + return 0; +} + +static loff_t vdd_llseek (struct file *filp, loff_t off, int whence) +{ + long newpos; + +#ifdef _DEBUG + printk("*** 1 vdd_llseek off = %llu, whence = %d.\n", off, whence); +#endif + + switch(whence) + { + case 0: //begin + newpos = off; + break; + + case 1: //current + newpos = filp->f_pos + off; + break; + + case 2: //end + newpos = _Size() + off; + break; + + default: + return -EINVAL; + } + + if(newpos < 0) + return -EINVAL; + + filp->f_pos = newpos; + +#ifdef _DEBUG + printk("*** 2 vdd_llseek newpos = %d, f_pos = %llu, whence = %d.\n", newpos, filp->f_pos, whence); +#endif + + return newpos; +} + +static ssize_t vdd_read(struct file *filep, char * buf, size_t count, loff_t *f_pos) +{ + size_t read_num = 0; + size_t begin_, end_; + + if(!debug_queue.queue || !count) + return 0; + +#ifdef _DEBUG + printk("***before debug_read count = %d, buf = 0x%x, f_pos = 0x%x.\n", count, buf, filep->f_pos); +#endif + +#if 0 + + begin_ = (count >= _Size()) ? _Begin() : (_nIterator(_Size() - count)); + read_num = N_copy(buf, begin_, _End()); + +#else + + if(filep->f_pos >= _Size()) + { + //filep->f_pos = 0; //if end, then lseek to begin... + read_num = 0; + } + else + { + begin_ = _nIterator(filep->f_pos); + end_ = (count >= _Distance(begin_, _End())) ? _End() : (_nIterator(_Distance(_Begin(), begin_) + count)); + + _Usage()++; + read_num = N_copy(buf, begin_, end_); + _Usage()--; + +#ifdef _DEBUG + printk("***debug_reading begin_ = %d, end_ = %d, read_num = %d.\n", begin_, end_, read_num); +#endif + filep->f_pos += read_num; + } + +#endif + +#ifdef _DEBUG + printk("***after debug_read count = %d, buf = 0x%x, f_pos = 0x%x.\n", count, buf, filep->f_pos); +#endif + + return read_num; +} + +static ssize_t vdd_write(struct file *filep, const char * buf, size_t count, loff_t *f_pos) +{ + if(!debug_queue.queue) + return 0; + + _Usage()++; + N_Add(buf, count); + _Usage()--; + +#ifdef _DEBUG + printk("***debug_write count = %d, buf = 0x%x, f_pos = 0x%x.\n", count, buf, filep->f_pos); +#endif + + return count; +} + + + + +static int vdd_open(struct inode *inode, struct file *filep) +{ + unsigned int dev = MINOR(inode->i_rdev); + + if(dev >= MAX_VDD_DEV) + return -EFAULT; + + //030529? + if(_Usage()) + return -EFAULT; + + filep->f_op = &vdd_ops; + filep->f_pos = 0; + + MOD_INC_USE_COUNT; + +#ifdef _DEBUG + printk("open VDD[vdd%d] succeed!\n", dev); +#endif + + return 0; +} + +static int vdd_release(struct inode *inode, struct file *filep) +{ + MOD_DEC_USE_COUNT; + +#ifdef _DEBUG + printk("close VDD succeed!\n"); +#endif + return 0; +} +#ifdef CONFIG_DEVFS_FS +static void register_vdd_usr(void) +{ + int i; + + down(&vdd_table_mutex); + + MOD_INC_USE_COUNT; + + for (i=0; i< MAX_VDD_DEV; i++) + { + char name[8]; + sprintf(name, "vdd%d", i); + + devfs_vdd_handle[i] = devfs_register(NULL, name, + DEVFS_FL_DEFAULT, VDD_MAJOR, i, + S_IFCHR | S_IRUGO | S_IWUGO, + &vdd_ops, NULL); + + } + + up(&vdd_table_mutex); +} + +static void unregister_vdd_usr(void) +{ + int i; + + down(&vdd_table_mutex); + + MOD_DEC_USE_COUNT; + + for (i=0; i< MAX_VDD_DEV; i++) + devfs_unregister(devfs_vdd_handle[i]); + + up(&vdd_table_mutex); +} +#endif +//----------------------------module funcs----------------------------------// +int vdd_init(void) +{ + int result; + +#ifdef _DEBUG + char *cptr = "Winbond Virtual Debug Device[c, 2k, round queue]!\n"; + printk("****vdd initing..."); +#endif +#ifdef CONFIG_DEVFS_FS + result = devfs_register_chrdev(VDD_MAJOR, "vdd", &vdd_ops); + if(result < 0) + { + printk("VDD: can't register vdd.\n"); + return result; + } + + register_vdd_usr(); +#else + result = register_chrdev(VDD_MAJOR, "vdd", &vdd_ops); + if(result < 0) + { + printk("VDD: can't register vdd.\n"); + return result; + } +#endif + if(!debug_queue.queue) + debug_queue.queue = MALLOC(debug_queue.MaxSize); + +#ifdef _DEBUG + N_Add(cptr, strlen(cptr)); +#endif + return 0; +} + +void vdd_cleanup(void) +{ + if(debug_queue.queue) + { + FREE(debug_queue.queue); + debug_queue.queue = NULL; + } +#ifdef CONFIG_DEVFS_FS + unregister_vdd_usr(); + devfs_unregister_chrdev(VDD_MAJOR, "vdd"); +#else + unregister_chrdev(VDD_MAJOR, "vdd"); +#endif +} + +module_init(vdd_init); +module_exit(vdd_cleanup); diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c new file mode 100755 index 0000000..7b9cd9b --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c @@ -0,0 +1,600 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. + * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_i2c.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * Winbond W90N710 I2C Driver + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * None + * + * HISTORY + * 2005/05/20 Ver 1.0 Created by PC34 QFu + * + * REMARK + * None + *************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "w90n745_i2c.h" + + +//#define I2C_DEBUG +//#define I2C_DEBUG_PRINT_LINE +//#define I2C_DEBUG_ENABLE_ENTER_LEAVE +//#define I2C_DEBUG_ENABLE_MSG +//#define I2C_DEBUG_ENABLE_MSG2 + +#ifdef I2C_DEBUG +#define PDEBUG(fmt, arg...) printk(fmt, ##arg) +#else +#define PDEBUG(fmt, arg...) +#endif + +#ifdef I2C_DEBUG_PRINT_LINE +#define PRN_LINE() PDEBUG("[%-20s] : %d\n", __FUNCTION__, __LINE__) +#else +#define PRN_LINE() +#endif + +#ifdef I2C_DEBUG_ENABLE_ENTER_LEAVE +#define ENTER() PDEBUG("[%-20s] : Enter...\n", __FUNCTION__) +#define LEAVE() PDEBUG("[%-20s] : Leave...\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#ifdef I2C_DEBUG_ENABLE_MSG +#define MSG(msg) PDEBUG("[%-20s] : %s", __FUNCTION__, msg) +#else +#define MSG(msg) +#endif + +#ifdef I2C_DEBUG_ENABLE_MSG2 +#define MSG2(fmt, arg...) PDEBUG("[%-20s] : "fmt, __FUNCTION__, ##arg) +#define PRNBUF(buf, count) {int i;MSG2("CID Data: ");for(i=0;ino] + addr) +#define i2c_inl(dev, addr) inl(base[dev->no] + addr) + + +static const int base[I2C_NUMBER] = {I2C0_IO_BASE, I2C1_IO_BASE}; +static const int irq[I2C_NUMBER] = {I2C0_IRQ, I2C1_IRQ}; +static int bNackValid; + +static void i2c_Disable(i2c_dev *dev) /* Disable i2c core and interrupt */ +{ + + i2c_outl(dev, 0x00, CSR); +} + +static void i2c_Enable(i2c_dev *dev) /* Enable i2c core and interrupt */ +{ + i2c_outl(dev, 0x03, CSR); +} + +static int i2c_SetSpeed(i2c_dev *dev, int sp) +{ + unsigned int d; + + if( sp != 100 && sp != 400) + return -1; + + d = I2C_INPUT_CLOCK/(sp * 5) -1; + + i2c_outl(dev, d & 0xffff, DIVIDER); + + MSG2("Set Speed = %d\n", sp); + + return 0; +} + +static int i2c_IsBusFree(i2c_dev *dev) +{ + if( (i2c_inl(dev, SWR) & 0x18) == 0x18 && //SDR and SCR keep high + (i2c_inl(dev, CSR) & 0x0400) == 0 ){ //I2C_BUSY is false + //MSG("Bus Free\n"); + + return 1; + } + else{ + //MSG("Bus Busy\n"); + + return 0; + } + +} + +static int i2c_Command(i2c_dev *dev, int cmd) +{ + +#if 0 + printk("CMD : "); + + if(cmd & I2C_CMD_START){ + printk("START "); + } + + if (cmd & I2C_CMD_STOP){ + printk("STOP "); + + } + + if(cmd & I2C_CMD_NACK){ + printk("NACK "); + } + + if(cmd & I2C_CMD_WRITE){ + printk("WRITE : [%02x]", i2c_inl(dev, TXR) & 0xff); + } + + if(cmd & I2C_CMD_READ){ + printk("READ : [%02x]", i2c_inl(dev, RXR) & 0xff); + } + + printk("\n"); + +#endif + + if(cmd & I2C_CMD_WRITE) + bNackValid = 1; + else + bNackValid = 0; + + i2c_outl(dev, cmd, CMDR); + + return 0; + +} + + +static int i2c_CalculateAddress(i2c_dev *dev, int mode) +{ + int i; + unsigned int addr; + unsigned int subaddr; + + subaddr = dev->subaddr; + addr = dev->addr; + + addr = ((addr << 1) & 0xfe) | I2C_WRITE; + + dev->buffer[0] = addr & 0xff; + + for(i = dev->subaddr_len; i > 0; i--){ + dev->buffer[i] = subaddr & 0xff; + subaddr >>= 8; + } + + if(mode == I2C_STATE_READ){ + i = dev->subaddr_len + 1; + dev->buffer[i] = (addr & 0xfe) | I2C_READ; + } + +#ifdef I2C_DEBUG + if(mode == I2C_STATE_WRITE){ + MSG2("Begin Write to Device [%02x] Address [%02x]\n", + dev->addr, dev->subaddr); + MSG("Buffer : \n"); + for(i = 0; i < dev->subaddr_len +1; i++) + PDEBUG("%02x ", dev->buffer[i]); + PDEBUG("\n"); + } + else{ + MSG2("Begin Read from Device [%02x] Address [%02x]\n", + dev->addr, dev->subaddr); + MSG("Buffer : \n"); + for(i = 0; i < dev->subaddr_len +2; i++) + PDEBUG("%02x ", dev->buffer[i]); + PDEBUG("\n"); + } +#endif + + return 0; +} + +/* init i2c_dev after open */ +static int i2c_ResetI2C(i2c_dev *dev) +{ + u32 reg; + + // configure i2c pin + reg = inl(REG_GPIO_CFG5); + reg &= 0x3ff00fff; + reg |= 0x00055000; + outl(reg, REG_GPIO_CFG5); + + dev->addr = -1; + dev->last_error = 0; + dev->subaddr = 0; + dev->subaddr_len = 0; + + init_waitqueue_head(&dev->wq); + + return i2c_SetSpeed(dev, 100); + +} + +void i2c_interrupt(int irq, void *data, struct pt_regs *reg) +{ + i2c_dev *dev = (i2c_dev *)data; + int csr, val; + + csr = i2c_inl(dev, CSR); + + //MSG2("INT : CSR : [%02x] dev->pos = [%02x]\n", csr, dev->pos); + + csr |= 0x04; + + i2c_outl(dev, csr, CSR); + + if(dev->state == I2C_STATE_NOP) + return; + + MSG2("bNackValid = %d\n", bNackValid); + + if((csr & 0x800) && bNackValid){ /* NACK only valid in WRITE */ + MSG("NACK Error\n"); + dev->last_error = I2C_ERR_NACK; + i2c_Command(dev, I2C_CMD_STOP); + goto wake_up_quit; + } + else if(csr & 0x200){ /* Arbitration lost */ + MSG("Arbitration lost\n"); + dev->last_error = I2C_ERR_LOSTARBITRATION; + i2c_Command(dev, I2C_CMD_STOP); + goto wake_up_quit; + } + else if(!(csr & 0x100)){ /* transmit complete */ + if(dev->pos < dev->subaddr_len + 1){ /* send address state */ + MSG("Send Address\n"); + val = dev->buffer[dev->pos++] & 0xff; + i2c_outl(dev, val, TXR); + i2c_Command(dev, I2C_CMD_WRITE); + } + else if(dev->state == I2C_STATE_READ){ + + /* sub address send over , begin restart a read command */ + + if(dev->pos == dev->subaddr_len + 1){ + + MSG("Restart Reading...\n"); + + val = dev->buffer[dev->pos++]; + i2c_outl(dev, val, TXR); + i2c_Command(dev, I2C_CMD_START | I2C_CMD_WRITE); + } + else{ + + dev->buffer[dev->pos++] = i2c_inl(dev, RXR) & 0xff; + + MSG2("Read Pos : [%02x] Data : [%02x]\n", + dev->pos, dev->buffer[dev->pos-1]); + + if( dev->pos < dev->len){ + MSG("Read Next\n"); + if(dev->pos == dev->len -1 ) /* last character */ + i2c_Command(dev, I2C_CMD_READ | + I2C_CMD_STOP | + I2C_CMD_NACK); + else + i2c_Command(dev, I2C_CMD_READ); + } + else{ + MSG("Read Over \n"); + goto wake_up_quit; + } + } + } + else if(dev->state == I2C_STATE_WRITE){ /* write data */ + + if( dev->pos < dev->len){ + MSG2("Write Pos : [%02x] Data : [%02x]\n", + dev->pos, dev->buffer[dev->pos]); + + val = dev->buffer[dev->pos]; + + i2c_outl(dev, val, TXR); + + if(dev->pos == dev->len -1 ) /* last character */ + i2c_Command(dev, I2C_CMD_WRITE| I2C_CMD_STOP); + else + i2c_Command(dev, I2C_CMD_WRITE); + + dev->pos ++; + } + else{ + MSG("Write Over\n"); + goto wake_up_quit; + } + } + //else if(dev->state == I2C_STATE_PROBE){ + // MSG2("Probe Address : [%02x]\n", dev->addr); + // goto wake_up_quit; + //} + + } + + return; + +wake_up_quit: + + MSG("Wake up \n"); + + dev->state = I2C_STATE_NOP; + wake_up_interruptible(&dev->wq); + + return; +} + +static int i2c_open(struct inode *inode, struct file *filp) +{ + i2c_dev *dev; + + unsigned int num = MINOR(inode->i_rdev); + + if( num >= I2C_NUMBER) { + printk("I2C : no dev\n"); + return -ENODEV; + } + dev = (i2c_dev *)kmalloc(sizeof(i2c_dev), GFP_KERNEL); + if(dev == NULL) { + printk("no mem\n"); + return -ENOMEM; + } + dev->no = num; + + if(request_irq(irq[num], i2c_interrupt, SA_INTERRUPT, "i2c", dev) < 0){ + printk("I2C : Request Irq error\n"); + kfree(dev); + return -EIO; + } + + if(i2c_ResetI2C(dev)){ + free_irq(irq[num], dev); + kfree(dev); + printk("eio\n"); + return -EIO; + } + + + MOD_INC_USE_COUNT; + + filp->private_data = dev; + + return 0; +} + +static int i2c_release(struct inode *inode, struct file *flip) +{ + i2c_dev *dev = flip->private_data; + + MSG2("Free IRQ %d\n", irq[dev->no]); + + free_irq(irq[dev->no], dev); + kfree(dev); + MOD_DEC_USE_COUNT; + + return 0; +} + +static int i2c_read(struct file *filp, char *buf, size_t count, loff_t *f_pos) +{ + i2c_dev *dev = (i2c_dev *)filp->private_data; + + if(count == 0) + return 0; + + if(!i2c_IsBusFree(dev)) + return -EBUSY; + + if(count > I2C_MAX_BUF_LEN - 10) + count = I2C_MAX_BUF_LEN - 10; + + dev->state = I2C_STATE_READ; + dev->pos = 1; + dev->len = dev->subaddr_len + 1 + count + 2;/* plus 1 unused char */ + dev->last_error = 0; + + i2c_CalculateAddress(dev, I2C_STATE_READ); + + i2c_Enable(dev); + + i2c_outl(dev, dev->buffer[0] & 0xff, TXR); + + i2c_Command(dev, I2C_CMD_START | I2C_CMD_WRITE); + + wait_event_interruptible(dev->wq, dev->state == I2C_STATE_NOP); + + i2c_Disable(dev); + + if(dev->last_error) + return -EIO; + + if(copy_to_user(buf, dev->buffer + dev->subaddr_len + 3, count)) + return -EFAULT; + + dev->subaddr += count; + + return count; +} + +static int i2c_write(struct file *filp, const char *buf, size_t count, loff_t *f_pos) +{ + i2c_dev *dev = (i2c_dev *)filp->private_data; + + + if(count == 0) + return 0; + + if(!i2c_IsBusFree(dev)) + return -EBUSY; + + + if(count > I2C_MAX_BUF_LEN - 10) + count = I2C_MAX_BUF_LEN - 10; + + if(copy_from_user(dev->buffer + dev->subaddr_len + 1 , buf, count)) + return -EFAULT; + + + dev->state = I2C_STATE_WRITE; + dev->pos = 1; + dev->len = dev->subaddr_len + 1 + count; + dev->last_error = 0; + + i2c_CalculateAddress(dev, I2C_STATE_WRITE); + + i2c_Enable(dev); + + i2c_outl(dev, dev->buffer[0] & 0xff, TXR); + + i2c_Command(dev, I2C_CMD_START | I2C_CMD_WRITE); + + wait_event_interruptible(dev->wq, dev->state == I2C_STATE_NOP); + + i2c_Disable(dev); + + if(dev->last_error) + return -EIO; + + dev->subaddr += count; + + return count; +} + +static int i2c_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg) +{ + int err = 0, tmp, retval = 0; + struct sub_address sub_addr; + i2c_dev *dev =(i2c_dev *)flip->private_data; + + if(_IOC_TYPE(cmd) != I2C_IOC_MAGIC) return -ENOTTY; + if(_IOC_NR(cmd) > I2C_IOC_MAXNR) return -ENOTTY; + + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + + if(err) + return -EFAULT; + + switch(cmd){ + case I2C_IOC_SET_DEV_ADDRESS: + dev->addr = arg; + MSG2("Address : %02x\n", arg&0xff); + break; + + case I2C_IOC_SET_SPEED: + if(i2c_SetSpeed(dev, arg)) + retval = -EPERM; + + break; + + case I2C_IOC_GET_LAST_ERROR: + if(put_user(dev->last_error, (int *)arg)){ + retval = -EFAULT; + break; + } + + break; + + case I2C_IOC_SET_SUB_ADDRESS: + if(copy_from_user(&sub_addr, (void *)arg, sizeof(sub_addr))){ + retval = -EFAULT; + break; + } + + if(sub_addr.sub_addr_len > 4){ + retval = -EPERM; + break; + } + + MSG2("Sub Address = %02x, length = %d\n", + sub_addr.sub_addr, sub_addr.sub_addr_len); + + dev->subaddr = sub_addr.sub_addr; + dev->subaddr_len = sub_addr.sub_addr_len; + + break; + + default: + retval = -ENOTTY; + break; + } + + return retval; + +} + + +struct file_operations i2c_fops = +{ + owner: THIS_MODULE, + open: i2c_open, + release: i2c_release, + ioctl: i2c_ioctl, + read: i2c_read, + write: i2c_write, +}; + +static void i2c_module_exit(void) +{ + unregister_chrdev(I2C_MAJOR, "i2c"); +} + +static int __init i2c_module_init(void) +{ + int i, retval; + + retval = register_chrdev(I2C_MAJOR, "i2c", &i2c_fops); + if(retval < 0) + goto failed; + + printk("I2C Bus Driver has been installed successfully.\n"); + + return 0; + +failed: + + i2c_module_exit(); + + printk("Init I2C Bus Driver failed!\n"); + + return retval; +} + +module_init(i2c_module_init); +module_exit(i2c_module_exit); + diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h b/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h new file mode 100644 index 0000000..b1ab388 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h @@ -0,0 +1,123 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. + * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_i2c.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * Winbond W90N745 I2C Driver header + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * None + * + * HISTORY + * 2005/05/20 Ver 1.0 Created by PC34 QFu + * + * REMARK + * None + *************************************************************************/ +#ifndef _W90N745_I2C_H_ +#define _W90N745_I2C_H_ + +#include +#include + +#include + +#ifdef __KERNEL__ + +#define I2C_NUMBER 2 + +#define I2C_FIFO_LEN 4 +#define I2C_MAX_BUF_LEN 450 + +#define I2C0_IRQ INT_I2C0 +#define I2C1_IRQ INT_I2C1 + +#define I2C_MAJOR 89 +#define I2C0_IO_BASE 0xfff86000 +#define I2C1_IO_BASE 0xfff86100 +#define I2C_IOMEM_SIZE 0x18 + +#define REG_GPIO_CFG5 0xFFF83050 +#define I2C_INPUT_CLOCK 80000 /* 80 000 KHz */ + +/* register map */ +#define CSR 0x00 +#define DIVIDER 0x04 +#define CMDR 0x08 +#define SWR 0x0c /* not available in W90n710 */ +#define RXR 0x10 +#define TXR 0x14 + +/* bit map in CMDR */ +#define I2C_CMD_START 0x10 +#define I2C_CMD_STOP 0x08 +#define I2C_CMD_READ 0x04 +#define I2C_CMD_WRITE 0x02 +#define I2C_CMD_NACK 0x01 + +/* for transfer use */ +#define I2C_WRITE 0x00 +#define I2C_READ 0x01 + + +#define I2C_STATE_NOP 0x00 +#define I2C_STATE_READ 0x01 +#define I2C_STATE_WRITE 0x02 +#define I2C_STATE_PROBE 0x03 + +typedef struct _i2c_dev{ + int no; /* i2c bus number */ + volatile int state; + int last_error; + int addr; + + unsigned subaddr; + int subaddr_len; + + unsigned char buffer[I2C_MAX_BUF_LEN]; + volatile unsigned int pos, len; + + wait_queue_head_t wq; + +}i2c_dev; + +#endif + +struct sub_address{ + char sub_addr_len; + unsigned int sub_addr; +}; + +/* error code */ +#define I2C_ERR_ID (0x00) + +#define I2C_ERR_NOERROR (0x00) +#define I2C_ERR_LOSTARBITRATION (0x01 | I2C_ERR_ID) +#define I2C_ERR_BUSBUSY (0x02 | I2C_ERR_ID) +#define I2C_ERR_NACK (0x03 | I2C_ERR_ID) /* data transfer error */ +#define I2C_ERR_SLAVENACK (0x04 | I2C_ERR_ID) /* slave not respond after address */ + +/* define ioctl command */ +#define I2C_IOC_MAGIC 'i' + +#define I2C_IOC_MAXNR 3 + +#define I2C_IOC_SET_DEV_ADDRESS _IOW(I2C_IOC_MAGIC, 0, int) +#define I2C_IOC_SET_SUB_ADDRESS _IOW(I2C_IOC_MAGIC, 1, int) +#define I2C_IOC_SET_SPEED _IOW(I2C_IOC_MAGIC, 2, int) +#define I2C_IOC_GET_LAST_ERROR _IOR(I2C_IOC_MAGIC, 3, int) +#endif + diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c new file mode 100755 index 0000000..d350a47 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c @@ -0,0 +1,253 @@ +/**************************************************************************** + * * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. * + * * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_keypad.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * The Winbond 64-keyboard driver + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * None + * + * HISTORY + * 2005/09/09 Ver 1.0 Created by PC34 YHan + * + * REMARK + * None + *************************************************************************/ + +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include "w90n745_keypad.h" + +#define MAJOR_NUM 192 + +static int volatile kpd_get=0; +static int volatile kpd_block=1; +unsigned char DEV_NAME[10] = "Keypad"; + +typedef struct _keymap +{ + short row; + short col; +}keymap; + +keymap key __attribute__ ((aligned (4))); + +static DECLARE_WAIT_QUEUE_HEAD(read_wait_a); + +static void read_task_block() +{ + DECLARE_WAITQUEUE(wait, current); + add_wait_queue(&read_wait_a, &wait); + set_current_state(TASK_INTERRUPTIBLE); + + schedule(); + + set_current_state(TASK_RUNNING); + remove_wait_queue(&read_wait_a, &wait); + + return ; +} + +static void read_task_wake_up(void) +{ + wake_up_interruptible(&read_wait_a); + + return ; +} + +void keypad745_irq(int irq, void *dev_id, struct pt_regs *regs) +{ + + volatile unsigned int status; + kpd_get=1; + + key.row = 0; + key.col = 0; + + status = readl(KPISTATUS); + #ifdef KPI_DEBUG + printk("KPI ISR KPISTATUS=0x%08x\n",status); + #endif + if(status&0x00210000) + { + key.row = (status&0x00000078)>>3; + key.col = (status&0x00000007); + } + + if(kpd_block) + read_task_wake_up(); + + return; + +} + +int keypad745_open(struct inode* i,struct file* f) +{ + int result; + int irq; + int old_cfg; + kpd_block=1; + + if(f->f_flags & 0x800) //0x800:04000 + kpd_block=0; + + + irq = INT_KEYPAD; + + MOD_INC_USE_COUNT; + + result = request_irq(irq,keypad745_irq,0,DEV_NAME,NULL); + if(result!=0) + printk("register the keypad_irq failed!\n"); + + old_cfg=readl(GPIO_CFG); + old_cfg=old_cfg&GPIO_CFG_MASK; + old_cfg=old_cfg|GPIO_CFG_VALUE; + writel(old_cfg,GPIO_CFG); + + #ifdef KPI_DEBUG + printk("KPI OPEN:\nKPICONF=0x%08x\nGPIO_CFG=0x%08x\n",readl(KPICONF),readl(GPIO_CFG)); + #endif + + return 0; +} + +int keypad745_close(struct inode* i,struct file* f) +{ + MOD_DEC_USE_COUNT; + free_irq(INT_KEYPAD,NULL); + + return 0; +} + + +ssize_t keypad745_read(struct file *filp, char *buff, size_t read_mode, loff_t *offp) +{ + kpd_block = read_mode ; + + if(kpd_block) + { + read_task_block(); + } + + if(kpd_get) + { + kpd_get=0; + copy_to_user(buff,(char*)&key,sizeof(keymap)); + key.row = 0; + key.col = 0; + return 1; + } + else + return -1; +} + +static int keypad745_ioctl(struct inode *inode, struct file *flip, + unsigned int cmd, unsigned long arg) +{ + int err = 0; + + if(_IOC_TYPE(cmd) != KEYPAD_MAGIC) return -ENOTTY; + if(_IOC_NR(cmd) > KEYPAD_MAXNR) return -ENOTTY; + + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + + if(err) + return -EFAULT; + + switch (cmd) + { + case KPD_BLOCK: + kpd_block=1; + flip->f_flags &= ~0x800; + break; + case KPD_NONBLOCK: + kpd_block=0; + flip->f_flags |= ~0x800; + + break; + default: + break; + } + + return 0; +} + +struct file_operations keypad745_fops = +{ + owner: THIS_MODULE, + open: keypad745_open, + read: keypad745_read, + ioctl:keypad745_ioctl, + release: keypad745_close, +}; + +static int __init keypad_745_reg(void) +{ + int result; + int old_cfg; + result = register_chrdev(MAJOR_NUM,DEV_NAME,&keypad745_fops); + if(result<0) + { + printk("initial the device error!\n"); + return (result); + } + + old_cfg=readl(GPIO_CFG); + old_cfg=old_cfg&GPIO_CFG_MASK; + old_cfg=old_cfg|GPIO_CFG_VALUE; + writel(old_cfg,GPIO_CFG); + + writel(0,KPICONF); + writel(0,KPI3KCONF); + writel(0,KPISTATUS); +#ifdef __WB_EVB__ + old_cfg=readl(GPIO_DIR); + old_cfg |= 0x3FF0000; + writel(old_cfg,GPIO_DIR); +#endif + writel(KPICONF_VALUE,KPICONF); + + init_waitqueue_head(&read_wait_a); + + printk("W90N745 Keypad initialized successful\n"); + + return (result); + +} + +static void keypad_745_exit(void) +{ + unregister_chrdev(MAJOR_NUM,DEV_NAME); + + return; +} + +module_init(keypad_745_reg); +module_exit(keypad_745_exit); diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h b/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h new file mode 100755 index 0000000..4395461 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h @@ -0,0 +1,57 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. + * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_keypad.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * The winbond 64-keyboard driver header file + * + * DATA STRUCTURES + * None + * + * FUNCTIONS + * None + * + * HISTORY + * 2005/09/09 Ver 1.0 Created by PC34 YHan + * + * REMARK + * None + *************************************************************************/ + +#ifndef W90N745__KEYPAD_H +#define W90N745__KEYPAD_H + +//#define KPI_DEBUG + +#define KPICONF 0xFFF88000 +#define KPI3KCONF 0xFFF88004 +#define KPILPCONF 0xFFF88008 +#define KPISTATUS 0xFFF8800C + +#ifdef __WB_EVB__ +#define GPIO_CFG 0xFFF83020 +#define GPIO_DIR 0xFFF83024 +#define GPIO_CFG_MASK 0xFFF00000 +#define GPIO_CFG_VALUE 0x000AAAAA +#define KPICONF_VALUE 0x000405FA +#endif + + + +#define KEYPAD_MAGIC 'k' +#define KEYPAD_MAXNR 2 + +#define KPD_BLOCK _IOW('k', 1, unsigned int) +#define KPD_NONBLOCK _IOW('k', 2, unsigned int) + +#endif diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c new file mode 100644 index 0000000..17ac0c0 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c @@ -0,0 +1,809 @@ +/**************************************************************************** + * * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. * + * * + ***************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * linux-2.4.x/drivers/char/w90n745_ps2.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * This file is the winbond ps2 keyboard driver + * + * HISTORY + * 1/9/2005 Ver 1.0 Created by PC34 MCLi + * + * REMARK + * None + **************************************************************************/ + +#include + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#undef KBD_DATA_REG +#undef KBD_STATUS_REG +#undef KBD_CNTL_REG + +#include "keyboard.h" +#include "w90n745_ps2.h" + +#include +#include +#include +#include + +#include + +/* Some configuration switches are present in the include file... */ + + + +/* Simple translation table for the SysRq keys */ + +static void kbd_write_command_w(int data); +static void kbd_write_output_w(int data); +#ifndef kbd_controller_present +#define kbd_controller_present() 1 +#endif +static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED; +static unsigned char handle_kbd_event(void); + +/* used only by send_data - set by keyboard_interrupt */ +static volatile unsigned char reply_expected; +static volatile unsigned char acknowledge; +static volatile unsigned char resend; + +/* + * Wait for keyboard controller input buffer to drain. + * + * Don't use 'jiffies' so that we don't depend on + * interrupts.. + * + * Quote from PS/2 System Reference Manual: + * + * "Address hex 0060 and address hex 0064 should be written only when + * the input-buffer-full bit and output-buffer-full bit in the + * Controller Status register are set 0." + */ + +unsigned char IsExt = 0; +unsigned char IsLED = 0; +unsigned char CapsLock = 0; +unsigned char gvLEDCode = 0; /* bit 2 ~ 0 : Capslock, 1:NumLock, 0:ScrollLock */ + +int IsLEDKeyPress(unsigned int code) +{ + int value = 0; + + if (code == 0x77) + { + value = 1; + if (gvLEDCode & 0x02) + gvLEDCode &= 0xFD; + else + gvLEDCode |= 0x02; + } + else if (code == 0x7E) + { + value = 1; + if (gvLEDCode & 0x01) gvLEDCode &= 0xFE; + else gvLEDCode |= 0x01; + } + else if (code == 0x58) + { + value = 1; + if (gvLEDCode & 0x04) gvLEDCode &= 0xFB; + else gvLEDCode |= 0x04; + } + + return value; +} + +int SendKBCommand(unsigned char cmd) +{ + unsigned volatile int mctl; + unsigned volatile char scancode; + unsigned long timeout = KBD_TIMEOUT*5; + + DWORD_WRITE(KBD_CNTL_REG, (cmd | 0x100)); + mctl=DWORD_READ(KBD_CNTL_REG) & 0x100; + while (mctl) + { + if (!--timeout) { + printk("keyboard: Timeout - AT keyboard not present?(%02x)\n", cmd); + + return 0; + } + + mctl=DWORD_READ(KBD_CNTL_REG) & 0x100; + } + + while (1) + { + if (!--timeout) { + printk("keyboard: Timeout - AT keyboard not present?(%02x)\n", cmd); + + return 0; + } + scancode=DWORD_READ(PS2_SCANCODE_REG)&0xFF; + // printk("scancode:%x\n",scancode); + if (scancode == KBD_REPLY_ACK) + break; + } + + return 1; +} + +static void kb_wait(void) +{ + unsigned long timeout = KBC_TIMEOUT; + + do { + /* + * "handle_kbd_event()" will handle any incoming events + * while we wait - keypresses or mouse movement. + */ + unsigned char status = handle_kbd_event(); + + if(CapsLock) + status=0x14; + + if (! (status & KBD_STAT_IBF)) + return; + + mdelay(1); + timeout--; + } while (timeout); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "Keyboard timed out[1]\n"); +#endif +} + +/* + * Translation of escaped scancodes to keycodes. + * This is now user-settable. + * + */ +#define E0_KPENTER 90 +#define E0_RCTRL 20 +#define E0_KPSLASH 74 +#define E0_PRSCR 99 +#define E0_RALT 100 +#define E0_BREAK 101 /* (control-pause) */ +#define E0_HOME 72 +#define E0_UP 103 +#define E0_PGUP 104 +#define E0_LEFT 128 +#define E0_RIGHT 106 +#define E0_END 129 +#define E0_DOWN 130 +#define E0_PGDN 109 +#define E0_INS 110 +#define E0_DEL 111 + +#define E1_PAUSE 138 + +/* + * They could be thrown away (and all occurrences below replaced by 0), + * but that would force many users to use the `setkeycodes' utility, where + * they needed not before. It does not matter that there are duplicates, as + * long as no duplication occurs for any single keyboard. + */ +#define SC_LIM 89 + +#define FOCUS_PF1 85 /* actual code! */ +#define FOCUS_PF2 89 +#define FOCUS_PF3 90 +#define FOCUS_PF4 91 +#define FOCUS_PF5 92 +#define FOCUS_PF6 93 +#define FOCUS_PF7 94 +#define FOCUS_PF8 95 +#define FOCUS_PF9 120 +#define FOCUS_PF10 121 +#define FOCUS_PF11 122 +#define FOCUS_PF12 123 + +#define JAP_86 124 +/* mcli2@winbond.com.tw: + * The four keys are located over the numeric keypad, and are + * labelled A1-A4. It's an rc930 keyboard, from + * Regnecentralen/RC International, Now ICL. + * Scancodes: 59, 5a, 5b, 5c. + */ +#define RGN1 124 +#define RGN2 125 +#define RGN3 126 +#define RGN4 127 + +static unsigned char high_keys[128 - SC_LIM] = { + RGN1, RGN2, RGN3, RGN4, 0, 0, 0, /* 0x69-0x6f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x70-0x77 */ + 0, 0, 0, 0, 0, FOCUS_PF11, 0, FOCUS_PF12, /* 0x78-0x7f */ + 0, 0, 0, FOCUS_PF2, FOCUS_PF9, 0, 0, FOCUS_PF3, /* 0x80-0x87 */ + FOCUS_PF4, FOCUS_PF5, FOCUS_PF6, FOCUS_PF7, /* 0x88-0x8b */ + FOCUS_PF8, JAP_86, FOCUS_PF10, 0 /* 0x8c-0x8f */ +}; + +/* BTC */ +#define E0_MACRO 132 +/* LK450 */ +#define E0_F13 133 +#define E0_F14 134 +#define E0_HELP 135 +#define E0_DO 136 +#define E0_F17 137 +#define E0_KPMINPLUS 138 + +/* + * My OmniKey generates e0 4c for the "OMNI" key and the + * right alt key does nada. [mcli2@winbond.com.tw] + */ +#define E0_OK 144 +/* + * New microsoft keyboard is rumoured to have + * 1F (left window button), 27 (right window button), + * 2F (menu button). [or: LBANNER, RBANNER, RMENU] + * [or: Windows_L, Windows_R, TaskMan] + */ +#define E0_MSLW 125 +#define E0_MSRW 126 +#define E0_MSTM 127 + +static unsigned char e0_keys[144] = { + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x00-0x07 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x08-0x0f */ + 0, 0, 0, 0, E0_RCTRL, 0, 0, 0, /* 0x10-0x17 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x18-0x1f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x20-0x27 */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x28-0x2f */ + 0, 0, 0, 0, 0, 0, 0, E0_PRSCR, /* 0x30-0x37 */ + E0_RALT, 0, 0, 0, 0, 0, 0, 0, /* 0x38-0x3f */ + 0, 0, 0, 0, 0, 0, E0_BREAK, E0_MSTM, /* 0x40-0x47 */ + E0_HOME, 0, E0_KPSLASH, 0, 0, 0, 0, 0, /* 0x48-0x4f */ + 0, 0, 0, 0, 0, 0, 0, 0, /* 0x50-0x57 */ + 0, 0, E0_KPENTER, 0, E0_MSRW, 0, 0, 0, /* 0x58-0x5f */ + 0, 0, E0_MSLW, 0, 0, 0, 0, 0, /* 0x60-0x67 */ + 0, E0_END, 0, E0_LEFT, 0, 0, 0, E0_MACRO, /* 0x68-0x6f */ + E0_INS, E0_DEL, E0_DOWN, E0_OK, E0_RIGHT, 0, E0_UP, 0, /* 0x70-0x77 */ + 0, E0_KPMINPLUS, E0_PGDN, 0, E0_PGUP, 0, 0, 0, /* 0x78-0x7f */ + 0, 0, 0, 0, 0, E0_F13, E0_F14, E0_HELP, /* 0x80-0x87 */ + E0_DO, E0_F17, 0, 0, 0, 0, 0, 0 /* 0x88-0x8F */ +}; + +char temp=0; + +int pckbd_setkeycode(unsigned int scancode, unsigned int keycode) +{ + if (scancode < SC_LIM || scancode > 255 || keycode > 143) + return -EINVAL; + if (scancode < 144) + high_keys[scancode - SC_LIM] = keycode; + else + e0_keys[scancode - 144] = keycode; + + return 0; +} + + +int pckbd_getkeycode(unsigned int scancode) +{ + return + (scancode < SC_LIM || scancode > 255) ? -EINVAL : + (scancode < 144) ? high_keys[scancode - SC_LIM] : + e0_keys[scancode - 144]; +} + +static int do_acknowledge(unsigned char scancode) +{ + if (reply_expected) { + /* Unfortunately, we must recognise these codes only if we know they + * are known to be valid (i.e., after sending a command), because there + * are some brain-damaged keyboards (yes, FOCUS 9000 again) which have + * keys with such codes :( + */ + if (scancode == KBD_REPLY_ACK) { + acknowledge = 1; + reply_expected = 0; + return 0; + } else if (scancode == KBD_REPLY_RESEND) { + resend = 1; + reply_expected = 0; + return 0; + } + } + + return 1; +} + +int pckbd_translate(unsigned char scancode, unsigned char *keycode, char raw_mode) +{ + static int prev_scancode; + + /* special prefix scancodes.. */ + if (scancode == 0xe0 || scancode == 0xe1) { + prev_scancode = scancode; + return 0; + } + + /* 0xFF is sent by a few keyboards, ignore it. 0x00 is error */ + if (scancode == 0x00 || scancode == 0xff) { + prev_scancode = 0; + return 0; + } + + scancode &= 0x7f; + + if (prev_scancode) { + /* + * usually it will be 0xe0, but a Pause key generates + * e1 14 77 e1 14 77 when pressed, and nothing when released + */ + if (prev_scancode != 0xe0) { + if (prev_scancode == 0xe1 && scancode == 0x14) { + prev_scancode = 0x100; + return 0; + } else if (prev_scancode == 0x100 && scancode == 0x77) { + *keycode = E1_PAUSE; + prev_scancode = 0; + } else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown e1 escape sequence\n"); +#endif + prev_scancode = 0; + return 0; + } + } else { + prev_scancode = 0; + /* + * The keyboard maintains its own internal caps lock and + * num lock statuses. In caps lock mode E0 AA precedes make + * code and E0 2A follows break code. In num lock mode, + * E0 2A precedes make code and E0 AA follows break code. + * We do our own book-keeping, so we will just ignore these. + */ + /* + * For my keyboard there is no caps lock mode, but there are + * both Shift-L and Shift-R modes. The former mode generates + * E0 2A / E0 AA pairs, the latter E0 B6 / E0 36 pairs. + * So, we should also ignore the latter. - aeb@cwi.nl + */ + if (scancode == 0x12 || scancode == 0x59) + return 0; + + if (e0_keys[scancode]) + *keycode = e0_keys[scancode]; + else { +#ifdef KBD_REPORT_UNKN + if (!raw_mode) + printk(KERN_INFO "keyboard: unknown scancode e0 %02x\n", + scancode); +#endif + return 0; + } + } + } + else + *keycode = scancode; + + return 1; +} + +char pckbd_unexpected_up(unsigned char keycode) +{ + /* unexpected, but this can happen: maybe this was a key release for a + FOCUS 9000 PF key; if we want to see it, we have to clear up_flag */ + if (keycode >= 144 || keycode == 96) + return 0; + else + return 0200; +} + +int pckbd_pm_resume(struct pm_dev *dev, pm_request_t rqst, void *data) +{ + return 0; +} + + +static inline void handle_mouse_event(unsigned char scancode) +{ +} + +static unsigned char kbd_exists = 1; + +static inline void handle_keyboard_event(unsigned int scancode) +{ +#ifdef CONFIG_VT + kbd_exists = 1; + if (do_acknowledge(scancode&0xFF)) + handle_scancode(scancode&0xFF, !(scancode & 0x200)); +#endif + tasklet_schedule(&keyboard_tasklet); +} + +/* + * This reads the keyboard status port, and does the + * appropriate action. + * + * It requires that we hold the keyboard controller + * spinlock. + */ + +static unsigned char handle_kbd_event(void) +{ + unsigned int scancode; + unsigned char aciicode; + static unsigned char led_count=0; + unsigned int status = kbd_read_status(); + + IsExt=CapsLock=0; + + if (status & 0x01) + { + scancode = DWORD_READ(KBD_DATA_REG); + aciicode = DWORD_READ(PS2_ASCII_REG) & 0xFF; + + if(scancode & 0x100) + IsExt=1; + + if(scancode==0x58) + CapsLock=1; + +// printk("scancode:%x\n",scancode); + + IsLEDKeyPress(scancode); + + handle_keyboard_event(scancode); + DWORD_WRITE(KBD_STATUS_REG, 0x01); //clear the status + } + else if (status & 0x10) + DWORD_WRITE(KBD_STATUS_REG, 0x10); //clear the status + else if (status & 0x20) + printk("WARNING ! TX_ERR Bit is set.\n"); + + return status; +} + +static void keyboard_interrupt(int irq, void *dev_id, struct pt_regs *regs) +{ +#ifdef CONFIG_VT + kbd_pt_regs = regs; +#endif + spin_lock_irq(&kbd_controller_lock); + handle_kbd_event(); + spin_unlock_irq(&kbd_controller_lock); +} + +/* + * send_data sends a character to the keyboard and waits + * for an acknowledge, possibly retrying if asked to. Returns + * the success status. + * + * Don't use 'jiffies', so that we don't depend on interrupts + */ +static int send_data(unsigned char data) +{ + int retries = 3; + unsigned int scancode; + + do { + unsigned long timeout = KBD_TIMEOUT; + + acknowledge = 0; /* Set by interrupt routine on receipt of ACK. */ + resend = 0; + reply_expected = 1; + kbd_write_output_w(data); + + + if(!(DWORD_READ(KBD_CNTL_REG) & 0x100)) + acknowledge=1; + + scancode=DWORD_READ(PS2_SCANCODE_REG)&0xFF; + if(scancode==KBD_REPLY_ACK) + return 1; + + for (;;) { + if (acknowledge) + return 1; + if (resend) + break; + mdelay(1); + if (!--timeout) { +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "keyboard: Timeout - AT keyboard not present?(%02x)\n", data); +#endif + return 0; + } + } + } while (retries-- > 0); +#ifdef KBD_REPORT_TIMEOUTS + printk(KERN_WARNING "keyboard: Too many NACKs -- noisy kbd cable?\n"); +#endif + return 0; +} + +void pckbd_leds(unsigned char leds) +{ +#if 0 + if (kbd_exists && (!send_data(KBD_CMD_SET_LEDS) || !send_data(leds))) { + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + kbd_exists = 0; + } +#else + SendKBCommand(KBD_CMD_SET_LEDS); + SendKBCommand(leds); +#endif +} + +#define DEFAULT_KEYB_REP_DELAY 250 +#define DEFAULT_KEYB_REP_RATE 30 /* cps */ + +static struct kbd_repeat kbdrate={ + DEFAULT_KEYB_REP_DELAY, + DEFAULT_KEYB_REP_RATE +}; + +static unsigned char parse_kbd_rate(struct kbd_repeat *r) +{ + static struct r2v{ + int rate; + unsigned char val; + } kbd_rates[]={ {5,0x14}, + {7,0x10}, + {10,0x0c}, + {15,0x08}, + {20,0x04}, + {25,0x02}, + {30,0x00} + }; + static struct d2v{ + int delay; + unsigned char val; + } kbd_delays[]={{250,0}, + {500,1}, + {750,2}, + {1000,3} + }; + int rate=0,delay=0; + if (r != NULL){ + int i,new_rate=30,new_delay=250; + if (r->rate <= 0) + r->rate=kbdrate.rate; + if (r->delay <= 0) + r->delay=kbdrate.delay; + for (i=0; i < sizeof(kbd_rates)/sizeof(struct r2v); i++) + if (kbd_rates[i].rate == r->rate){ + new_rate=kbd_rates[i].rate; + rate=kbd_rates[i].val; + break; + } + for (i=0; i < sizeof(kbd_delays)/sizeof(struct d2v); i++) + if (kbd_delays[i].delay == r->delay){ + new_delay=kbd_delays[i].delay; + delay=kbd_delays[i].val; + break; + } + r->rate=new_rate; + r->delay=new_delay; + } + return (delay << 5) | rate; +} + +static int write_kbd_rate(unsigned char r) +{ + if (!send_data(KBD_CMD_SET_RATE) || !send_data(r)){ + send_data(KBD_CMD_ENABLE); /* re-enable kbd if any errors */ + return 0; + }else + return 1; +} + +static int pckbd_rate(struct kbd_repeat *rep) +{ + if (rep == NULL) + return -EINVAL; + else{ + unsigned char r=parse_kbd_rate(rep); + struct kbd_repeat old_rep; + memcpy(&old_rep,&kbdrate,sizeof(struct kbd_repeat)); + if (write_kbd_rate(r)){ + memcpy(&kbdrate,rep,sizeof(struct kbd_repeat)); + memcpy(rep,&old_rep,sizeof(struct kbd_repeat)); + return 0; + } + } + return -EIO; +} + +#define KBD_NO_DATA (-1) /* No data */ +#define KBD_BAD_DATA (-2) /* Parity or other error */ + +static int __init kbd_read_data(void) +{ + int retval = KBD_NO_DATA; + unsigned int status; + + status = kbd_read_status(); + + if (status & KBD_STAT_OBF) { + unsigned char data = kbd_read_input(); + + retval = data; + if (status & (KBD_STAT_GTO | KBD_STAT_PERR)) + retval = KBD_BAD_DATA; + } + + return retval; +} + +static void __init kbd_clear_input(void) +{ + int maxread = 100; /* Random number */ + + do { + if (kbd_read_data() == KBD_NO_DATA) + break; + } while (--maxread); +} + +static int __init kbd_wait_for_input(void) +{ + long timeout = KBD_INIT_TIMEOUT; + + do { + int retval = kbd_read_data(); + if (retval >= 0) + { + + return retval; + } + mdelay(1); + } while (--timeout); + return -1; +} + +static void kbd_write_command_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +static void kbd_write_output_w(int data) +{ + unsigned long flags; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_output(data); + spin_unlock_irqrestore(&kbd_controller_lock, flags); +} + +#if defined(__alpha__) +/* + * Some Alphas cannot mask some/all interrupts, so we have to + * make sure not to allow interrupts AT ALL when polling for + * specific return values from the keyboard. + * + * I think this should work on any architecture, but for now, only Alpha. + */ +static int kbd_write_command_w_and_wait(int data) +{ + unsigned long flags; + int input; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_command(data); + input = kbd_wait_for_input(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + return input; +} + +static int kbd_write_output_w_and_wait(int data) +{ + unsigned long flags; + int input; + + spin_lock_irqsave(&kbd_controller_lock, flags); + kb_wait(); + kbd_write_output(data); + input = kbd_wait_for_input(); + spin_unlock_irqrestore(&kbd_controller_lock, flags); + return input; +} +#else +static int kbd_write_command_w_and_wait(int data) +{ + kbd_write_command_w(data); + return kbd_wait_for_input(); +} + +static int kbd_write_output_w_and_wait(int data) +{ + kbd_write_output_w(data); + return kbd_wait_for_input(); +} +#endif /* __alpha__ */ + +void __init pckbd_init_hw(void) +{ + /* change GPIO5 [4:5] to PS2 signals */ + DWORD_WRITE(0xFFF83050, DWORD_READ(0xFFF83050)|0xF00); + + /* Flush any pending input. */ + kbd_clear_input(); + + kbd_rate = pckbd_rate; + + if(request_irq(INT_PS2, keyboard_interrupt, SA_SHIRQ, "keyboard", &temp)) + printk("keyboard request_irq error !\n"); + else + printk("keyboard request_irq OK !\n"); +} + +static int blink_frequency = HZ/2; + +/* Tell the user who may be running in X and not see the console that we have + panic'ed. This is to distingush panics from "real" lockups. + Could in theory send the panic message as morse, but that is left as an + exercise for the reader. */ +void panic_blink(void) +{ + static unsigned long last_jiffie; + static char led; + /* Roughly 1/2s frequency. KDB uses about 1s. Make sure it is + different. */ + if (!blink_frequency) + return; + if (jiffies - last_jiffie > blink_frequency) { + led ^= 0x01 | 0x04; + while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); + kbd_write_output(KBD_CMD_SET_LEDS); + mdelay(1); + while (kbd_read_status() & KBD_STAT_IBF) mdelay(1); + mdelay(1); + kbd_write_output(led); + last_jiffie = jiffies; + } +} + +static int __init panicblink_setup(char *str) +{ + int par; + if (get_option(&str,&par)) + blink_frequency = par*(1000/HZ); + return 1; +} + +/* panicblink=0 disables the blinking as it caused problems with some console + switches. otherwise argument is ms of a blink period. */ +__setup("panicblink=", panicblink_setup); diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h new file mode 100644 index 0000000..baab952 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h @@ -0,0 +1,115 @@ +/**************************************************************************** + * * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. * + * * + ***************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_ps2.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * This file is the head file for winbond ps2 driver + * + * HISTORY + * 1/9/2005 Ver 1.0 Created by PC34 MCLi + * + * REMARK + * None + **************************************************************************/ + +/* + * Configuration Switches + */ + +#ifndef W90N745_PS2_H +#define W90N745_PS2_H + +#undef KBD_REPORT_ERR /* Report keyboard errors */ +#define KBD_REPORT_UNKN /* Report unknown scan codes */ +#define KBD_REPORT_TIMEOUTS /* Report keyboard timeouts */ +#undef KBD_IS_FOCUS_9000 /* We have the brain-damaged FOCUS-9000 keyboard */ +#undef INITIALIZE_MOUSE /* Define if your PS/2 mouse needs initialization. */ + + +#define KBD_INIT_TIMEOUT 1000 /* Timeout in ms for initializing the keyboard */ +#define KBC_TIMEOUT 250 /* Timeout in ms for sending to keyboard controller */ +#define KBD_TIMEOUT 5000 /* Timeout in ms for keyboard command acknowledge */ + +/* + * Internal variables of the driver + */ +extern unsigned char pckbd_read_mask; +extern unsigned char aux_device_present; + +/* + * Keyboard Controller Registers on normal PCs. + */ +#define KBD_STATUS_REG 0xFFF89004 /* Status register (R) */ +#define KBD_CNTL_REG 0xFFF89000 /* Controller command register (W) */ +#define KBD_DATA_REG 0xFFF89008 /* Keyboard data register (R/W) */ +#define PS2_SCANCODE_REG 0xFFF89008 +#define PS2_ASCII_REG 0xFFF8900C + +/* + * Keyboard Controller Commands + */ +#define KBD_CCMD_READ_MODE 0x20 /* Read mode bits */ +#define KBD_CCMD_WRITE_MODE 0x60 /* Write mode bits */ +#define KBD_CCMD_GET_VERSION 0xA1 /* Get controller version */ +#define KBD_CCMD_MOUSE_DISABLE 0xA7 /* Disable mouse interface */ +#define KBD_CCMD_MOUSE_ENABLE 0xA8 /* Enable mouse interface */ +#define KBD_CCMD_TEST_MOUSE 0xA9 /* Mouse interface test */ +#define KBD_CCMD_SELF_TEST 0xAA /* Controller self test */ +#define KBD_CCMD_KBD_TEST 0xAB /* Keyboard interface test */ +#define KBD_CCMD_KBD_DISABLE 0xAD /* Keyboard interface disable */ +#define KBD_CCMD_KBD_ENABLE 0xAE /* Keyboard interface enable */ +#define KBD_CCMD_WRITE_AUX_OBUF 0xD3 /* Write to output buffer as if initiated by the auxiliary device */ +#define KBD_CCMD_WRITE_MOUSE 0xD4 /* Write the following byte to the mouse */ + +/* + * Keyboard Commands + */ +#define KBD_CMD_SET_LEDS 0xED /* Set keyboard leds */ +#define KBD_CMD_SET_RATE 0xF3 /* Set typematic rate */ +#define KBD_CMD_ENABLE 0xF4 /* Enable scanning */ +#define KBD_CMD_DISABLE 0xF5 /* Disable scanning */ +#define KBD_CMD_RESET 0xFF /* Reset */ + +/* + * Keyboard Replies + */ +#define KBD_REPLY_POR 0xAA /* Power on reset */ +#define KBD_REPLY_ACK 0xFA /* Command ACK */ +#define KBD_REPLY_RESEND 0xFE /* Command NACK, send the cmd again */ + +/* + * Status Register Bits + */ +#define KBD_STAT_OBF 0x01 /* Keyboard output buffer full */ +#define KBD_STAT_IBF 0x02 /* Keyboard input buffer full */ +#define KBD_STAT_SELFTEST 0x04 /* Self test successful */ +#define KBD_STAT_CMD 0x08 /* Last write was a command write (0=data) */ +#define KBD_STAT_UNLOCKED 0x10 /* Zero if keyboard locked */ +#define KBD_STAT_MOUSE_OBF 0x20 /* Mouse output buffer full */ +#define KBD_STAT_GTO 0x40 /* General receive/xmit timeout */ +#define KBD_STAT_PERR 0x80 /* Parity error */ + +/* + * Controller Mode Register Bits + */ + +#define KBD_MODE_KBD_INT 0x01 /* Keyboard data generate IRQ1 */ +#define KBD_MODE_MOUSE_INT 0x02 /* Mouse data generate IRQ12 */ +#define KBD_MODE_SYS 0x04 /* The system flag (?) */ +#define KBD_MODE_NO_KEYLOCK 0x08 /* The keylock doesn't affect the keyboard if set */ +#define KBD_MODE_DISABLE_KBD 0x10 /* Disable keyboard interface */ +#define KBD_MODE_DISABLE_MOUSE 0x20 /* Disable mouse interface */ +#define KBD_MODE_KCC 0x40 /* Scan code conversion to PC format */ +#define KBD_MODE_RFU 0x80 + +#endif //W90N745_PS2_H diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c new file mode 100644 index 0000000..4ffd03d --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c @@ -0,0 +1,4650 @@ +/* + * linux/drivers/char/w90n745_uart.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * 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 + * + * 03/96: Modularised by Angelo Haritsis + * + * rs_set_termios fixed to look also for changes of the input + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. + * Bernd Anhäupl 05/17/96. + * + * 1/97: Extended dumb serial ports are a config option now. + * Saves 4k. Michael A. Griffith + * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * suppress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * + * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a + * few races on freeing buffers too. + * Alan Modra + * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton + * + * 7/00: Support Timedia/Sunix/Exsys PCI cards + * + * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. + * Arnaldo Carvalho de Melo + * + * 10/00: add in optional software flow control for serial console. + * Kanoj Sarcar (Modified by Theodore Ts'o) + * + * 08/30/2005:Porting to Winbond uart + * + */ + +static char *serial_version = "1.0"; +static char *serial_revdate = "2005-08-15"; + +/* + * Serial driver configuration section. Here are the various options: + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. + * + * CONFIG_SERIAL_MANY_PORTS + * Enables support for ports beyond the standard, stupid + * COM 1/2/3/4. + * + * CONFIG_SERIAL_MULTIPORT + * Enables support for special multiport board support. + * + * CONFIG_SERIAL_SHARE_IRQ + * Enables support for multiple serial ports on one IRQ + * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#if 0 +/* These defines are normally controlled by the autoconf.h */ +#define CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ +#define CONFIG_SERIAL_MULTIPORT +#define CONFIG_HUB6 +#endif + +#ifndef CONFIG_BOARD_W90N745 +#ifdef CONFIG_PCI +#define ENABLE_SERIAL_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +//#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 131343) +#include +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include +#endif +#include +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE +#include +#endif +#ifdef ENABLE_SERIAL_PCI +#include +#endif +#ifdef ENABLE_SERIAL_PNP +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif +#ifdef CONFIG_LEDMAN +#include +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +#include +#include +#include +#include + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Winbond W90N745 Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * 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]; +#ifdef CONFIG_SERIAL_MULTIPORT +static struct rs_multiport_struct rs_multiport[NR_IRQS]; +#endif +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_SERIAL_WINBOND_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static unsigned detect_uart_irq (struct serial_state * state); +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, // usurped by cyclades.c + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, // usurped by cyclades.c + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "W90N745", 1, 0}, /* Winbond W90N745 uart support */ + { 0, 0} +}; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { + SERIAL_PORT_DFNS // Defined in serial.h +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +#define NR_PCI_BOARDS 8 + +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif +#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#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 + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user 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; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +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; +} + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return CSR_READ( offset); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, + int value) +{ + CSR_WRITE(offset, value); +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * For the 16C950 + */ +/* +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} +*/ +/* + * ------------------------------------------------------------ + * 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(flags); cli(); +/* + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + disable_uart_tx_interrupt(info->line); + 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(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } +/* + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + 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 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(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + //int lscr_status; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_RX : LEDMAN_COM2_RX); +#endif + *status = serial_inp(info, COM_LSR); + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, COM_RX); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + // + // For statistics only + // + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + // + // We do the SysRQ and SAK checking + // here because otherwise the break + // may get masked by ignore_status_mask + // or read_status_mask. + // +#if defined(CONFIG_SERIAL_WINBOND_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + // + // Mask off conditions which should be ignored. + // + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE + if (info->line == sercons.index) { + // Recover the break flag from console xmit + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } 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; + } +#if defined(CONFIG_SERIAL_WINBOND_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + (tty->flip.count < TTY_FLIPBUF_SIZE)) { + // + // Overrun is special, since it's reported + // immediately, and doesn't affect the current + // character + // + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + } +#if defined(CONFIG_SERIAL_WINBOND_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, COM_LSR); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + +#if (LINUX_VERSION_CODE > 131394) // 2.1.66 + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_TX : LEDMAN_COM2_TX); +#endif + + if (info->x_char) { + serial_outp(info, COM_TX, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt(info->line); + return; + } + + count = info->xmit_fifo_size; + do { + while(!(serial_inp(info, COM_LSR)&UART_LSR_THRE)); + serial_out(info, COM_TX, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + while(!(serial_inp(info, COM_LSR)&UART_LSR_THRE)); + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < 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.head == info->xmit.tail) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt(info->line); + } +} + +/* +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + // update input line counters + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + 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("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + 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); + } + } + } +} +*/ +#ifdef CONFIG_SERIAL_SHARE_IRQ +/* + * 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; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + end_mark = 0; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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); +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ + + +/* + * 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; + struct async_struct * info; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + status = serial_inp(info, COM_IIR); + do { + +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif +#if 1 + if (status & UART_IIR_DR) + receive_chars(info, &status, regs); +// 1/28/2003 Winbond W90N745 doesn't have modem relate registers +// check_modem_status(info); + + if (status & UART_IIR_THRE) + transmit_chars(info, 0); +#endif + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + } while (((status = serial_inp(info, COM_IIR)) & (UART_IIR_DR | UART_IIR_THRE))); +// } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_MULTIPORT +/* + * 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); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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]; + // + // The user was a bonehead, and misconfigured their + // multiport info. Rather than lock up the kernel + // in an infinite loop, if we loop too many times, + // print a message and break out of the loop. + // + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->state->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 +} +*/ +#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 (test_and_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); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * 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(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + if (info->next_port) { + do { + serial_out(info, COM_IER, 0); + info->IER |= UART_IER_THRI; + serial_out(info, COM_IER, info->IER); + info = info->next_port; + } while (info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[i].port1) + rs_interrupt_multi(i, NULL, NULL); + else +#endif + rs_interrupt(i, NULL, NULL); + } else +#endif // CONFIG_SERIAL_SHARE_IRQ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + rs_interrupt(0, NULL, NULL); +#else + rs_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} +/* + * --------------------------------------------------------------- + * 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. + * --------------------------------------------------------------- + */ + +/* + * 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 > 3) ? timeout-2 : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +/* +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} +*/ +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +/* +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +*/ +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + 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, state->irq); +#endif +/* + if (uart_config[state->type].flags & UART_STARTECH) { + // Wake up UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + // + // Turn off LCR == 0xBF so we actually set the IER + // register on the XR16C850 + // + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, 0); + // + // Now reset LCR so we can turn off the ECB bit + // + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + // + // For a XR16C850, we need to set the trigger levels + // + if (state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } + serial_outp(info, UART_LCR, 0); + } + + if (state->type == PORT_16750) { + // Wake up UART + serial_outp(info, UART_IER, 0); + } + + if (state->type == PORT_16C950) { + // Wake up and initialize UART + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); // Reset the UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // If this is an RSA port, see if we can kick it up to the + // higher speed clock. + // + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif +*/ +/* + // + // Clear the FIFO buffers and disable them + // (they will be reenabled in change_speed()) + // + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } +*/ +/* + // + // 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); +*/ + +/* + // + // 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 (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", state->line); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } +*/ + // + // Allocate the IRQ if necessary + // + + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif // CONFIG_SERIAL_SHARE_IRQ + } else + handler = rs_interrupt_single; + + // Mac Wang register rx and tx seperatly + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + // + // Insert serial port into IRQ chain. + // + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + IRQ_ports[state->irq - 1] = info; + figure_IRQ_timeout(state->irq - 1); + + // + // Now, initialize the UART + // +/* + serial_outp(info, UART_LCR, UART_LCR_WLEN8); // reset DLAB + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_outp(info, UART_MCR, info->MCR); +*/ + +#if 0 + //serial_outp(info,COM_IER, 0x00); /* RDA interrupt source on / LOOP disable */ +// serial_outp(info,COM_LCR, 0x80); /* select divisor latch registers */ + +// serial_outp(info,COM_DLL, 6); +// serial_outp(info,COM_DLM, 0); + +// serial_outp(info,COM_LCR, 0x03); /* none parity, 8 data bits, 1 stop bits */ +// serial_outp(info,COM_MCR, 0x00); + + serial_outp(info,COM_TOR, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + //serial_outp(info,COM_FCR, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ +#endif + + // + // Finally, enable interrupts + // +/* + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); // enable interrupts +*/ + serial_outp(info,COM_TOR, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + CSR_WRITE (AIC_SCR9, 0x41); /* high-level sensitive, priority level 1 * lsshi 2005-4-20 11:41 */ + CSR_WRITE (AIC_MECR, 0x40); /* trun on bit 6 */ + enable_uart_rx_interrupt(info->line) ; + +#ifdef CONFIG_SERIAL_MANY_PORTS + 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); + } +#endif + + // + // 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.head = info->xmit.tail = 0; + + // + // Set up serial timers... + // + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + // + // Set up the tty->alt_speed kludge + // +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + // + // and set the speed of the serial port + // + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * 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; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); // 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[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + // + // Free the IRQ, if necessary + // + // rx + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } +/* + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + if (IRQ_ports[state->irq - 1]) { + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + retval = request_irq(state->irq - 1, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq - 1]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + } +*/ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + +/* + info->IER = 0; + serial_outp(info, UART_IER, 0x00); // disable all intrs +*/ + disable_uart_rx_interrupt(info->line); + disable_uart_tx_interrupt(info->line); +/* +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // reset interrupts on the AST Fourport board + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask +*/ +/* + // disable break condition + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + // disable FIFO's + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // Reset the RSA board back to 115kbps compat mode. + // + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif +*/ + + (void)serial_in(info, COM_RX); // read data port to reset things + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + // Arrange to enter sleep mode + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + // Arrange to enter sleep mode + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +*/ + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; +/* +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + 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; + } + return baud_table[i]; +} +*/ +#endif + +/* + * 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, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; +// unsigned cflag, cval, fcr = 0; + unsigned cflag, cval; + int bits; + unsigned long flags; + unsigned int div; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + // byte size and parity + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + // Never happens, but GCC is too dumb to figure it out + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + bits++; + } + if (cflag & PARODD) + cval |= UART_LCR_OPAR; + else if (cflag & PARENB) + cval |= UART_LCR_EPAR; + else + cval |= UART_LCR_NPAR; +/* + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif +*/ + // Determine divisor based on baud rate + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; // B0 transition handled in rs_set_termios +/* +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif +*/ + baud_base = info->state->baud_base; +/* + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } +*/ +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + quot = 1; //tricky + + // If the quotient is zero refuse the change + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + } + // As a last resort, if the quotient is zero, default to 9600 bps + if (!quot) + quot = baud_base / 9600; + // + // Work around a bug in the Oxford Semiconductor 952 rev B + // chip which causes it to seriously miscalculate baud rates + // when DLL is 0. + // +/* + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; +*/ + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; // Add .02 seconds of slop + +/* + // Set up FIFO's + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->state->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + // CTS flow control flag and modem status interrupts + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + 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; + + // + // Characters to ignore + // + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_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; + } +#if 0 // breaks serial console during boot stage + // + // !!! ignore all characters if CREAD is not set + // + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; +#endif + save_flags(flags); cli(); +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } +*/ +/* + 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 +*/ +/* + if (info->state->type == PORT_16750) + serial_outp(info, UART_FCR, fcr); // set fcr +*/ +// serial_outp(info, UART_LCR, cval); // reset DLAB + info->LCR = cval; // Save LCR +/* + if (info->state->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + // emulated UARTs (Lucent Venus 167x) need two steps + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(info, UART_FCR, fcr); // set fcr + } +*/ +#if 1 + serial_outp (info,COM_LCR, 0x80); /* select divisor latch registers */ + + div=baudrate_div(baud); + serial_outp (info,COM_DLL, div&0xFF); + serial_outp (info,COM_DLM, (div>>8)&0xFF); + + serial_outp (info,COM_LCR, cval); /* none parity, 8 data bits, 1 stop bits */ +#endif + restore_flags(flags); +} + +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(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + 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.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 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; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + //restore_flags(flags);//clyu + buf += c; + count -= c; + ret += c; + restore_flags(flags); + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +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 CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(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_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + // Make sure transmit interrupts are on +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt(info->line); + } +} + +/* + * ------------------------------------------------------------ + * 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; + unsigned long flags; +#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)) + rs_send_xchar(tty, STOP_CHAR(tty)); +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#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 + rs_send_xchar(tty, START_CHAR(tty)); + } +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + // Make sure address is not already in use + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + // + // OK, past this point, all the error checking has been done. + // At this point, we start making changes..... + // + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + // + // We need to shutdown the serial port at the old + // port/irq combination. + // + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif +*/ + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } 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; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, COM_LSR); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + // + // If we're about to load something into the transmit + // register, we'll pretend the transmitter isn't empty to + // avoid a race condition (depending on when the transmit + // interrupt happens). + // + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +/* +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((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); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} +*/ +/* +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} +*/ +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +/* +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +*/ +#else +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, COM_LCR, info->LCR); + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +/* +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->state->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->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + 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; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->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[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +*/ +#endif +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; // kernel counter temps + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + int retval, tmp; +#endif + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + 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 (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); // 1/4 second + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: // support for POSIX tcsendbreak() + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif +/* + case TIOCMGET: + 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: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: // Get line status register + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + // + // 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: + save_flags(flags); cli(); + // note the counters on entry + cprev = info->state->icount; + restore_flags(flags); + // Force modem status interrupts on +/* + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); +*/ + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + // see if a signal did it + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; // atomic copy + restore_flags(flags); + 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: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + // "setserial -W" is called in Debian boot + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + 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; +// unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + +/* + // Handle transition to B0 status + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + // Handle transition away from B0 status + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } +*/ + + // Handle turning off CRTSCTS + 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; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + 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, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + // + // Uh, oh. tty->count is 1, which means that the tty + // structure will be freed. state->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, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + // + // Save the termios structure, since this port may have + // separate termios for callout and dialin. + // + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->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); + enable_uart_rx_interrupt(info->line); + // + // Before we drop DTR, make sure the UART transmitter + // has completely drained; this is especially + // important if there is a transmit FIFO! + // + rs_wait_until_sent(tty, info->timeout); + } + 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) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + 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; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; // Just in case.... + + orig_jiffies = jiffies; + // + // Set the check interval to be 1/5 of the estimated time to + // send a single character, and make it at least 1. The check + // interval should also be less than the timeout. + // + // Note: we have to use pretty tight timings here to satisfy + // the NIST-PCTS. + // + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + // + // If the transmitter hasn't cleared in twice the approximate + // amount of time to send the entire FIFO, it probably won't + // ever clear. This assumes the UART isn't doing flow + // control, which is currently the case. Hence, if it ever + // takes longer than info->timeout, this is probably due to a + // UART bug of some kind. So, we clamp the timeout parameter at + // 2*info->timeout. + // + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, COM_LSR)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->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) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + // + // 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 + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -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 (state->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, state->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", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); +*/ + restore_flags(flags); + set_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; +*/ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + 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. + */ +struct tty_struct *key_tty=NULL; //lsshi add +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + key_tty=tty; //lsshi add 2004-3-4 11:02 + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + // + // This relies on lock_kernel() stuff so wants tidying for 2.5 + // + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + // + // If the port is the middle of closing, bail out now + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + info->closing_wait = 3000; + // + // Start up serial port + // + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + 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 + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; +// char stat_buf[30], control, status; + char stat_buf[30]; + int ret; + //unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + // + // Figure out the current RS-232 lines + // + if (!info) { + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + stat_buf[0] = 0; + stat_buf[1] = 0; +/* + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); +*/ + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + // + // Last thing is the RS-232 status lines + // + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * 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 char serial_options[] __initdata = +#ifdef CONFIG_HUB6 + " HUB-6" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MANY_PORTS + " MANY_PORTS" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MULTIPORT + " MULTIPORT" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_SHARE_IRQ + " SHARE_IRQ" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_DETECT_IRQ + " DETECT_IRQ" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PCI + " SERIAL_PCI" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PNP + " ISAPNP" +#define SERIAL_OPT +#endif +#ifdef SERIAL_OPT + " enabled\n"; +#else + " no serial options enabled\n"; +#endif +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + +/* + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. + */ +static unsigned detect_uart_irq (struct serial_state * state) +{ +/* + int irq; + unsigned long irqs; + unsigned char save_mcr, save_ier; + struct async_struct scr_info; // serial_{in,out} because HUB6 + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP=0; // no warning + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif + scr_info.io_type = state->io_type; + scr_info.iomem_base = state->iomem_base; + scr_info.iomem_reg_shift = state->iomem_reg_shift; + + // forget possible initially masked and pending IRQ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); + save_ier = serial_inp(&scr_info, UART_IER); + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); // enable all intrs + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(&scr_info, UART_MCR, save_mcr); + serial_outp(&scr_info, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; +*/ + return 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +/* +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} +*/ +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +/* +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + // + // First we check to see if it's an Oxford Semiconductor UART. + // + // If we have to do this here because some non-National + // Semiconductor clone chips lock up if you try writing to the + // LSR register (which serial_icr_read does) + // + if (state->type == PORT_16550A) { + // + // EFR [4] must be set else this test fails + // + // This shouldn't be necessary, but Mike Hudson + // (Exoray@isys.ca) claims that it's needed for 952 + // dual UART's (which are not recommended for new designs). + // + info->ACR = 0; + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); + // Check for Oxford Semiconductor 16C950 + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV) | + (scratch3 << 8); + return; + } + } + + // + // We check for a XR16C850 by setting DLL and DLM to 0, and + // then reading back DLL and DLM. If DLM reads back 0x10, + // then the UART is a XR16C850 and the DLL contains the chip + // revision. If DLM reads back 0x14, then the UART is a + // XR16C854. + // + // + + // Save the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + scratch2 = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; + state->type = PORT_16850; + return; + } + + // Restore the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); + // + // We distinguish between the '654 and the '650 by counting + // how many bytes are in the FIFO. I'm using this for now, + // since that's the technique that was sent to me in the + // serial driver update, but I'm not convinced this works. + // I've had problems doing this in the past. -TYT + // + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} +*/ +/* + * 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 serial_state * state) +{ +// unsigned char status1, status2, scratch, scratch2, scratch3; +// unsigned char save_lcr, save_mcr; + struct async_struct *info, scr_info; + unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + save_flags(flags); cli(); + +/* + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { + // + // 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); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; // We failed; there's nothing here + } + } +*/ +/* + save_mcr = serial_in(info, UART_MCR); + save_lcr = serial_in(info, UART_LCR); +*/ + // + // 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 (!(state->flags & ASYNC_SKIP_TEST)) { + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + state->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(info, UART_LCR, 0xBF); // set up for StarTech test + serial_outp(info, UART_EFR, 0); // EFR is the same as FCR + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; + switch (scratch) { + case 0: + state->type = PORT_16450; + break; + case 1: + state->type = PORT_UNKNOWN; + break; + case 2: + state->type = PORT_16550; + break; + case 3: + state->type = PORT_16550A; + break; + } + if (state->type == PORT_16550A) { + // Check for Startech UART's + serial_outp(info, UART_LCR, UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { + state->type = PORT_16650; + } else { + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) + autoconfig_startech_uarts(info, state, flags); + } + } + if (state->type == PORT_16550A) { + // Check for TI 16750 + serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 7) { + // + // If this is a 16750, and not a cheap UART + // clone, then it should only go into 64 byte + // mode if the UART_FCR7_64BYTE bit was set + // while UART_LCR_DLAB was latched. + // + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 6) + state->type = PORT_16750; + } + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif + serial_outp(info, UART_LCR, save_lcr); + if (state->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)) + state->type = PORT_8250; + } +*/ + state->type = PORT_W90N745; + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif +*/ + request_region(info->port,8,"serial(auto)"); + } + + // + // Reset the UART. + // +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ + + serial_outp (info,COM_LCR, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL, 6); + serial_outp (info,COM_DLM, 0); + + serial_outp (info,COM_LCR, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + //serial_outp (info,COM_IER, 0x03); /* RDA interrupt source on / LOOP disable */ + serial_outp (info,COM_MCR, 0x00); + + //serial_outp (info,COM_TOR, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + serial_outp (info,COM_FCR, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ + + + restore_flags(flags); +} + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + + +#if (LINUX_VERSION_CODE > 0x20100) +//EXPORT_SYMBOL(register_serial); +//EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include + X(register_serial), + X(unregister_serial), +#include +}; +#endif + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + memset(&rs_multiport[i], 0, + sizeof(struct rs_multiport_struct)); +#endif + } +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE + // + // The interrupt of the serial console port + // can't be shared. + // + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + // Initialize the tty_driver structure + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial"; +#endif +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 64 + SERIAL_DEV_OFFSET; + 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 = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + 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; +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + // + // The callout device is just like normal device except for + // major number and the subtype code. + // + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + 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, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) + continue; +#ifdef CONFIG_MCA + if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) + continue; +#endif + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(state); + } + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif +#ifdef ENABLE_SERIAL_PNP + probe_serial_pnp(); +#endif + return 0; +} +/* + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). + */ +/* +int __init early_serial_setup(struct serial_struct *req) +{ + int i = req->line; + + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); +} +*/ +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +/* +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } +#ifdef __i386__ + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + 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; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) + state->irq = detect_uart_irq(state); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} +*/ +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ +/* +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + // These will be hidden, because they are devices that will no longer + // be available to the system. (ie, PCMCIA modems, once ejected) + // + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} +*/ +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + // printk("Unloading %s: version %s\n", serial_name, serial_version); + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + 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 ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(rs_table[i].port, 8); + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif +*/ + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev == 0) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +#endif +*/ + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +/* +#ifdef ENABLE_SERIAL_PCI + if (serial_pci_driver.name[0]) + pci_unregister_driver (&serial_pci_driver); +#endif +*/ +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); +MODULE_AUTHOR("Theodore Ts'o "); +MODULE_LICENSE("GPL"); + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_WINBOND_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, COM_LSR); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here.- + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + unsigned i; + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, COM_TX, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, COM_TX, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity/flow control. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + { + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + } + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + /* + serial_out(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_out(info, UART_DLL, quot & 0xff); // LS of divisor + serial_out(info, UART_DLM, quot >> 8); // MS of divisor + serial_out(info, UART_LCR, cval); // reset DLAB + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + */ + /* + * If we read 0xff from the LSR, there is no UART here. + */ + /* + if (serial_in(info, UART_LSR) == 0xff) + return -1; + */ + serial_outp (info,COM_LCR, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL, 6); + serial_outp (info,COM_DLM, 0); + + serial_outp (info,COM_LCR, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init winbond_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c new file mode 100755 index 0000000..120f7dd --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c @@ -0,0 +1,4719 @@ +/* + * linux/drivers/char/w90n745_uart_1.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * 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 + * + * 03/96: Modularised by Angelo Haritsis + * + * rs_set_termios fixed to look also for changes of the input + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. + * Bernd Anhäupl 05/17/96. + * + * 1/97: Extended dumb serial ports are a config option now. + * Saves 4k. Michael A. Griffith + * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * suppress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * + * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a + * few races on freeing buffers too. + * Alan Modra + * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton + * + * 7/00: Support Timedia/Sunix/Exsys PCI cards + * + * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. + * Arnaldo Carvalho de Melo + * + * 10/00: add in optional software flow control for serial console. + * Kanoj Sarcar (Modified by Theodore Ts'o) + * + * 08/30/2005:Porting to Winbond uart + * + */ + +static char *serial_version = "1.0"; +static char *serial_revdate = "2005-08-15"; + +/* + * Serial driver configuration section. Here are the various options: + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. + * + * CONFIG_SERIAL_MANY_PORTS + * Enables support for ports beyond the standard, stupid + * COM 1/2/3/4. + * + * CONFIG_SERIAL_MULTIPORT + * Enables support for special multiport board support. + * + * CONFIG_SERIAL_SHARE_IRQ + * Enables support for multiple serial ports on one IRQ + * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +//#define GP1_IS_DTR + +#if 0 +/* These defines are normally controlled by the autoconf.h */ +#define CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ +#define CONFIG_SERIAL_MULTIPORT +#define CONFIG_HUB6 +#endif + +#ifndef CONFIG_BOARD_W90N745 +#ifdef CONFIG_PCI +#define ENABLE_SERIAL_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +//#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 131343) +#include +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include +#endif +#include +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE +#include +#endif +#ifdef ENABLE_SERIAL_PCI +#include +#endif +#ifdef ENABLE_SERIAL_PNP +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif +#ifdef CONFIG_LEDMAN +#include +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +#include +#include +#include +#include + +#include + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Winbond W90N7451 Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * 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]; +#ifdef CONFIG_SERIAL_MULTIPORT +static struct rs_multiport_struct rs_multiport[NR_IRQS]; +#endif +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_SERIAL_W90N7451_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static unsigned detect_uart_irq (struct serial_state * state); +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, // usurped by cyclades.c + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, // usurped by cyclades.c + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "W90N7451", 1, 0}, /* Winbond W90N745 uart 3 support */ + { 0, 0} +}; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { +// SERIAL_PORT_DFNS // Defined in serial.h +{ 0, 115200, UART_BASE1, INT_UART1, STD_COM_FLAGS },/* ttyS1 */ +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +#define NR_PCI_BOARDS 8 + +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif +#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#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 + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user 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; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +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; +} + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return CSR_READ( offset); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, + int value) +{ +#if 0 + printk("offset :%x\n",offset); + if(value!='A') + printk("value :%x\n",value); + else + printk("value :%c\n",value); +#endif + CSR_WRITE(offset, value); +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * For the 16C950 + */ +/* +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} +*/ +/* + * ------------------------------------------------------------ + * 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(flags); cli(); + + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + } + + /* + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } + */ + + disable_uart_tx_interrupt_1(info->line); + 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(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + //printk("3\n"); + enable_uart_tx_interrupt_1(info->line); + } +/* + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + 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 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(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + //int lscr_status; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_RX : LEDMAN_COM2_RX); +#endif + *status = serial_inp(info, COM_LSR_1); + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, COM_RX_1); + //printk("-"); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + // + // For statistics only + // + //printk("-"); + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + // + // We do the SysRQ and SAK checking + // here because otherwise the break + // may get masked by ignore_status_mask + // or read_status_mask. + // +#if defined(CONFIG_SERIAL_W90N7451_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + // + // Mask off conditions which should be ignored. + // + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE + if (info->line == sercons.index) { + // Recover the break flag from console xmit + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } 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; + } +#if defined(CONFIG_SERIAL_W90N7451_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + (tty->flip.count < TTY_FLIPBUF_SIZE)) { + // + // Overrun is special, since it's reported + // immediately, and doesn't affect the current + // character + // + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + } +#if defined(CONFIG_SERIAL_W90N7451_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, COM_LSR_1); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + +#if (LINUX_VERSION_CODE > 131394) // 2.1.66 + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + int d = 8; //yachen + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_TX : LEDMAN_COM2_TX); +#endif + //printk("!"); + if (info->x_char) { + //printk("%02x\n", info->x_char); //==================> + //printk("!"); + serial_outp(info, COM_TX_1, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { + info->IER &= ~UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + disable_uart_tx_interrupt_1(info->line); + return; + } + + count = info->xmit_fifo_size; + do { + //while(!(serial_inp(info, COM_LSR_1)&UART_LSR_THRE)); + //printk("%02x ", info->xmit.buf[info->xmit.tail]); //==================> + serial_out(info, COM_TX_1, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (/*--count*/ --d > 0); + //printk("\n"); //==================> + //while(!(serial_inp(info, COM_LSR_1)&UART_LSR_THRE)); + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < 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.head == info->xmit.tail) { + info->IER &= ~UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + disable_uart_tx_interrupt_1(info->line); + } +} + + +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + //struct async_icount *icount; + + status = serial_in(info, /*UART_MSR*/ COM_MSR_1); +/* + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + // update input line counters + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + 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("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } +*/ + if (info->flags & ASYNC_CTS_FLOW) { + if (info->tty->hw_stopped) { + if (status & /*UART_MSR_CTS*/ 0x10) { +#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); + enable_uart_tx_interrupt_1(info->line); //========> + rs_sched_event(info, RS_EVENT_WRITE_WAKEUP); + return; + } + } else { + if (!(status & /*UART_MSR_CTS*/ 0x10)) { +#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); + disable_uart_tx_interrupt_1(info->line); //=========> + } + } + } +} + +#ifdef CONFIG_SERIAL_SHARE_IRQ +/* + * 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; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + end_mark = 0; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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); +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ + +#define UART_IIR_MODEM_STATUS_CHANGE 0x00 +/* + * 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; + struct async_struct * info; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + status = serial_inp(info, COM_IIR_1); + do { + +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif +#if 1 + if (status & UART_IIR_DR) + receive_chars(info, &status, regs); + if((status & 0xf )== UART_IIR_MODEM_STATUS_CHANGE) + check_modem_status(info); //==========> + + if (status & UART_IIR_THRE) + transmit_chars(info, 0); +#endif + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + } while (((status = serial_inp(info, COM_IIR_1)) & (UART_IIR_DR | UART_IIR_THRE))); +// } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_MULTIPORT +/* + * 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); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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]; + // + // The user was a bonehead, and misconfigured their + // multiport info. Rather than lock up the kernel + // in an infinite loop, if we loop too many times, + // print a message and break out of the loop. + // + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->state->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 +} +*/ +#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 (test_and_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); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * 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(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + if (info->next_port) { + do { + serial_out(info, COM_IER_1, 0); + info->IER |= UART_IER_THRI; + serial_out(info, COM_IER_1, info->IER); + info = info->next_port; + } while (info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[i].port1) + rs_interrupt_multi(i, NULL, NULL); + else +#endif + rs_interrupt(i, NULL, NULL); + } else +#endif // CONFIG_SERIAL_SHARE_IRQ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + rs_interrupt(0, NULL, NULL); +#else + rs_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} +/* + * --------------------------------------------------------------- + * 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. + * --------------------------------------------------------------- + */ + +/* + * 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 > 3) ? timeout-2 : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +/* +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} +*/ +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +/* +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +*/ +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + 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, state->irq); +#endif +/* + if (uart_config[state->type].flags & UART_STARTECH) { + // Wake up UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + // + // Turn off LCR == 0xBF so we actually set the IER + // register on the XR16C850 + // + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, 0); + // + // Now reset LCR so we can turn off the ECB bit + // + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + // + // For a XR16C850, we need to set the trigger levels + // + if (state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } + serial_outp(info, UART_LCR, 0); + } + + if (state->type == PORT_16750) { + // Wake up UART + serial_outp(info, UART_IER, 0); + } + + if (state->type == PORT_16C950) { + // Wake up and initialize UART + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); // Reset the UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // If this is an RSA port, see if we can kick it up to the + // higher speed clock. + // + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif +*/ +/* + // + // Clear the FIFO buffers and disable them + // (they will be reenabled in change_speed()) + // + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } +*/ +/* + // + // 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); +*/ + +/* + // + // 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 (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", state->line); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } +*/ + // + // Allocate the IRQ if necessary + // + + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif // CONFIG_SERIAL_SHARE_IRQ + } else + handler = rs_interrupt_single; + + // Mac Wang register rx and tx seperatly + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial1", &IRQ_ports[state->irq]); + + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + // + // Insert serial port into IRQ chain. + // + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + IRQ_ports[state->irq - 1] = info; + figure_IRQ_timeout(state->irq - 1); + + // + // Now, initialize the UART + // + // julian + // un-marked the following for modem initialization + // change UART_LCR to COM_LCR_1 + // change UART_MCR to COM_MCR_1 + + serial_outp(info, /* UART_LCR */ COM_LCR_1, UART_LCR_WLEN8); // reset DLAB + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_outp(info, /* UART_MCR */ COM_MCR_1, info->MCR); + +#if 0 + //serial_outp(info,COM_IER_1, 0x00); /* RDA interrupt source on / LOOP disable */ +// serial_outp(info,COM_LCR_1, 0x80); /* select divisor latch registers */ + +// serial_outp(info,COM_DLL_1, 6); +// serial_outp(info,COM_DLM_1, 0); + +// serial_outp(info,COM_LCR_1, 0x03); /* none parity, 8 data bits, 1 stop bits */ +// serial_outp(info,COM_MCR_1, 0x00); + + serial_outp(info,COM_TOR_1, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + //serial_outp(info,COM_FCR_1, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ +#endif + + // + // Finally, enable interrupts + // +/* + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); // enable interrupts +*/ + serial_outp(info,COM_TOR_1, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + CSR_WRITE (AIC_SCR9, 0x41); /* high-level sensitive, priority level 1 * lsshi 2005-4-20 11:41 */ + CSR_WRITE (AIC_MECR, 0x40); /* trun on bit 6 */ + enable_uart_rx_interrupt_1(info->line) ; + +#ifdef CONFIG_SERIAL_MANY_PORTS + 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); + } +#endif + + // + // 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.head = info->xmit.tail = 0; + + // + // Set up serial timers... + // + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + // + // Set up the tty->alt_speed kludge + // +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + // + // and set the speed of the serial port + // + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * 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; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); // 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[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + // + // Free the IRQ, if necessary + // + // rx + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial1", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } +/* + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + if (IRQ_ports[state->irq - 1]) { + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + retval = request_irq(state->irq - 1, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq - 1]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + } +*/ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + +/* + info->IER = 0; + serial_outp(info, UART_IER, 0x00); // disable all intrs +*/ + disable_uart_rx_interrupt_1(info->line); + disable_uart_tx_interrupt_1(info->line); +/* +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // reset interrupts on the AST Fourport board + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask +*/ +/* + // disable break condition + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + // disable FIFO's + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // Reset the RSA board back to 115kbps compat mode. + // + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif +*/ + + (void)serial_in(info, COM_RX_1); // read data port to reset things + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + // Arrange to enter sleep mode + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + // Arrange to enter sleep mode + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +*/ + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; +/* +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + 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; + } + return baud_table[i]; +} +*/ +#endif + +/* + * 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, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; +// unsigned cflag, cval, fcr = 0; + unsigned cflag, cval; + int bits; + unsigned long flags; + unsigned int div; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + // byte size and parity + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + // Never happens, but GCC is too dumb to figure it out + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + bits++; + } + if (cflag & PARODD) + cval |= UART_LCR_OPAR; + else if (cflag & PARENB) + cval |= UART_LCR_EPAR; + else + cval |= UART_LCR_NPAR; +/* + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif +*/ + // Determine divisor based on baud rate + baud = tty_get_baud_rate(info->tty); +// printk("baud:%d\n",baud); + if (!baud) + baud = 9600; // B0 transition handled in rs_set_termios +/* +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif +*/ + baud_base = info->state->baud_base; +/* + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } +*/ +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + quot = 1; //tricky + + // If the quotient is zero refuse the change + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + } + // As a last resort, if the quotient is zero, default to 9600 bps + if (!quot) + quot = baud_base / 9600; + // + // Work around a bug in the Oxford Semiconductor 952 rev B + // chip which causes it to seriously miscalculate baud rates + // when DLL is 0. + // +/* + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; +*/ + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; // Add .02 seconds of slop + +/* + // Set up FIFO's + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->state->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + // CTS flow control flag and modem status interrupts + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + info->IER |= UART_IER_MSI; +*/ + if (cflag & CRTSCTS) { + info->flags |= ASYNC_CTS_FLOW; + //info->IER |= UART_IER_MSI; + CSR_WRITE(COM_IER_1, CSR_READ(COM_IER_1)|0x08); //===========> + if(CSR_READ(COM_MSR_1) & 0x10) { + //printk("clear to send"); + info->tty->hw_stopped = 0; + }else + info->tty->hw_stopped = 1; + } else { + info->flags &= ~ASYNC_CTS_FLOW; + CSR_WRITE(COM_IER_1, CSR_READ(COM_IER_1)&0x17); //===========> + info->tty->hw_stopped = 0; //===========> + } +/* + 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; + + // + // Characters to ignore + // + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_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; + } +#if 0 // breaks serial console during boot stage + // + // !!! ignore all characters if CREAD is not set + // + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; +#endif + save_flags(flags); cli(); +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } +*/ +/* + 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 +*/ +/* + if (info->state->type == PORT_16750) + serial_outp(info, UART_FCR, fcr); // set fcr +*/ +// serial_outp(info, UART_LCR, cval); // reset DLAB + info->LCR = cval; // Save LCR +/* + if (info->state->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + // emulated UARTs (Lucent Venus 167x) need two steps + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(info, UART_FCR, fcr); // set fcr + } +*/ +/* + outp(COM_LSB, 6); // Baud rate = 115200 = 15,000,000/(16*(6+2)) +*/ + serial_outp (info,COM_LCR_1, 0x80); /* select divisor latch registers */ + div=baudrate_div(baud); + serial_outp (info,COM_DLL_1, div&0xFF); + serial_outp (info,COM_DLM_1, (div>>8)&0xFF); + + serial_outp (info,COM_LCR_1, cval); /* none parity, 8 data bits, 1 stop bits */ + + restore_flags(flags); +} + +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(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + 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; + save_flags(flags); cli(); + if (info->xmit.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) { + restore_flags(flags); + return; + } + save_flags(flags); cli(); + info->IER |= UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + //printk("1\n"); + enable_uart_tx_interrupt_1(info->line); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + //restore_flags(flags);//clyu + buf += c; + count -= c; + ret += c; + restore_flags(flags); + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + save_flags(flags); cli(); + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + //printk("2\n"); + enable_uart_tx_interrupt_1(info->line); + } + restore_flags(flags); + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +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 CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(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_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; +#if 1 + if (ch) { + // Make sure transmit interrupts are on + info->IER |= UART_IER_THRI; + //serial_out(info, UART_IER, info->IER); + enable_uart_tx_interrupt_1(info->line); + } +#endif +} + +/* + * ------------------------------------------------------------ + * 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; + unsigned long flags; +#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)) + rs_send_xchar(tty, STOP_CHAR(tty)); + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; + + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + CSR_WRITE(COM_MCR_1, 0); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#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 + rs_send_xchar(tty, START_CHAR(tty)); + } + + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; + + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + CSR_WRITE(COM_MCR_1, 2); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + // Make sure address is not already in use + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + // + // OK, past this point, all the error checking has been done. + // At this point, we start making changes..... + // + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + // + // We need to shutdown the serial port at the old + // port/irq combination. + // + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif +*/ + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } 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; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, COM_LSR_1); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + // + // If we're about to load something into the transmit + // register, we'll pretend the transmitter isn't empty to + // avoid a race condition (depending on when the transmit + // interrupt happens). + // + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, COM_MSR_1); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +//#ifdef TIOCM_OUT1 +// | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) +// | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +//#endif +// | ((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); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +//#ifdef TIOCM_OUT1 +// if (arg & TIOCM_OUT1) +// info->MCR |= UART_MCR_OUT1; +// if (arg & TIOCM_OUT2) +// info->MCR |= UART_MCR_OUT2; +//#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +//#ifdef TIOCM_OUT1 +// if (arg & TIOCM_OUT1) +// info->MCR &= ~UART_MCR_OUT1; +// if (arg & TIOCM_OUT2) +// info->MCR &= ~UART_MCR_OUT2; +//#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +//#ifdef TIOCM_OUT1 +// UART_MCR_OUT1 | +// UART_MCR_OUT2 | +//#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +//#ifdef TIOCM_OUT1 +// | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) +// | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +//#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); +// info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + + + serial_out(info, COM_MCR_1, info->MCR & 0x12); // mask out reserved bits +#ifdef GP1_IS_DTR + if(info->MCR & UART_MCR_DTR) // clear to 0 + *(unsigned int volatile *)(0xfff83008) &= 0x1d; + else + *(unsigned int volatile *)(0xfff83008) |= 0x2; +#endif + restore_flags(flags); + return 0; +} + +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +/* +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +*/ +#else +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, COM_LCR_1, info->LCR); + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +/* +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->state->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->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + 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; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->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[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +*/ +#endif +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; // kernel counter temps + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + int retval, tmp; +#endif + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + 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 (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); // 1/4 second + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: // support for POSIX tcsendbreak() + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif + + case TIOCMGET: + 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: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: // Get line status register + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + // + // 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: + save_flags(flags); cli(); + // note the counters on entry + cprev = info->state->icount; + restore_flags(flags); + // Force modem status interrupts on +/* + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); +*/ + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + // see if a signal did it + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; // atomic copy + restore_flags(flags); + 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: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + // "setserial -W" is called in Debian boot + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + 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; +// unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + +/* + // Handle transition to B0 status + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + // Handle transition away from B0 status + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } +*/ + + // Handle turning off CRTSCTS + 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; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + 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, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + // + // Uh, oh. tty->count is 1, which means that the tty + // structure will be freed. state->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, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + // + // Save the termios structure, since this port may have + // separate termios for callout and dialin. + // + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->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); + enable_uart_rx_interrupt_1(info->line); + // + // Before we drop DTR, make sure the UART transmitter + // has completely drained; this is especially + // important if there is a transmit FIFO! + // + rs_wait_until_sent(tty, info->timeout); + } + 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) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + 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; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; // Just in case.... + + orig_jiffies = jiffies; + // + // Set the check interval to be 1/5 of the estimated time to + // send a single character, and make it at least 1. The check + // interval should also be less than the timeout. + // + // Note: we have to use pretty tight timings here to satisfy + // the NIST-PCTS. + // + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + // + // If the transmitter hasn't cleared in twice the approximate + // amount of time to send the entire FIFO, it probably won't + // ever clear. This assumes the UART isn't doing flow + // control, which is currently the case. Hence, if it ever + // takes longer than info->timeout, this is probably due to a + // UART bug of some kind. So, we clamp the timeout parameter at + // 2*info->timeout. + // + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, COM_LSR_1)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->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) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + // + // 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 + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -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 (state->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, state->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", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); +*/ + restore_flags(flags); + set_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; +*/ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + 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. + */ +// struct tty_struct *key_tty=NULL; //mcli +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + //key_tty=tty; //mcli + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + // + // This relies on lock_kernel() stuff so wants tidying for 2.5 + // + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + // + // If the port is the middle of closing, bail out now + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + info->closing_wait = 3000; + // + // Start up serial port + // + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + 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 + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; +// char stat_buf[30], control, status; + char stat_buf[30]; + int ret; + //unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + // + // Figure out the current RS-232 lines + // + if (!info) { + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + stat_buf[0] = 0; + stat_buf[1] = 0; +/* + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); +*/ + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + // + // Last thing is the RS-232 status lines + // + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +static int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * 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 char serial_options[] __initdata = +#ifdef CONFIG_HUB6 + " HUB-6" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MANY_PORTS + " MANY_PORTS" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MULTIPORT + " MULTIPORT" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_SHARE_IRQ + " SHARE_IRQ" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_DETECT_IRQ + " DETECT_IRQ" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PCI + " SERIAL_PCI" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PNP + " ISAPNP" +#define SERIAL_OPT +#endif +#ifdef SERIAL_OPT + " enabled\n"; +#else + " no serial options enabled\n"; +#endif +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + +/* + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. + */ +static unsigned detect_uart_irq (struct serial_state * state) +{ +/* + int irq; + unsigned long irqs; + unsigned char save_mcr, save_ier; + struct async_struct scr_info; // serial_{in,out} because HUB6 + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP=0; // no warning + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif + scr_info.io_type = state->io_type; + scr_info.iomem_base = state->iomem_base; + scr_info.iomem_reg_shift = state->iomem_reg_shift; + + // forget possible initially masked and pending IRQ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); + save_ier = serial_inp(&scr_info, UART_IER); + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); // enable all intrs + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(&scr_info, UART_MCR, save_mcr); + serial_outp(&scr_info, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; +*/ + return 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +/* +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} +*/ +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +/* +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + // + // First we check to see if it's an Oxford Semiconductor UART. + // + // If we have to do this here because some non-National + // Semiconductor clone chips lock up if you try writing to the + // LSR register (which serial_icr_read does) + // + if (state->type == PORT_16550A) { + // + // EFR [4] must be set else this test fails + // + // This shouldn't be necessary, but Mike Hudson + // (Exoray@isys.ca) claims that it's needed for 952 + // dual UART's (which are not recommended for new designs). + // + info->ACR = 0; + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); + // Check for Oxford Semiconductor 16C950 + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV) | + (scratch3 << 8); + return; + } + } + + // + // We check for a XR16C850 by setting DLL and DLM to 0, and + // then reading back DLL and DLM. If DLM reads back 0x10, + // then the UART is a XR16C850 and the DLL contains the chip + // revision. If DLM reads back 0x14, then the UART is a + // XR16C854. + // + // + + // Save the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + scratch2 = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; + state->type = PORT_16850; + return; + } + + // Restore the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); + // + // We distinguish between the '654 and the '650 by counting + // how many bytes are in the FIFO. I'm using this for now, + // since that's the technique that was sent to me in the + // serial driver update, but I'm not convinced this works. + // I've had problems doing this in the past. -TYT + // + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} +*/ +/* + * 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 serial_state * state) +{ +// unsigned char status1, status2, scratch, scratch2, scratch3; +// unsigned char save_lcr, save_mcr; + struct async_struct *info, scr_info; + unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + save_flags(flags); cli(); + +/* + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { + // + // 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); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; // We failed; there's nothing here + } + } +*/ +/* + save_mcr = serial_in(info, UART_MCR); + save_lcr = serial_in(info, UART_LCR); +*/ + // + // 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 (!(state->flags & ASYNC_SKIP_TEST)) { + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + state->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(info, UART_LCR, 0xBF); // set up for StarTech test + serial_outp(info, UART_EFR, 0); // EFR is the same as FCR + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; + switch (scratch) { + case 0: + state->type = PORT_16450; + break; + case 1: + state->type = PORT_UNKNOWN; + break; + case 2: + state->type = PORT_16550; + break; + case 3: + state->type = PORT_16550A; + break; + } + if (state->type == PORT_16550A) { + // Check for Startech UART's + serial_outp(info, UART_LCR, UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { + state->type = PORT_16650; + } else { + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) + autoconfig_startech_uarts(info, state, flags); + } + } + if (state->type == PORT_16550A) { + // Check for TI 16750 + serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 7) { + // + // If this is a 16750, and not a cheap UART + // clone, then it should only go into 64 byte + // mode if the UART_FCR7_64BYTE bit was set + // while UART_LCR_DLAB was latched. + // + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 6) + state->type = PORT_16750; + } + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif + serial_outp(info, UART_LCR, save_lcr); + if (state->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)) + state->type = PORT_8250; + } +*/ + state->type = PORT_W90N745; + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif +*/ + request_region(info->port,8,"serial(auto)"); + } + + // + // Reset the UART. + // +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ + + serial_outp (info,COM_LCR_1, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_1, 6);//115200 + serial_outp (info,COM_DLM_1, 0); + + serial_outp (info,COM_LCR_1, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + //serial_outp (info,COM_IER_1, 0x03); /* RDA interrupt source on / LOOP disable */ + serial_outp (info,COM_MCR_1, 0x00); + + //serial_outp (info,COM_TOR_1, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + serial_outp (info,COM_FCR_1, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ + + + restore_flags(flags); +} + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + + +#if (LINUX_VERSION_CODE > 0x20100) +//EXPORT_SYMBOL(register_serial); +//EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include + X(register_serial), + X(unregister_serial), +#include +}; +#endif + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + memset(&rs_multiport[i], 0, + sizeof(struct rs_multiport_struct)); +#endif + } +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE + // + // The interrupt of the serial console port + // can't be shared. + // + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + // Initialize the tty_driver structure + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial1"; +#endif +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 65 + SERIAL_DEV_OFFSET; + 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 = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + 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; +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + // + // The callout device is just like normal device except for + // major number and the subtype code. + // + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + 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, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) + continue; +#ifdef CONFIG_MCA + if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) + continue; +#endif + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(state); + } + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif +#ifdef ENABLE_SERIAL_PNP + probe_serial_pnp(); +#endif +//#if 1 //=============> enable CTS/RTS pins +#ifdef CONFIG_UART1_CTSRTS + *(unsigned int volatile *)(0xfff83050) &= 0xfffff0ff; + *(unsigned int volatile *)(0xfff83050) |= 0xa00; + *(unsigned int volatile *)(0xfff80110) = 0xf; +#ifdef GP1_IS_DTR + //============> configure GP1 as DTR + *(unsigned int volatile *)(0xfff83000) &= 0x3f3; + *(unsigned int volatile *)(0xfff83004) |= 2; + *(unsigned int volatile *)(0xfff83008) |= 2; +#endif + //CSR_WRITE(COM_MCR_1, CSR_READ(COM_MCR_1)&0xf); +#endif + + return 0; +} +/* + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). + */ +/* +int __init early_serial_setup(struct serial_struct *req) +{ + int i = req->line; + + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); +} +*/ +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +/* +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } +#ifdef __i386__ + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + 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; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) + state->irq = detect_uart_irq(state); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} +*/ +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ +/* +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + // These will be hidden, because they are devices that will no longer + // be available to the system. (ie, PCMCIA modems, once ejected) + // + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} +*/ +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + // printk("Unloading %s: version %s\n", serial_name, serial_version); + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + 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 ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(rs_table[i].port, 8); + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif +*/ + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev == 0) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +#endif +*/ + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +/* +#ifdef ENABLE_SERIAL_PCI + if (serial_pci_driver.name[0]) + pci_unregister_driver (&serial_pci_driver); +#endif +*/ +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); +MODULE_AUTHOR("Theodore Ts'o "); +MODULE_LICENSE("GPL"); + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_W90N7451_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, COM_LSR_1); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here.- + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + unsigned i; + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + //printk("-"); + serial_out(info, COM_TX_1, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, COM_TX_1, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity/flow control. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + { + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + } + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + /* + serial_out(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_out(info, UART_DLL, quot & 0xff); // LS of divisor + serial_out(info, UART_DLM, quot >> 8); // MS of divisor + serial_out(info, UART_LCR, cval); // reset DLAB + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + */ + /* + * If we read 0xff from the LSR, there is no UART here. + */ + /* + if (serial_in(info, UART_LSR) == 0xff) + return -1; + */ + serial_outp (info,COM_LCR_1, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_1, 6); + serial_outp (info,COM_DLM_1, 0); + + serial_outp (info,COM_LCR_1, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init winbond_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c new file mode 100755 index 0000000..578ecde --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c @@ -0,0 +1,4668 @@ +/* + * linux/drivers/char/w90n745_uart_2.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * 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 + * + * 03/96: Modularised by Angelo Haritsis + * + * rs_set_termios fixed to look also for changes of the input + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. + * Bernd Anhäupl 05/17/96. + * + * 1/97: Extended dumb serial ports are a config option now. + * Saves 4k. Michael A. Griffith + * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * suppress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * + * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a + * few races on freeing buffers too. + * Alan Modra + * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton + * + * 7/00: Support Timedia/Sunix/Exsys PCI cards + * + * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. + * Arnaldo Carvalho de Melo + * + * 10/00: add in optional software flow control for serial console. + * Kanoj Sarcar (Modified by Theodore Ts'o) + * + * 08/30/2005:Porting to Winbond uart + * + */ + +static char *serial_version = "1.0"; +static char *serial_revdate = "2005-08-15"; + +/* + * Serial driver configuration section. Here are the various options: + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. + * + * CONFIG_SERIAL_MANY_PORTS + * Enables support for ports beyond the standard, stupid + * COM 1/2/3/4. + * + * CONFIG_SERIAL_MULTIPORT + * Enables support for special multiport board support. + * + * CONFIG_SERIAL_SHARE_IRQ + * Enables support for multiple serial ports on one IRQ + * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#if 0 +/* These defines are normally controlled by the autoconf.h */ +#define CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ +#define CONFIG_SERIAL_MULTIPORT +#define CONFIG_HUB6 +#endif + +#ifndef CONFIG_BOARD_W90N745 +#ifdef CONFIG_PCI +#define ENABLE_SERIAL_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +//#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 131343) +#include +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include +#endif +#include +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE +#include +#endif +#ifdef ENABLE_SERIAL_PCI +#include +#endif +#ifdef ENABLE_SERIAL_PNP +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif +#ifdef CONFIG_LEDMAN +#include +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +#include +#include +#include +#include + +#include //mcli + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Winbond W90N7452 Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * 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]; +#ifdef CONFIG_SERIAL_MULTIPORT +static struct rs_multiport_struct rs_multiport[NR_IRQS]; +#endif +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_SERIAL_W90N7452_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static unsigned detect_uart_irq (struct serial_state * state); +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, // usurped by cyclades.c + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, // usurped by cyclades.c + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "W90N7452", 1, 0}, /* Winbond W90N745 uart 3 support */ + { 0, 0} +}; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { +// SERIAL_PORT_DFNS // Defined in serial.h +{ 0, 115200, UART_BASE2, INT_UART2, STD_COM_FLAGS },/* ttyS2 */ +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +#define NR_PCI_BOARDS 8 + +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif +#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#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 + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user 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; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +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; +} + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return CSR_READ( offset); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, + int value) +{ +#if 0 + printk("offset :%x\n",offset); + if(value!='A') + printk("value :%x\n",value); + else + printk("value :%c\n",value); +#endif + CSR_WRITE(offset, value); +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * For the 16C950 + */ +/* +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} +*/ +/* + * ------------------------------------------------------------ + * 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(flags); cli(); +/* + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + disable_uart_tx_interrupt_2(info->line); + 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(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_2(info->line); + } +/* + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + 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 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(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + //int lscr_status; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_RX : LEDMAN_COM2_RX); +#endif + *status = serial_inp(info, COM_LSR_2); + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, COM_RX_2); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + // + // For statistics only + // + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + // + // We do the SysRQ and SAK checking + // here because otherwise the break + // may get masked by ignore_status_mask + // or read_status_mask. + // +#if defined(CONFIG_SERIAL_W90N7452_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + // + // Mask off conditions which should be ignored. + // + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE + if (info->line == sercons.index) { + // Recover the break flag from console xmit + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } 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; + } +#if defined(CONFIG_SERIAL_W90N7452_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + (tty->flip.count < TTY_FLIPBUF_SIZE)) { + // + // Overrun is special, since it's reported + // immediately, and doesn't affect the current + // character + // + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + } +#if defined(CONFIG_SERIAL_W90N7452_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, COM_LSR_2); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + +#if (LINUX_VERSION_CODE > 131394) // 2.1.66 + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_TX : LEDMAN_COM2_TX); +#endif + + if (info->x_char) { + serial_outp(info, COM_TX_2, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt_2(info->line); + return; + } + + count = info->xmit_fifo_size; + do { + while(!(serial_inp(info, COM_LSR_2)&UART_LSR_THRE)); + serial_out(info, COM_TX_2, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + while(!(serial_inp(info, COM_LSR_2)&UART_LSR_THRE)); + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < 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.head == info->xmit.tail) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt_2(info->line); + } +} + + +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + // update input line counters + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + 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("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + 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); + } + } + } +} + +#ifdef CONFIG_SERIAL_SHARE_IRQ +/* + * 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; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + end_mark = 0; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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); +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ + + +/* + * 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; + struct async_struct * info; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + status = serial_inp(info, COM_IIR_2); + do { + +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif +#if 1 + if (status & UART_IIR_DR) + receive_chars(info, &status, regs); +// 1/28/2003 Winbond W90N745 doesn't have modem relate registers + check_modem_status(info); //mcli + + if (status & UART_IIR_THRE) + transmit_chars(info, 0); +#endif + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + } while (((status = serial_inp(info, COM_IIR_2)) & (UART_IIR_DR | UART_IIR_THRE))); +// } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_MULTIPORT +/* + * 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); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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]; + // + // The user was a bonehead, and misconfigured their + // multiport info. Rather than lock up the kernel + // in an infinite loop, if we loop too many times, + // print a message and break out of the loop. + // + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->state->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 +} +*/ +#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 (test_and_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); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * 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(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + if (info->next_port) { + do { + serial_out(info, COM_IER_2, 0); + info->IER |= UART_IER_THRI; + serial_out(info, COM_IER_2, info->IER); + info = info->next_port; + } while (info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[i].port1) + rs_interrupt_multi(i, NULL, NULL); + else +#endif + rs_interrupt(i, NULL, NULL); + } else +#endif // CONFIG_SERIAL_SHARE_IRQ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + rs_interrupt(0, NULL, NULL); +#else + rs_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} +/* + * --------------------------------------------------------------- + * 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. + * --------------------------------------------------------------- + */ + +/* + * 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 > 3) ? timeout-2 : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +/* +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} +*/ +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +/* +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +*/ +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + 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, state->irq); +#endif +/* + if (uart_config[state->type].flags & UART_STARTECH) { + // Wake up UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + // + // Turn off LCR == 0xBF so we actually set the IER + // register on the XR16C850 + // + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, 0); + // + // Now reset LCR so we can turn off the ECB bit + // + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + // + // For a XR16C850, we need to set the trigger levels + // + if (state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } + serial_outp(info, UART_LCR, 0); + } + + if (state->type == PORT_16750) { + // Wake up UART + serial_outp(info, UART_IER, 0); + } + + if (state->type == PORT_16C950) { + // Wake up and initialize UART + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); // Reset the UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // If this is an RSA port, see if we can kick it up to the + // higher speed clock. + // + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif +*/ +/* + // + // Clear the FIFO buffers and disable them + // (they will be reenabled in change_speed()) + // + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } +*/ +/* + // + // 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); +*/ + +/* + // + // 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 (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", state->line); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } +*/ + // + // Allocate the IRQ if necessary + // + + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif // CONFIG_SERIAL_SHARE_IRQ + } else + handler = rs_interrupt_single; + + // Mac Wang register rx and tx seperatly + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial2", &IRQ_ports[state->irq]); + + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + // + // Insert serial port into IRQ chain. + // + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + IRQ_ports[state->irq - 1] = info; + figure_IRQ_timeout(state->irq - 1); + + // + // Now, initialize the UART + // +/* + serial_outp(info, UART_LCR, UART_LCR_WLEN8); // reset DLAB + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_outp(info, UART_MCR, info->MCR); +*/ + +#if 0 + //serial_outp(info,COM_IER_2, 0x00); /* RDA interrupt source on / LOOP disable */ +// serial_outp(info,COM_LCR_2, 0x80); /* select divisor latch registers */ + +// serial_outp(info,COM_DLL_2, 6); +// serial_outp(info,COM_DLM_2, 0); + +// serial_outp(info,COM_LCR_2, 0x03); /* none parity, 8 data bits, 1 stop bits */ +// serial_outp(info,COM_MCR_2, 0x00); + + serial_outp(info,COM_TOR_2, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + //serial_outp(info,COM_FCR_2, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ +#endif + + // + // Finally, enable interrupts + // +/* + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); // enable interrupts +*/ + serial_outp(info,COM_TOR_2, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + CSR_WRITE (AIC_SCR9, 0x41); /* high-level sensitive, priority level 1 * lsshi 2005-4-20 11:41 */ + CSR_WRITE (AIC_MECR, 0x40); /* trun on bit 6 */ + enable_uart_rx_interrupt_2(info->line) ; + +#ifdef CONFIG_SERIAL_MANY_PORTS + 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); + } +#endif + + // + // 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.head = info->xmit.tail = 0; + + // + // Set up serial timers... + // + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + // + // Set up the tty->alt_speed kludge + // +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + // + // and set the speed of the serial port + // + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * 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; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); // 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[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + // + // Free the IRQ, if necessary + // + // rx + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial2", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } +/* + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + if (IRQ_ports[state->irq - 1]) { + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + retval = request_irq(state->irq - 1, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq - 1]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + } +*/ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + +/* + info->IER = 0; + serial_outp(info, UART_IER, 0x00); // disable all intrs +*/ + disable_uart_rx_interrupt_2(info->line); + disable_uart_tx_interrupt_2(info->line); +/* +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // reset interrupts on the AST Fourport board + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask +*/ +/* + // disable break condition + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + // disable FIFO's + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // Reset the RSA board back to 115kbps compat mode. + // + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif +*/ + + (void)serial_in(info, COM_RX_2); // read data port to reset things + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + // Arrange to enter sleep mode + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + // Arrange to enter sleep mode + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +*/ + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; +/* +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + 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; + } + return baud_table[i]; +} +*/ +#endif + +/* + * 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, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; +// unsigned cflag, cval, fcr = 0; + unsigned cflag, cval; + int bits; + unsigned long flags; + unsigned int div; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + // byte size and parity + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + // Never happens, but GCC is too dumb to figure it out + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + bits++; + } + if (cflag & PARODD) + cval |= UART_LCR_OPAR; + else if (cflag & PARENB) + cval |= UART_LCR_EPAR; + else + cval |= UART_LCR_NPAR; +/* + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif +*/ + // Determine divisor based on baud rate + baud = tty_get_baud_rate(info->tty); + //printk("baud:%d\n",baud); + if (!baud) + baud = 9600; // B0 transition handled in rs_set_termios +/* +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif +*/ + baud_base = info->state->baud_base; +/* + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } +*/ +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + quot = 1; //tricky + + // If the quotient is zero refuse the change + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + } + // As a last resort, if the quotient is zero, default to 9600 bps + if (!quot) + quot = baud_base / 9600; + // + // Work around a bug in the Oxford Semiconductor 952 rev B + // chip which causes it to seriously miscalculate baud rates + // when DLL is 0. + // +/* + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; +*/ + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; // Add .02 seconds of slop + +/* + // Set up FIFO's + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->state->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + // CTS flow control flag and modem status interrupts + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + 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; + + // + // Characters to ignore + // + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_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; + } +#if 0 // breaks serial console during boot stage + // + // !!! ignore all characters if CREAD is not set + // + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; +#endif + save_flags(flags); cli(); +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } +*/ +/* + 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 +*/ +/* + if (info->state->type == PORT_16750) + serial_outp(info, UART_FCR, fcr); // set fcr +*/ +// serial_outp(info, UART_LCR, cval); // reset DLAB + info->LCR = cval; // Save LCR +/* + if (info->state->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + // emulated UARTs (Lucent Venus 167x) need two steps + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(info, UART_FCR, fcr); // set fcr + } +*/ +/* + outp(COM_LSB, 6); // Baud rate = 115200 = 15,000,000/(16*(6+2)) +*/ + serial_outp (info,COM_LCR_2, 0x80); /* select divisor latch registers */ + div=baudrate_div(baud); + serial_outp (info,COM_DLL_2, div&0xFF); + serial_outp (info,COM_DLM_2, (div>>8)&0xFF); + + serial_outp (info,COM_LCR_2, cval); /* none parity, 8 data bits, 1 stop bits */ + + restore_flags(flags); +} + +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(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + 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.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_2(info->line); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + //restore_flags(flags);//clyu + buf += c; + count -= c; + ret += c; + restore_flags(flags); + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_2(info->line); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +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 CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(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_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + // Make sure transmit interrupts are on +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_2(info->line); + } +} + +/* + * ------------------------------------------------------------ + * 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; + unsigned long flags; +#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)) + rs_send_xchar(tty, STOP_CHAR(tty)); +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#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 + rs_send_xchar(tty, START_CHAR(tty)); + } +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + // Make sure address is not already in use + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + // + // OK, past this point, all the error checking has been done. + // At this point, we start making changes..... + // + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + // + // We need to shutdown the serial port at the old + // port/irq combination. + // + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif +*/ + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } 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; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, COM_LSR_2); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + // + // If we're about to load something into the transmit + // register, we'll pretend the transmitter isn't empty to + // avoid a race condition (depending on when the transmit + // interrupt happens). + // + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((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); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} + +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +/* +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +*/ +#else +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, COM_LCR_2, info->LCR); + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +/* +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->state->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->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + 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; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->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[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +*/ +#endif +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; // kernel counter temps + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + int retval, tmp; +#endif + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + 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 (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); // 1/4 second + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: // support for POSIX tcsendbreak() + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif + + case TIOCMGET: + 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: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: // Get line status register + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + // + // 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: + save_flags(flags); cli(); + // note the counters on entry + cprev = info->state->icount; + restore_flags(flags); + // Force modem status interrupts on +/* + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); +*/ + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + // see if a signal did it + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; // atomic copy + restore_flags(flags); + 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: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + // "setserial -W" is called in Debian boot + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + 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; +// unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + +/* + // Handle transition to B0 status + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + // Handle transition away from B0 status + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } +*/ + + // Handle turning off CRTSCTS + 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; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + 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, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + // + // Uh, oh. tty->count is 1, which means that the tty + // structure will be freed. state->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, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + // + // Save the termios structure, since this port may have + // separate termios for callout and dialin. + // + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->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); + enable_uart_rx_interrupt_2(info->line); + // + // Before we drop DTR, make sure the UART transmitter + // has completely drained; this is especially + // important if there is a transmit FIFO! + // + rs_wait_until_sent(tty, info->timeout); + } + 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) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + 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; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; // Just in case.... + + orig_jiffies = jiffies; + // + // Set the check interval to be 1/5 of the estimated time to + // send a single character, and make it at least 1. The check + // interval should also be less than the timeout. + // + // Note: we have to use pretty tight timings here to satisfy + // the NIST-PCTS. + // + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + // + // If the transmitter hasn't cleared in twice the approximate + // amount of time to send the entire FIFO, it probably won't + // ever clear. This assumes the UART isn't doing flow + // control, which is currently the case. Hence, if it ever + // takes longer than info->timeout, this is probably due to a + // UART bug of some kind. So, we clamp the timeout parameter at + // 2*info->timeout. + // + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, COM_LSR_2)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->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) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + // + // 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 + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -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 (state->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, state->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", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); +*/ + restore_flags(flags); + set_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; +*/ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + 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. + */ +// struct tty_struct *key_tty=NULL; //mcli +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + //key_tty=tty; //mcli + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + // + // This relies on lock_kernel() stuff so wants tidying for 2.5 + // + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + // + // If the port is the middle of closing, bail out now + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + info->closing_wait = 3000; + // + // Start up serial port + // + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + 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 + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; +// char stat_buf[30], control, status; + char stat_buf[30]; + int ret; + //unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + // + // Figure out the current RS-232 lines + // + if (!info) { + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + stat_buf[0] = 0; + stat_buf[1] = 0; +/* + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); +*/ + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + // + // Last thing is the RS-232 status lines + // + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +static int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * 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 char serial_options[] __initdata = +#ifdef CONFIG_HUB6 + " HUB-6" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MANY_PORTS + " MANY_PORTS" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MULTIPORT + " MULTIPORT" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_SHARE_IRQ + " SHARE_IRQ" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_DETECT_IRQ + " DETECT_IRQ" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PCI + " SERIAL_PCI" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PNP + " ISAPNP" +#define SERIAL_OPT +#endif +#ifdef SERIAL_OPT + " enabled\n"; +#else + " no serial options enabled\n"; +#endif +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + +/* + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. + */ +static unsigned detect_uart_irq (struct serial_state * state) +{ +/* + int irq; + unsigned long irqs; + unsigned char save_mcr, save_ier; + struct async_struct scr_info; // serial_{in,out} because HUB6 + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP=0; // no warning + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif + scr_info.io_type = state->io_type; + scr_info.iomem_base = state->iomem_base; + scr_info.iomem_reg_shift = state->iomem_reg_shift; + + // forget possible initially masked and pending IRQ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); + save_ier = serial_inp(&scr_info, UART_IER); + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); // enable all intrs + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(&scr_info, UART_MCR, save_mcr); + serial_outp(&scr_info, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; +*/ + return 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +/* +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} +*/ +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +/* +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + // + // First we check to see if it's an Oxford Semiconductor UART. + // + // If we have to do this here because some non-National + // Semiconductor clone chips lock up if you try writing to the + // LSR register (which serial_icr_read does) + // + if (state->type == PORT_16550A) { + // + // EFR [4] must be set else this test fails + // + // This shouldn't be necessary, but Mike Hudson + // (Exoray@isys.ca) claims that it's needed for 952 + // dual UART's (which are not recommended for new designs). + // + info->ACR = 0; + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); + // Check for Oxford Semiconductor 16C950 + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV) | + (scratch3 << 8); + return; + } + } + + // + // We check for a XR16C850 by setting DLL and DLM to 0, and + // then reading back DLL and DLM. If DLM reads back 0x10, + // then the UART is a XR16C850 and the DLL contains the chip + // revision. If DLM reads back 0x14, then the UART is a + // XR16C854. + // + // + + // Save the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + scratch2 = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; + state->type = PORT_16850; + return; + } + + // Restore the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); + // + // We distinguish between the '654 and the '650 by counting + // how many bytes are in the FIFO. I'm using this for now, + // since that's the technique that was sent to me in the + // serial driver update, but I'm not convinced this works. + // I've had problems doing this in the past. -TYT + // + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} +*/ +/* + * 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 serial_state * state) +{ +// unsigned char status1, status2, scratch, scratch2, scratch3; +// unsigned char save_lcr, save_mcr; + struct async_struct *info, scr_info; + unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + save_flags(flags); cli(); + +/* + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { + // + // 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); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; // We failed; there's nothing here + } + } +*/ +/* + save_mcr = serial_in(info, UART_MCR); + save_lcr = serial_in(info, UART_LCR); +*/ + // + // 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 (!(state->flags & ASYNC_SKIP_TEST)) { + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + state->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(info, UART_LCR, 0xBF); // set up for StarTech test + serial_outp(info, UART_EFR, 0); // EFR is the same as FCR + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; + switch (scratch) { + case 0: + state->type = PORT_16450; + break; + case 1: + state->type = PORT_UNKNOWN; + break; + case 2: + state->type = PORT_16550; + break; + case 3: + state->type = PORT_16550A; + break; + } + if (state->type == PORT_16550A) { + // Check for Startech UART's + serial_outp(info, UART_LCR, UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { + state->type = PORT_16650; + } else { + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) + autoconfig_startech_uarts(info, state, flags); + } + } + if (state->type == PORT_16550A) { + // Check for TI 16750 + serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 7) { + // + // If this is a 16750, and not a cheap UART + // clone, then it should only go into 64 byte + // mode if the UART_FCR7_64BYTE bit was set + // while UART_LCR_DLAB was latched. + // + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 6) + state->type = PORT_16750; + } + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif + serial_outp(info, UART_LCR, save_lcr); + if (state->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)) + state->type = PORT_8250; + } +*/ + state->type = PORT_W90N745; + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif +*/ + request_region(info->port,8,"serial(auto)"); + } + + // + // Reset the UART. + // +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ + + serial_outp (info,COM_LCR_2, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_2, 6);//115200 + serial_outp (info,COM_DLM_2, 0); + + serial_outp (info,COM_LCR_2, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + //serial_outp (info,COM_IER_2, 0x03); /* RDA interrupt source on / LOOP disable */ + serial_outp (info,COM_MCR_2, 0x00); + + //serial_outp (info,COM_TOR_2, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + serial_outp (info,COM_FCR_2, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ + + + restore_flags(flags); +} + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + + +#if (LINUX_VERSION_CODE > 0x20100) +//EXPORT_SYMBOL(register_serial); +//EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include + X(register_serial), + X(unregister_serial), +#include +}; +#endif + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + memset(&rs_multiport[i], 0, + sizeof(struct rs_multiport_struct)); +#endif + } +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE + // + // The interrupt of the serial console port + // can't be shared. + // + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + // Initialize the tty_driver structure + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial2"; +#endif +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 66 + SERIAL_DEV_OFFSET; + 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 = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + 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; +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + // + // The callout device is just like normal device except for + // major number and the subtype code. + // + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + 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, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) + continue; +#ifdef CONFIG_MCA + if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) + continue; +#endif + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(state); + } + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif +#ifdef ENABLE_SERIAL_PNP + probe_serial_pnp(); +#endif + return 0; +} +/* + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). + */ +/* +int __init early_serial_setup(struct serial_struct *req) +{ + int i = req->line; + + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); +} +*/ +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +/* +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } +#ifdef __i386__ + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + 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; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) + state->irq = detect_uart_irq(state); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} +*/ +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ +/* +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + // These will be hidden, because they are devices that will no longer + // be available to the system. (ie, PCMCIA modems, once ejected) + // + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} +*/ +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + // printk("Unloading %s: version %s\n", serial_name, serial_version); + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + 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 ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(rs_table[i].port, 8); + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif +*/ + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev == 0) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +#endif +*/ + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +/* +#ifdef ENABLE_SERIAL_PCI + if (serial_pci_driver.name[0]) + pci_unregister_driver (&serial_pci_driver); +#endif +*/ +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); +MODULE_AUTHOR("Theodore Ts'o "); +MODULE_LICENSE("GPL"); + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_W90N7452_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, COM_LSR_2); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here.- + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + unsigned i; + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, COM_TX_2, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, COM_TX_2, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity/flow control. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + { + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + } + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + /* + serial_out(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_out(info, UART_DLL, quot & 0xff); // LS of divisor + serial_out(info, UART_DLM, quot >> 8); // MS of divisor + serial_out(info, UART_LCR, cval); // reset DLAB + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + */ + /* + * If we read 0xff from the LSR, there is no UART here. + */ + /* + if (serial_in(info, UART_LSR) == 0xff) + return -1; + */ + serial_outp (info,COM_LCR_2, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_2, 6); + serial_outp (info,COM_DLM_2, 0); + + serial_outp (info,COM_LCR_2, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init winbond_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c new file mode 100644 index 0000000..6a08acb --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c @@ -0,0 +1,4669 @@ +/* + * linux/drivers/char/w90n745_uart_3.c + * + * Copyright (C) 1991, 1992 Linus Torvalds + * Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, + * 1998, 1999 Theodore Ts'o + * + * 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 + * + * 03/96: Modularised by Angelo Haritsis + * + * rs_set_termios fixed to look also for changes of the input + * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK. + * Bernd Anhäupl 05/17/96. + * + * 1/97: Extended dumb serial ports are a config option now. + * Saves 4k. Michael A. Griffith + * + * 8/97: Fix bug in rs_set_termios with RTS + * Stanislav V. Voronyi + * + * 3/98: Change the IRQ detection, use of probe_irq_o*(), + * suppress TIOCSERGWILD and TIOCSERSWILD + * Etienne Lorrain + * + * 4/98: Added changes to support the ARM architecture proposed by + * Russell King + * + * 5/99: Updated to include support for the XR16C850 and ST16C654 + * uarts. Stuart MacDonald + * + * 8/99: Generalized PCI support added. Theodore Ts'o + * + * 3/00: Rid circular buffer of redundant xmit_cnt. Fix a + * few races on freeing buffers too. + * Alan Modra + * + * 5/00: Support for the RSA-DV II/S card added. + * Kiyokazu SUTO + * + * 6/00: Remove old-style timer, use timer_list + * Andrew Morton + * + * 7/00: Support Timedia/Sunix/Exsys PCI cards + * + * 7/00: fix some returns on failure not using MOD_DEC_USE_COUNT. + * Arnaldo Carvalho de Melo + * + * 10/00: add in optional software flow control for serial console. + * Kanoj Sarcar (Modified by Theodore Ts'o) + * + * 08/30/2005:Porting to Winbond uart + * + */ + +static char *serial_version = "1.0"; +static char *serial_revdate = "2005-08-15"; + +/* + * Serial driver configuration section. Here are the various options: + * + * CONFIG_HUB6 + * Enables support for the venerable Bell Technologies + * HUB6 card. + * + * CONFIG_SERIAL_MANY_PORTS + * Enables support for ports beyond the standard, stupid + * COM 1/2/3/4. + * + * CONFIG_SERIAL_MULTIPORT + * Enables support for special multiport board support. + * + * CONFIG_SERIAL_SHARE_IRQ + * Enables support for multiple serial ports on one IRQ + * + * CONFIG_SERIAL_DETECT_IRQ + * Enable the autodetection of IRQ on standart ports + * + * SERIAL_PARANOIA_CHECK + * Check the magic number for the async_structure where + * ever possible. + */ + +#include +#include + +#undef SERIAL_PARANOIA_CHECK +#define CONFIG_SERIAL_NOPAUSE_IO +#define SERIAL_DO_RESTART + +#if 0 +/* These defines are normally controlled by the autoconf.h */ +#define CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_DETECT_IRQ +#define CONFIG_SERIAL_MULTIPORT +#define CONFIG_HUB6 +#endif + +#ifndef CONFIG_BOARD_W90N745 +#ifdef CONFIG_PCI +#define ENABLE_SERIAL_PCI +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#endif +#endif + +#if defined(CONFIG_ISAPNP)|| (defined(CONFIG_ISAPNP_MODULE) && defined(MODULE)) +#ifndef ENABLE_SERIAL_PNP +#define ENABLE_SERIAL_PNP +#endif +#endif + +/* Set of debugging defines */ + +#undef SERIAL_DEBUG_INTR +#undef SERIAL_DEBUG_OPEN +#undef SERIAL_DEBUG_FLOW +#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT +#undef SERIAL_DEBUG_PCI +#undef SERIAL_DEBUG_AUTOCONF + +/* Sanity checks */ + +#ifdef CONFIG_SERIAL_MULTIPORT +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef CONFIG_HUB6 +#ifndef CONFIG_SERIAL_MANY_PORTS +#define CONFIG_SERIAL_MANY_PORTS +#endif +#ifndef CONFIG_SERIAL_SHARE_IRQ +#define CONFIG_SERIAL_SHARE_IRQ +#endif +#endif + +#ifdef MODULE +#undef CONFIG_SERIAL_CONSOLE +#endif + +#define CONFIG_SERIAL_RSA + +#define RS_STROBE_TIME (10*HZ) +#define RS_ISR_PASS_LIMIT 256 + +#if defined(__i386__) && (defined(CONFIG_M386) || defined(CONFIG_M486)) +#define SERIAL_INLINE +#endif + +/* + * End of serial driver configuration section. + */ + +#include + +#include +#ifdef LOCAL_HEADERS +#include "serial_local.h" +#else +#include +#include +//#include +#include +#define LOCAL_VERSTRING "" +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#if (LINUX_VERSION_CODE >= 131343) +#include +#endif +#if (LINUX_VERSION_CODE >= 131336) +#include +#endif +#include +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE +#include +#endif +#ifdef ENABLE_SERIAL_PCI +#include +#endif +#ifdef ENABLE_SERIAL_PNP +#include +#endif +#ifdef CONFIG_MAGIC_SYSRQ +#include +#endif +#ifdef CONFIG_LEDMAN +#include +#endif + +/* + * All of the compatibilty code so we can compile serial.c against + * older kernels is hidden in serial_compat.h + */ +#if defined(LOCAL_HEADERS) || (LINUX_VERSION_CODE < 0x020317) /* 2.3.23 */ +#include "serial_compat.h" +#endif + +#include +#include +#include +#include + +#include //mcli + +#ifdef CONFIG_MAC_SERIAL +#define SERIAL_DEV_OFFSET 2 +#else +#define SERIAL_DEV_OFFSET 0 +#endif + +#ifdef SERIAL_INLINE +#define _INLINE_ inline +#else +#define _INLINE_ +#endif + +static char *serial_name = "Winbond W90N7453 Serial driver"; + +static DECLARE_TASK_QUEUE(tq_serial); + +static struct tty_driver serial_driver, callout_driver; +static int serial_refcount; + +static struct timer_list serial_timer; + +/* serial subtype definitions */ +#ifndef SERIAL_TYPE_NORMAL +#define SERIAL_TYPE_NORMAL 1 +#define SERIAL_TYPE_CALLOUT 2 +#endif + +/* number of characters left in xmit buffer before we ask for more */ +#define WAKEUP_CHARS 256 + +/* + * 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]; +#ifdef CONFIG_SERIAL_MULTIPORT +static struct rs_multiport_struct rs_multiport[NR_IRQS]; +#endif +static int IRQ_timeout[NR_IRQS]; +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE +static struct console sercons; +static int lsr_break_flag; +#endif +#if defined(CONFIG_SERIAL_W90N7453_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) +static unsigned long break_pressed; /* break, really ... */ +#endif + +static unsigned detect_uart_irq (struct serial_state * state); +static void autoconfig(struct serial_state * state); +static void change_speed(struct async_struct *info, struct termios *old); +static void rs_wait_until_sent(struct tty_struct *tty, int timeout); + +/* + * Here we define the default xmit fifo size used for each type of + * UART + */ +static struct serial_uart_config uart_config[] = { + { "unknown", 1, 0 }, + { "8250", 1, 0 }, + { "16450", 1, 0 }, + { "16550", 1, 0 }, + { "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "cirrus", 1, 0 }, // usurped by cyclades.c + { "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH }, + { "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "Startech", 1, 0}, // usurped by cyclades.c + { "16C950/954", 128, UART_CLEAR_FIFO | UART_USE_FIFO}, + { "ST16654", 64, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "XR16850", 128, UART_CLEAR_FIFO | UART_USE_FIFO | + UART_STARTECH }, + { "RSA", 2048, UART_CLEAR_FIFO | UART_USE_FIFO }, + { "W90N7453", 1, 0}, /* Winbond W90N745 uart 3 support */ + { 0, 0} +}; +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + +#define PORT_RSA_MAX 4 +static int probe_rsa[PORT_RSA_MAX]; +static int force_rsa[PORT_RSA_MAX]; + +MODULE_PARM(probe_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(probe_rsa, "Probe I/O ports for RSA"); +MODULE_PARM(force_rsa, "1-" __MODULE_STRING(PORT_RSA_MAX) "i"); +MODULE_PARM_DESC(force_rsa, "Force I/O ports for RSA"); +#endif /* CONFIG_SERIAL_RSA */ + + + +static struct serial_state rs_table[RS_TABLE_SIZE] = { +// SERIAL_PORT_DFNS // Defined in serial.h +{ 0, 115200, UART_BASE3, INT_UART3, STD_COM_FLAGS },/* ttyS3 */ +}; + + +#define NR_PORTS (sizeof(rs_table)/sizeof(struct serial_state)) + +#if (defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP)) +#define NR_PCI_BOARDS 8 + +static struct pci_board_inst serial_pci_board[NR_PCI_BOARDS]; + +#ifndef IS_PCI_REGION_IOPORT +#define IS_PCI_REGION_IOPORT(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_IO) +#endif +#ifndef IS_PCI_REGION_IOMEM +#define IS_PCI_REGION_IOMEM(dev, r) (pci_resource_flags((dev), (r)) & \ + IORESOURCE_MEM) +#endif +#ifndef PCI_IRQ_RESOURCE +#define PCI_IRQ_RESOURCE(dev, r) ((dev)->irq_resource[r].start) +#endif +#ifndef pci_get_subvendor +#define pci_get_subvendor(dev) ((dev)->subsystem_vendor) +#define pci_get_subdevice(dev) ((dev)->subsystem_device) +#endif +#endif /* ENABLE_SERIAL_PCI || ENABLE_SERIAL_PNP */ + +#ifndef PREPARE_FUNC +#define PREPARE_FUNC(dev) (dev->prepare) +#define ACTIVATE_FUNC(dev) (dev->activate) +#define DEACTIVATE_FUNC(dev) (dev->deactivate) +#endif + +#define HIGH_BITS_OFFSET ((sizeof(long)-sizeof(int))*8) + +static struct tty_struct *serial_table[NR_PORTS]; +static struct termios *serial_termios[NR_PORTS]; +static struct termios *serial_termios_locked[NR_PORTS]; + + +#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 + +/* + * tmp_buf is used as a temporary buffer by serial_write. We need to + * lock it in case the copy_from_user 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; +#ifdef DECLARE_MUTEX +static DECLARE_MUTEX(tmp_buf_sem); +#else +static struct semaphore tmp_buf_sem = MUTEX; +#endif + +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; +} + +static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset) +{ + return CSR_READ( offset); +} + +static _INLINE_ void serial_out(struct async_struct *info, int offset, + int value) +{ +#if 0 + printk("offset :%x\n",offset); + if(value!='A') + printk("value :%x\n",value); + else + printk("value :%c\n",value); +#endif + CSR_WRITE(offset, value); +} + +/* + * We used to support using pause I/O for certain machines. We + * haven't supported this for a while, but just in case it's badly + * needed for certain old 386 machines, I've left these #define's + * in.... + */ +#define serial_inp(info, offset) serial_in(info, offset) +#define serial_outp(info, offset, value) serial_out(info, offset, value) + + +/* + * For the 16C950 + */ +/* +void serial_icr_write(struct async_struct *info, int offset, int value) +{ + serial_out(info, UART_SCR, offset); + serial_out(info, UART_ICR, value); +} + +unsigned int serial_icr_read(struct async_struct *info, int offset) +{ + int value; + + serial_icr_write(info, UART_ACR, info->ACR | UART_ACR_ICRRD); + serial_out(info, UART_SCR, offset); + value = serial_in(info, UART_ICR); + serial_icr_write(info, UART_ACR, info->ACR); + return value; +} +*/ +/* + * ------------------------------------------------------------ + * 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(flags); cli(); +/* + if (info->IER & UART_IER_THRI) { + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); + } + if (info->state->type == PORT_16C950) { + info->ACR |= UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + disable_uart_tx_interrupt_3(info->line); + 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(flags); cli(); + if (info->xmit.head != info->xmit.tail + && info->xmit.buf) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_3(info->line); + } +/* + if (info->state->type == PORT_16C950) { + info->ACR &= ~UART_ACR_TXDIS; + serial_icr_write(info, UART_ACR, info->ACR); + } +*/ + 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 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(&info->tqueue, &tq_serial); + mark_bh(SERIAL_BH); +} + +static _INLINE_ void receive_chars(struct async_struct *info, + int *status, struct pt_regs * regs) +{ + struct tty_struct *tty = info->tty; + unsigned char ch; + struct async_icount *icount; + int max_count = 256; + //int lscr_status; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_RX : LEDMAN_COM2_RX); +#endif + *status = serial_inp(info, COM_LSR_3); + icount = &info->state->icount; + do { + if (tty->flip.count >= TTY_FLIPBUF_SIZE) { + tty->flip.tqueue.routine((void *) tty); + if (tty->flip.count >= TTY_FLIPBUF_SIZE) + return; // if TTY_DONT_FLIP is set + } + ch = serial_inp(info, COM_RX_3); + *tty->flip.char_buf_ptr = ch; + icount->rx++; + +#ifdef SERIAL_DEBUG_INTR + printk("DR%02x:%02x...", ch, *status); +#endif + *tty->flip.flag_buf_ptr = 0; + if (*status & (UART_LSR_BI | UART_LSR_PE | + UART_LSR_FE | UART_LSR_OE)) { + // + // For statistics only + // + if (*status & UART_LSR_BI) { + *status &= ~(UART_LSR_FE | UART_LSR_PE); + icount->brk++; + // + // We do the SysRQ and SAK checking + // here because otherwise the break + // may get masked by ignore_status_mask + // or read_status_mask. + // +#if defined(CONFIG_SERIAL_W90N7453_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (info->line == sercons.index) { + if (!break_pressed) { + break_pressed = jiffies; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if (info->flags & ASYNC_SAK) + do_SAK(tty); + } else if (*status & UART_LSR_PE) + icount->parity++; + else if (*status & UART_LSR_FE) + icount->frame++; + if (*status & UART_LSR_OE) + icount->overrun++; + + // + // Mask off conditions which should be ignored. + // + *status &= info->read_status_mask; + +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE + if (info->line == sercons.index) { + // Recover the break flag from console xmit + *status |= lsr_break_flag; + lsr_break_flag = 0; + } +#endif + if (*status & (UART_LSR_BI)) { +#ifdef SERIAL_DEBUG_INTR + printk("handling break...."); +#endif + *tty->flip.flag_buf_ptr = TTY_BREAK; + } 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; + } +#if defined(CONFIG_SERIAL_W90N7453_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + if (break_pressed && info->line == sercons.index) { + if (ch != 0 && + time_before(jiffies, break_pressed + HZ*5)) { + handle_sysrq(ch, regs, NULL, NULL); + break_pressed = 0; + goto ignore_char; + } + break_pressed = 0; + } +#endif + if ((*status & info->ignore_status_mask) == 0) { + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + tty->flip.count++; + } + if ((*status & UART_LSR_OE) && + (tty->flip.count < TTY_FLIPBUF_SIZE)) { + // + // Overrun is special, since it's reported + // immediately, and doesn't affect the current + // character + // + *tty->flip.flag_buf_ptr = TTY_OVERRUN; + tty->flip.count++; + tty->flip.flag_buf_ptr++; + tty->flip.char_buf_ptr++; + } +#if defined(CONFIG_SERIAL_W90N7453_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) + ignore_char: +#endif + *status = serial_inp(info, COM_LSR_3); + } while ((*status & UART_LSR_DR) && (max_count-- > 0)); + +#if (LINUX_VERSION_CODE > 131394) // 2.1.66 + tty_flip_buffer_push(tty); +#else + queue_task_irq_off(&tty->flip.tqueue, &tq_timer); +#endif +} + +static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done) +{ + int count; + +#ifdef CONFIG_LEDMAN + ledman_cmd(LEDMAN_CMD_SET, + (info->line == 0) ? LEDMAN_COM1_TX : LEDMAN_COM2_TX); +#endif + + if (info->x_char) { + serial_outp(info, COM_TX_3, info->x_char); + info->state->icount.tx++; + info->x_char = 0; + if (intr_done) + *intr_done = 0; + return; + } + if (info->xmit.head == info->xmit.tail + || info->tty->stopped + || info->tty->hw_stopped) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt_3(info->line); + return; + } + + count = info->xmit_fifo_size; + do { + while(!(serial_inp(info, COM_LSR_3)&UART_LSR_THRE)); + serial_out(info, COM_TX_3, info->xmit.buf[info->xmit.tail]); + info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1); + info->state->icount.tx++; + if (info->xmit.head == info->xmit.tail) + break; + } while (--count > 0); + while(!(serial_inp(info, COM_LSR_3)&UART_LSR_THRE)); + if (CIRC_CNT(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) < 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.head == info->xmit.tail) { +/* + info->IER &= ~UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + disable_uart_tx_interrupt_3(info->line); + } +} + + +static _INLINE_ void check_modem_status(struct async_struct *info) +{ + int status; + struct async_icount *icount; + + status = serial_in(info, UART_MSR); + + if (status & UART_MSR_ANY_DELTA) { + icount = &info->state->icount; + // update input line counters + if (status & UART_MSR_TERI) + icount->rng++; + if (status & UART_MSR_DDSR) + icount->dsr++; + if (status & UART_MSR_DDCD) { + icount->dcd++; +#ifdef CONFIG_HARD_PPS + if ((info->flags & ASYNC_HARDPPS_CD) && + (status & UART_MSR_DCD)) + hardpps(); +#endif + } + if (status & UART_MSR_DCTS) + 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("doing serial hangup..."); +#endif + if (info->tty) + tty_hangup(info->tty); + } + } + 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); + } + } + } +} + +#ifdef CONFIG_SERIAL_SHARE_IRQ +/* + * 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; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + + do { + if (!info->tty || + (serial_in(info, UART_IIR) & UART_IIR_NO_INT)) { + if (!end_mark) + end_mark = info; + goto next; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + end_mark = 0; + + info->last_active = jiffies; + + status = serial_inp(info, UART_LSR); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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); +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (normal) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} +*/ +#endif /* #ifdef CONFIG_SERIAL_SHARE_IRQ */ + + +/* + * 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; + struct async_struct * info; +#ifdef CONFIG_SERIAL_MULTIPORT + int first_multi = 0; + struct rs_multiport_struct *multi; +#endif + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + +#ifdef SERIAL_DEBUG_INTR + printk("rs_interrupt_single(%d)...", irq); +#endif + + info = IRQ_ports[irq]; + if (!info || !info->tty) + return; + +#ifdef CONFIG_SERIAL_MULTIPORT + multi = &rs_multiport[irq]; + if (multi->port_monitor) + first_multi = inb(multi->port_monitor); +#endif + status = serial_inp(info, COM_IIR_3); + do { + +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif +#if 1 + if (status & UART_IIR_DR) + receive_chars(info, &status, regs); +// 1/28/2003 Winbond W90N745 doesn't have modem relate registers + check_modem_status(info); //mcli + + if (status & UART_IIR_THRE) + transmit_chars(info, 0); +#endif + if (pass_counter++ > RS_ISR_PASS_LIMIT) { +#if 0 + printk("rs_single loop break.\n"); +#endif + break; + } +#ifdef SERIAL_DEBUG_INTR + printk("IIR = %x...", serial_in(info, UART_IIR)); +#endif + } while (((status = serial_inp(info, COM_IIR_3)) & (UART_IIR_DR | UART_IIR_THRE))); +// } while (!(serial_in(info, UART_IIR) & UART_IIR_NO_INT)); + info->last_active = jiffies; +#ifdef CONFIG_SERIAL_MULTIPORT + if (multi->port_monitor) + printk("rs port monitor (single) irq %d: 0x%x, 0x%x\n", + info->state->irq, first_multi, + inb(multi->port_monitor)); +#endif +#ifdef SERIAL_DEBUG_INTR + printk("end.\n"); +#endif +} + +#ifdef CONFIG_SERIAL_MULTIPORT +/* + * 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); +#ifdef SERIAL_DEBUG_INTR + printk("status = %x...", status); +#endif + if (status & UART_LSR_DR) + receive_chars(info, &status, regs); + 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]; + // + // The user was a bonehead, and misconfigured their + // multiport info. Rather than lock up the kernel + // in an infinite loop, if we loop too many times, + // print a message and break out of the loop. + // + if (pass_counter++ > RS_ISR_PASS_LIMIT) { + printk("Misconfigured multiport serial info " + "for irq %d. Breaking out irq loop\n", irq); + break; + } + if (multi->port_monitor) + printk("rs port monitor irq %d: 0x%x, 0x%x\n", + info->state->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 +} +*/ +#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 (test_and_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); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + } +} + +/* + * 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(unsigned long dummy) +{ + static unsigned long last_strobe; + struct async_struct *info; + unsigned int i; + unsigned long flags; + + if ((jiffies - last_strobe) >= RS_STROBE_TIME) { + for (i=0; i < NR_IRQS; i++) { + info = IRQ_ports[i]; + if (!info) + continue; + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + if (info->next_port) { + do { + serial_out(info, COM_IER_3, 0); + info->IER |= UART_IER_THRI; + serial_out(info, COM_IER_3, info->IER); + info = info->next_port; + } while (info); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[i].port1) + rs_interrupt_multi(i, NULL, NULL); + else +#endif + rs_interrupt(i, NULL, NULL); + } else +#endif // CONFIG_SERIAL_SHARE_IRQ + rs_interrupt_single(i, NULL, NULL); + restore_flags(flags); + } + } + last_strobe = jiffies; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + if (IRQ_ports[0]) { + save_flags(flags); cli(); +#ifdef CONFIG_SERIAL_SHARE_IRQ + rs_interrupt(0, NULL, NULL); +#else + rs_interrupt_single(0, NULL, NULL); +#endif + restore_flags(flags); + + mod_timer(&serial_timer, jiffies + IRQ_timeout[0]); + } +} +/* + * --------------------------------------------------------------- + * 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. + * --------------------------------------------------------------- + */ + +/* + * 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 > 3) ? timeout-2 : 1; +} + +#ifdef CONFIG_SERIAL_RSA +/* Attempts to turn on the RSA FIFO. Returns zero on failure */ +/* +static int enable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode | UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = mode & UART_RSA_MSR_FIFO; + } + + restore_flags(flags); + return result; +} +*/ +/* Attempts to turn off the RSA FIFO. Returns zero on failure */ +/* +static int disable_rsa(struct async_struct *info) +{ + unsigned char mode; + int result; + unsigned long flags; + + save_flags(flags); cli(); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + + if (!result) { + serial_outp(info, UART_RSA_MSR, mode & ~UART_RSA_MSR_FIFO); + mode = serial_inp(info, UART_RSA_MSR); + result = !(mode & UART_RSA_MSR_FIFO); + } + + restore_flags(flags); + return result; +} +*/ +#endif /* CONFIG_SERIAL_RSA */ + +static int startup(struct async_struct * info) +{ + unsigned long flags; + int retval=0; + void (*handler)(int, void *, struct pt_regs *); + struct serial_state *state= info->state; + unsigned long page; +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned short ICP; +#endif + + page = get_zeroed_page(GFP_KERNEL); + if (!page) + return -ENOMEM; + + save_flags(flags); cli(); + + if (info->flags & ASYNC_INITIALIZED) { + free_page(page); + goto errout; + } + + if (!CONFIGURED_SERIAL_PORT(state) || !state->type) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + free_page(page); + goto errout; + } + 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, state->irq); +#endif +/* + if (uart_config[state->type].flags & UART_STARTECH) { + // Wake up UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + // + // Turn off LCR == 0xBF so we actually set the IER + // register on the XR16C850 + // + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, 0); + // + // Now reset LCR so we can turn off the ECB bit + // + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + // + // For a XR16C850, we need to set the trigger levels + // + if (state->type == PORT_16850) { + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_RX); + serial_outp(info, UART_TRG, UART_TRG_96); + serial_outp(info, UART_FCTR, UART_FCTR_TRGD | + UART_FCTR_TX); + serial_outp(info, UART_TRG, UART_TRG_96); + } + serial_outp(info, UART_LCR, 0); + } + + if (state->type == PORT_16750) { + // Wake up UART + serial_outp(info, UART_IER, 0); + } + + if (state->type == PORT_16C950) { + // Wake up and initialize UART + info->ACR = 0; + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_IER, 0); + serial_outp(info, UART_LCR, 0); + serial_icr_write(info, UART_CSR, 0); // Reset the UART + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + } +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // If this is an RSA port, see if we can kick it up to the + // higher speed clock. + // + if (state->type == PORT_RSA) { + if (state->baud_base != SERIAL_RSA_BAUD_BASE && + enable_rsa(info)) + state->baud_base = SERIAL_RSA_BAUD_BASE; + if (state->baud_base == SERIAL_RSA_BAUD_BASE) + serial_outp(info, UART_RSA_FRR, 0); + } +#endif +*/ +/* + // + // Clear the FIFO buffers and disable them + // (they will be reenabled in change_speed()) + // + if (uart_config[state->type].flags & UART_CLEAR_FIFO) { + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); + } +*/ +/* + // + // 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); +*/ + +/* + // + // 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 (!(info->flags & ASYNC_BUGGY_UART) && + (serial_inp(info, UART_LSR) == 0xff)) { + printk("ttyS%d: LSR safety check engaged!\n", state->line); + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + } else + retval = -ENODEV; + goto errout; + } +*/ + // + // Allocate the IRQ if necessary + // + + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { +#ifdef CONFIG_SERIAL_SHARE_IRQ + free_irq(state->irq, &IRQ_ports[state->irq]); +#ifdef CONFIG_SERIAL_MULTIPORT + if (rs_multiport[state->irq].port1) + handler = rs_interrupt_multi; + else +#endif + handler = rs_interrupt; +#else + retval = -EBUSY; + goto errout; +#endif // CONFIG_SERIAL_SHARE_IRQ + } else + handler = rs_interrupt_single; + + // Mac Wang register rx and tx seperatly + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial3", &IRQ_ports[state->irq]); + + if (retval) { + if (capable(CAP_SYS_ADMIN)) { + if (info->tty) + set_bit(TTY_IO_ERROR, + &info->tty->flags); + retval = 0; + } + goto errout; + } + } + + // + // Insert serial port into IRQ chain. + // + info->prev_port = 0; + info->next_port = IRQ_ports[state->irq]; + if (info->next_port) + info->next_port->prev_port = info; + IRQ_ports[state->irq] = info; + figure_IRQ_timeout(state->irq); + + IRQ_ports[state->irq - 1] = info; + figure_IRQ_timeout(state->irq - 1); + + // + // Now, initialize the UART + // +/* + serial_outp(info, UART_LCR, UART_LCR_WLEN8); // reset DLAB + + info->MCR = 0; + if (info->tty->termios->c_cflag & CBAUD) + info->MCR = UART_MCR_DTR | UART_MCR_RTS; +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + if (state->irq == 0) + info->MCR |= UART_MCR_OUT1; + } else +#endif + { + if (state->irq != 0) + info->MCR |= UART_MCR_OUT2; + } + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_outp(info, UART_MCR, info->MCR); +*/ + +#if 0 + //serial_outp(info,COM_IER_3, 0x00); /* RDA interrupt source on / LOOP disable */ +// serial_outp(info,COM_LCR_3, 0x80); /* select divisor latch registers */ + +// serial_outp(info,COM_DLL_3, 6); +// serial_outp(info,COM_DLM_3, 0); + +// serial_outp(info,COM_LCR_3, 0x03); /* none parity, 8 data bits, 1 stop bits */ +// serial_outp(info,COM_MCR_3, 0x00); + + serial_outp(info,COM_TOR_3, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + //serial_outp(info,COM_FCR_3, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ +#endif + + // + // Finally, enable interrupts + // +/* + info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; + serial_outp(info, UART_IER, info->IER); // enable interrupts +*/ + serial_outp(info,COM_TOR_3, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + CSR_WRITE (AIC_SCR9, 0x41); /* high-level sensitive, priority level 1 * lsshi 2005-4-20 11:41 */ + CSR_WRITE (AIC_MECR, 0x40); /* trun on bit 6 */ + enable_uart_rx_interrupt_3(info->line) ; + +#ifdef CONFIG_SERIAL_MANY_PORTS + 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); + } +#endif + + // + // 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.head = info->xmit.tail = 0; + + // + // Set up serial timers... + // + mod_timer(&serial_timer, jiffies + 2*HZ/100); + + // + // Set up the tty->alt_speed kludge + // +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if (info->tty) { + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; + } +#endif + // + // and set the speed of the serial port + // + change_speed(info, 0); + + info->flags |= ASYNC_INITIALIZED; + restore_flags(flags); + return 0; + +errout: + restore_flags(flags); + return retval; +} + +/* + * 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; + struct serial_state *state; + int retval; + + if (!(info->flags & ASYNC_INITIALIZED)) + return; + + state = info->state; + +#ifdef SERIAL_DEBUG_OPEN + printk("Shutting down serial port %d (irq %d)....", info->line, + state->irq); +#endif + + save_flags(flags); cli(); // 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[state->irq] = info->next_port; + figure_IRQ_timeout(state->irq); + + // + // Free the IRQ, if necessary + // + // rx + if (state->irq && (!IRQ_ports[state->irq] || + !IRQ_ports[state->irq]->next_port)) { + if (IRQ_ports[state->irq]) { + free_irq(state->irq, &IRQ_ports[state->irq]); + retval = request_irq(state->irq, rs_interrupt_single, + SA_SHIRQ, "serial3", + &IRQ_ports[state->irq]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq, &IRQ_ports[state->irq]); + } +/* + if (state->irq - 1 && (!IRQ_ports[state->irq - 1] || + !IRQ_ports[state->irq - 1]->next_port)) { + if (IRQ_ports[state->irq - 1]) { + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + retval = request_irq(state->irq - 1, rs_interrupt_single, + SA_SHIRQ, "serial", + &IRQ_ports[state->irq - 1]); + + if (retval) + printk("serial shutdown: request_irq: error %d" + " Couldn't reacquire IRQ.\n", retval); + } else + free_irq(state->irq - 1, &IRQ_ports[state->irq - 1]); + } +*/ + + if (info->xmit.buf) { + unsigned long pg = (unsigned long) info->xmit.buf; + info->xmit.buf = 0; + free_page(pg); + } + +/* + info->IER = 0; + serial_outp(info, UART_IER, 0x00); // disable all intrs +*/ + disable_uart_rx_interrupt_3(info->line); + disable_uart_tx_interrupt_3(info->line); +/* +#ifdef CONFIG_SERIAL_MANY_PORTS + if (info->flags & ASYNC_FOURPORT) { + // reset interrupts on the AST Fourport board + (void) inb((info->port & 0xFE0) | 0x01F); + info->MCR |= UART_MCR_OUT1; + } else +#endif + info->MCR &= ~UART_MCR_OUT2; + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask +*/ +/* + // disable break condition + serial_out(info, UART_LCR, serial_inp(info, UART_LCR) & ~UART_LCR_SBC); + + if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + serial_outp(info, UART_MCR, info->MCR); + + // disable FIFO's + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ +/* +#ifdef CONFIG_SERIAL_RSA + // + // Reset the RSA board back to 115kbps compat mode. + // + if ((state->type == PORT_RSA) && + (state->baud_base == SERIAL_RSA_BAUD_BASE && + disable_rsa(info))) + state->baud_base = SERIAL_RSA_BAUD_BASE_LO; +#endif +*/ + + (void)serial_in(info, COM_RX_3); // read data port to reset things + + if (info->tty) + set_bit(TTY_IO_ERROR, &info->tty->flags); + +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + // Arrange to enter sleep mode + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, UART_EFR_ECB); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_IER, UART_IERX_SLEEP); + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, 0); + serial_outp(info, UART_LCR, 0); + } + if (info->state->type == PORT_16750) { + // Arrange to enter sleep mode + serial_outp(info, UART_IER, UART_IERX_SLEEP); + } +*/ + info->flags &= ~ASYNC_INITIALIZED; + restore_flags(flags); +} + +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +static int baud_table[] = { + 0, 50, 75, 110, 134, 150, 200, 300, + 600, 1200, 1800, 2400, 4800, 9600, 19200, + 38400, 57600, 115200, 230400, 460800, 0 }; +/* +static int tty_get_baud_rate(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned int cflag, i; + + cflag = tty->termios->c_cflag; + + i = cflag & CBAUD; + if (i & CBAUDEX) { + i &= ~CBAUDEX; + if (i < 1 || i > 2) + 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; + } + return baud_table[i]; +} +*/ +#endif + +/* + * 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, + struct termios *old_termios) +{ + int quot = 0, baud_base, baud; +// unsigned cflag, cval, fcr = 0; + unsigned cflag, cval; + int bits; + unsigned long flags; + unsigned int div; + + if (!info->tty || !info->tty->termios) + return; + cflag = info->tty->termios->c_cflag; + if (!CONFIGURED_SERIAL_PORT(info)) + return; + + // byte size and parity + switch (cflag & CSIZE) { + case CS5: cval = 0x00; bits = 7; break; + case CS6: cval = 0x01; bits = 8; break; + case CS7: cval = 0x02; bits = 9; break; + case CS8: cval = 0x03; bits = 10; break; + // Never happens, but GCC is too dumb to figure it out + default: cval = 0x00; bits = 7; break; + } + if (cflag & CSTOPB) { + cval |= 0x04; + bits++; + } + if (cflag & PARENB) { + bits++; + } + if (cflag & PARODD) + cval |= UART_LCR_OPAR; + else if (cflag & PARENB) + cval |= UART_LCR_EPAR; + else + cval |= UART_LCR_NPAR; +/* + if (cflag & PARENB) { + cval |= UART_LCR_PARITY; + bits++; + } + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; +#ifdef CMSPAR + if (cflag & CMSPAR) + cval |= UART_LCR_SPAR; +#endif +*/ + // Determine divisor based on baud rate + baud = tty_get_baud_rate(info->tty); + //printk("baud:%d\n",baud); + if (!baud) + baud = 9600; // B0 transition handled in rs_set_termios +/* +#ifdef CONFIG_SERIAL_RSA + if ((info->state->type == PORT_RSA) && + (info->state->baud_base != SERIAL_RSA_BAUD_BASE) && + enable_rsa(info)) + info->state->baud_base = SERIAL_RSA_BAUD_BASE; +#endif +*/ + baud_base = info->state->baud_base; +/* + if (info->state->type == PORT_16C950) { + if (baud <= baud_base) + serial_icr_write(info, UART_TCR, 0); + else if (baud <= 2*baud_base) { + serial_icr_write(info, UART_TCR, 0x8); + baud_base = baud_base * 2; + } else if (baud <= 4*baud_base) { + serial_icr_write(info, UART_TCR, 0x4); + baud_base = baud_base * 4; + } else + serial_icr_write(info, UART_TCR, 0); + } +*/ +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + quot = 1; //tricky + + // If the quotient is zero refuse the change + if (!quot && old_termios) { + info->tty->termios->c_cflag &= ~CBAUD; + info->tty->termios->c_cflag |= (old_termios->c_cflag & CBAUD); + baud = tty_get_baud_rate(info->tty); + if (!baud) + baud = 9600; +/* + if (baud == 38400 && + ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST)) + quot = info->state->custom_divisor; + else { + if (baud == 134) + // Special case since 134 is really 134.5 + quot = (2*baud_base / 269); + else if (baud) + quot = baud_base / baud; + } +*/ + } + // As a last resort, if the quotient is zero, default to 9600 bps + if (!quot) + quot = baud_base / 9600; + // + // Work around a bug in the Oxford Semiconductor 952 rev B + // chip which causes it to seriously miscalculate baud rates + // when DLL is 0. + // +/* + if (((quot & 0xFF) == 0) && (info->state->type == PORT_16C950) && + (info->state->revision == 0x5201)) + quot++; +*/ + info->quot = quot; + info->timeout = ((info->xmit_fifo_size*HZ*bits*quot) / baud_base); + info->timeout += HZ/50; // Add .02 seconds of slop + +/* + // Set up FIFO's + if (uart_config[info->state->type].flags & UART_USE_FIFO) { + if ((info->state->baud_base / quot) < 2400) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_1; +#ifdef CONFIG_SERIAL_RSA + else if (info->state->type == PORT_RSA) + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_14; +#endif + else + fcr = UART_FCR_ENABLE_FIFO | UART_FCR_TRIGGER_8; + } + if (info->state->type == PORT_16750) + fcr |= UART_FCR7_64BYTE; + + // CTS flow control flag and modem status interrupts + info->IER &= ~UART_IER_MSI; + if (info->flags & ASYNC_HARDPPS_CD) + 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; + + // + // Characters to ignore + // + info->ignore_status_mask = 0; + if (I_IGNPAR(info->tty)) + info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE; + if (I_IGNBRK(info->tty)) { + info->ignore_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; + } +#if 0 // breaks serial console during boot stage + // + // !!! ignore all characters if CREAD is not set + // + if ((cflag & CREAD) == 0) + info->ignore_status_mask |= UART_LSR_DR; +#endif + save_flags(flags); cli(); +/* + if (uart_config[info->state->type].flags & UART_STARTECH) { + serial_outp(info, UART_LCR, 0xBF); + serial_outp(info, UART_EFR, + (cflag & CRTSCTS) ? UART_EFR_CTS : 0); + } +*/ +/* + 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 +*/ +/* + if (info->state->type == PORT_16750) + serial_outp(info, UART_FCR, fcr); // set fcr +*/ +// serial_outp(info, UART_LCR, cval); // reset DLAB + info->LCR = cval; // Save LCR +/* + if (info->state->type != PORT_16750) { + if (fcr & UART_FCR_ENABLE_FIFO) { + // emulated UARTs (Lucent Venus 167x) need two steps + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } + serial_outp(info, UART_FCR, fcr); // set fcr + } +*/ +/* + outp(COM_LSB, 6); // Baud rate = 115200 = 15,000,000/(16*(6+2)) +*/ + serial_outp (info,COM_LCR_3, 0x80); /* select divisor latch registers */ + div=baudrate_div(baud); + serial_outp (info,COM_DLL_3, div&0xFF); + serial_outp (info,COM_DLM_3, (div>>8)&0xFF); + + serial_outp (info,COM_LCR_3, cval); /* none parity, 8 data bits, 1 stop bits */ + + restore_flags(flags); +} + +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(flags); cli(); + if (CIRC_SPACE(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE) == 0) { + restore_flags(flags); + return; + } + + info->xmit.buf[info->xmit.head] = ch; + info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1); + 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.head == info->xmit.tail + || tty->stopped + || tty->hw_stopped + || !info->xmit.buf) + return; + + save_flags(flags); cli(); +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_3(info->line); + restore_flags(flags); +} + +static int rs_write(struct tty_struct * tty, int from_user, + const unsigned char *buf, int count) +{ + int c, ret = 0; + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; + +// printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_write")) + return 0; + + if (!tty || !info->xmit.buf || !tmp_buf) + return 0; + + save_flags(flags); + if (from_user) { + down(&tmp_buf_sem); + while (1) { + int c1; + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) + break; + + c -= copy_from_user(tmp_buf, buf, c); + if (!c) { + if (!ret) + ret = -EFAULT; + break; + } + cli(); + c1 = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (c1 < c) + c = c1; + memcpy(info->xmit.buf + info->xmit.head, tmp_buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + //restore_flags(flags);//clyu + buf += c; + count -= c; + ret += c; + restore_flags(flags); + } + up(&tmp_buf_sem); + } else { + cli(); + while (1) { + c = CIRC_SPACE_TO_END(info->xmit.head, + info->xmit.tail, + SERIAL_XMIT_SIZE); + if (count < c) + c = count; + if (c <= 0) { + break; + } + memcpy(info->xmit.buf + info->xmit.head, buf, c); + info->xmit.head = ((info->xmit.head + c) & + (SERIAL_XMIT_SIZE-1)); + buf += c; + count -= c; + ret += c; + } + restore_flags(flags); + } + if (info->xmit.head != info->xmit.tail + && !tty->stopped + && !tty->hw_stopped) { +/* + && !(info->IER & UART_IER_THRI)) { + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_3(info->line); + } + return ret; +} + +static int rs_write_room(struct tty_struct *tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_write_room")) + return 0; + return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +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 CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE); +} + +static void rs_flush_buffer(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_buffer")) + return; + save_flags(flags); cli(); + info->xmit.head = info->xmit.tail = 0; + restore_flags(flags); + wake_up_interruptible(&tty->write_wait); +#ifdef SERIAL_HAVE_POLL_WAIT + wake_up_interruptible(&tty->poll_wait); +#endif + if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && + tty->ldisc.write_wakeup) + (tty->ldisc.write_wakeup)(tty); +} + +/* + * This function is used to send a high-priority XON/XOFF character to + * the device + */ +static void rs_send_xchar(struct tty_struct *tty, char ch) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + + if (serial_paranoia_check(info, tty->device, "rs_send_char")) + return; + + info->x_char = ch; + if (ch) { + // Make sure transmit interrupts are on +/* + info->IER |= UART_IER_THRI; + serial_out(info, UART_IER, info->IER); +*/ + enable_uart_tx_interrupt_3(info->line); + } +} + +/* + * ------------------------------------------------------------ + * 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; + unsigned long flags; +#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)) + rs_send_xchar(tty, STOP_CHAR(tty)); +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR &= ~UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +static void rs_unthrottle(struct tty_struct * tty) +{ + struct async_struct *info = (struct async_struct *)tty->driver_data; + unsigned long flags; +#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 + rs_send_xchar(tty, START_CHAR(tty)); + } +/* + if (tty->termios->c_cflag & CRTSCTS) + info->MCR |= UART_MCR_RTS; +*/ + save_flags(flags); cli(); +// serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); +} + +/* + * ------------------------------------------------------------ + * rs_ioctl() and friends + * ------------------------------------------------------------ + */ +static int get_serial_info(struct async_struct * info, + struct serial_struct * retinfo) +{ + struct serial_struct tmp; + struct serial_state *state = info->state; + + if (!retinfo) + return -EFAULT; + memset(&tmp, 0, sizeof(tmp)); + tmp.type = state->type; + tmp.line = state->line; + tmp.port = state->port; + if (HIGH_BITS_OFFSET) + tmp.port_high = state->port >> HIGH_BITS_OFFSET; + else + tmp.port_high = 0; + tmp.irq = state->irq; + tmp.flags = state->flags; + tmp.xmit_fifo_size = state->xmit_fifo_size; + tmp.baud_base = state->baud_base; + tmp.close_delay = state->close_delay; + tmp.closing_wait = state->closing_wait; + tmp.custom_divisor = state->custom_divisor; + tmp.hub6 = state->hub6; + tmp.io_type = state->io_type; + if (copy_to_user(retinfo,&tmp,sizeof(*retinfo))) + return -EFAULT; + return 0; +} + +static int set_serial_info(struct async_struct * info, + struct serial_struct * new_info) +{ + struct serial_struct new_serial; + struct serial_state old_state, *state; + unsigned int i,change_irq,change_port; + int retval = 0; + unsigned long new_port; + + if (copy_from_user(&new_serial,new_info,sizeof(new_serial))) + return -EFAULT; + state = info->state; + old_state = *state; + + new_port = new_serial.port; + if (HIGH_BITS_OFFSET) + new_port += (unsigned long) new_serial.port_high << HIGH_BITS_OFFSET; + + change_irq = new_serial.irq != state->irq; + change_port = (new_port != ((int) state->port)) || + (new_serial.hub6 != state->hub6); + + if (!capable(CAP_SYS_ADMIN)) { + if (change_irq || change_port || + (new_serial.baud_base != state->baud_base) || + (new_serial.type != state->type) || + (new_serial.close_delay != state->close_delay) || + (new_serial.xmit_fifo_size != state->xmit_fifo_size) || + ((new_serial.flags & ~ASYNC_USR_MASK) != + (state->flags & ~ASYNC_USR_MASK))) + return -EPERM; + state->flags = ((state->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + info->flags = ((info->flags & ~ASYNC_USR_MASK) | + (new_serial.flags & ASYNC_USR_MASK)); + state->custom_divisor = new_serial.custom_divisor; + goto check_and_exit; + } + + new_serial.irq = irq_cannonicalize(new_serial.irq); + + if ((new_serial.irq >= NR_IRQS) || (new_serial.irq < 0) || + (new_serial.baud_base < 9600)|| (new_serial.type < PORT_UNKNOWN) || + (new_serial.type > PORT_MAX) || (new_serial.type == PORT_CIRRUS) || + (new_serial.type == PORT_STARTECH)) { + return -EINVAL; + } + + if ((new_serial.type != state->type) || + (new_serial.xmit_fifo_size <= 0)) + new_serial.xmit_fifo_size = + uart_config[new_serial.type].dfl_xmit_fifo_size; + + // Make sure address is not already in use + if (new_serial.type) { + for (i = 0 ; i < NR_PORTS; i++) + if ((state != &rs_table[i]) && + (rs_table[i].port == new_port) && + rs_table[i].type) + return -EADDRINUSE; + } + + if ((change_port || change_irq) && (state->count > 1)) + return -EBUSY; + + // + // OK, past this point, all the error checking has been done. + // At this point, we start making changes..... + // + + state->baud_base = new_serial.baud_base; + state->flags = ((state->flags & ~ASYNC_FLAGS) | + (new_serial.flags & ASYNC_FLAGS)); + info->flags = ((state->flags & ~ASYNC_INTERNAL_FLAGS) | + (info->flags & ASYNC_INTERNAL_FLAGS)); + state->custom_divisor = new_serial.custom_divisor; + state->close_delay = new_serial.close_delay * HZ/100; + state->closing_wait = new_serial.closing_wait * HZ/100; +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + info->xmit_fifo_size = state->xmit_fifo_size = + new_serial.xmit_fifo_size; + + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (old_state.type == PORT_RSA) + release_region(state->port + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(state->port,8); + } + state->type = new_serial.type; + if (change_port || change_irq) { + // + // We need to shutdown the serial port at the old + // port/irq combination. + // + shutdown(info); + state->irq = new_serial.irq; + info->port = state->port = new_port; + info->hub6 = state->hub6 = new_serial.hub6; + if (info->hub6) + info->io_type = state->io_type = SERIAL_IO_HUB6; + else if (info->io_type == SERIAL_IO_HUB6) + info->io_type = state->io_type = SERIAL_IO_PORT; + } + if ((state->type != PORT_UNKNOWN) && state->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(state->port + UART_RSA_BASE, + 16, "serial_rsa(set)"); + else +#endif +*/ + request_region(state->port,8,"serial(set)"); + } + + +check_and_exit: + if (!state->port || !state->type) + return 0; + if (info->flags & ASYNC_INITIALIZED) { + if (((old_state.flags & ASYNC_SPD_MASK) != + (state->flags & ASYNC_SPD_MASK)) || + (old_state.custom_divisor != state->custom_divisor)) { +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) + info->tty->alt_speed = 57600; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) + info->tty->alt_speed = 115200; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI) + info->tty->alt_speed = 230400; + if ((state->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP) + info->tty->alt_speed = 460800; +#endif + change_speed(info, 0); + } + } 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; + unsigned long flags; + + save_flags(flags); cli(); + status = serial_in(info, COM_LSR_3); + restore_flags(flags); + result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); + + // + // If we're about to load something into the transmit + // register, we'll pretend the transmitter isn't empty to + // avoid a race condition (depending on when the transmit + // interrupt happens). + // + if (info->x_char || + ((CIRC_CNT(info->xmit.head, info->xmit.tail, + SERIAL_XMIT_SIZE) > 0) && + !info->tty->stopped && !info->tty->hw_stopped)) + result &= TIOCSER_TEMT; + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int get_modem_info(struct async_struct * info, unsigned int *value) +{ + unsigned char control, status; + unsigned int result; + unsigned long flags; + + control = info->MCR; + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + restore_flags(flags); + result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) + | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) +#ifdef TIOCM_OUT1 + | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) + | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0) +#endif + | ((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); + + if (copy_to_user(value, &result, sizeof(int))) + return -EFAULT; + return 0; +} + +static int set_modem_info(struct async_struct * info, unsigned int cmd, + unsigned int *value) +{ + unsigned int arg; + unsigned long flags; + + if (copy_from_user(&arg, value, sizeof(int))) + return -EFAULT; + + switch (cmd) { + case TIOCMBIS: + if (arg & TIOCM_RTS) + info->MCR |= UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR |= UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR |= UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR |= UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR |= UART_MCR_LOOP; + break; + case TIOCMBIC: + if (arg & TIOCM_RTS) + info->MCR &= ~UART_MCR_RTS; + if (arg & TIOCM_DTR) + info->MCR &= ~UART_MCR_DTR; +#ifdef TIOCM_OUT1 + if (arg & TIOCM_OUT1) + info->MCR &= ~UART_MCR_OUT1; + if (arg & TIOCM_OUT2) + info->MCR &= ~UART_MCR_OUT2; +#endif + if (arg & TIOCM_LOOP) + info->MCR &= ~UART_MCR_LOOP; + break; + case TIOCMSET: + info->MCR = ((info->MCR & ~(UART_MCR_RTS | +#ifdef TIOCM_OUT1 + UART_MCR_OUT1 | + UART_MCR_OUT2 | +#endif + UART_MCR_LOOP | + UART_MCR_DTR)) + | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) +#ifdef TIOCM_OUT1 + | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) + | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0) +#endif + | ((arg & TIOCM_LOOP) ? UART_MCR_LOOP : 0) + | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); + break; + default: + return -EINVAL; + } + save_flags(flags); cli(); + info->MCR |= ALPHA_KLUDGE_MCR; // Don't ask + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + return 0; +} + +static int do_autoconfig(struct async_struct * info) +{ + int irq, retval; + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + + if (info->state->count > 1) + return -EBUSY; + + shutdown(info); + + autoconfig(info->state); + if ((info->state->flags & ASYNC_AUTO_IRQ) && + (info->state->port != 0) && + (info->state->type != PORT_UNKNOWN)) { + irq = detect_uart_irq(info->state); + if (irq > 0) + info->state->irq = irq; + } + + retval = startup(info); + if (retval) + return retval; + return 0; +} + +/* + * rs_break() --- routine which turns the break handling on or off + */ +#if (LINUX_VERSION_CODE < 131394) /* Linux 2.1.66 */ +/* +static void send_break( struct async_struct * info, int duration) +{ + if (!CONFIGURED_SERIAL_PORT(info)) + return; + current->state = TASK_INTERRUPTIBLE; + current->timeout = jiffies + duration; + cli(); + info->LCR |= UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + schedule(); + info->LCR &= ~UART_LCR_SBC; + serial_out(info, UART_LCR, info->LCR); + sti(); +} +*/ +#else +static void rs_break(struct tty_struct *tty, int break_state) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long flags; + + if (serial_paranoia_check(info, tty->device, "rs_break")) + return; + + if (!CONFIGURED_SERIAL_PORT(info)) + return; + save_flags(flags); cli(); + if (break_state == -1) + info->LCR |= UART_LCR_SBC; + else + info->LCR &= ~UART_LCR_SBC; + serial_out(info, COM_LCR_3, info->LCR); + restore_flags(flags); +} +#endif + +#ifdef CONFIG_SERIAL_MULTIPORT +/* +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->state->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->state->irq; + + if (copy_to_user(retinfo,&ret,sizeof(*retinfo))) + return -EFAULT; + 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; + struct serial_state *state; + int was_multi, now_multi; + int retval; + void (*handler)(int, void *, struct pt_regs *); + + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + state = info->state; + + if (copy_from_user(&new_multi, in_multi, + sizeof(struct serial_multiport_struct))) + return -EFAULT; + + if (new_multi.irq != state->irq || state->irq == 0 || + !IRQ_ports[state->irq]) + return -EINVAL; + + multi = &rs_multiport[state->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[state->irq]->next_port && + (was_multi != now_multi)) { + free_irq(state->irq, &IRQ_ports[state->irq]); + if (now_multi) + handler = rs_interrupt_multi; + else + handler = rs_interrupt; + + retval = request_irq(state->irq, handler, SA_SHIRQ, + "serial", &IRQ_ports[state->irq]); + if (retval) { + printk("Couldn't reallocate serial interrupt " + "driver!!\n"); + } + } + return 0; +} +*/ +#endif +static int rs_ioctl(struct tty_struct *tty, struct file * file, + unsigned int cmd, unsigned long arg) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct async_icount cprev, cnow; // kernel counter temps + struct serial_icounter_struct icount; + unsigned long flags; +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + int retval, tmp; +#endif + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + if (serial_paranoia_check(info, tty->device, "rs_ioctl")) + return -ENODEV; + + if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && + (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) && + (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { + if (tty->flags & (1 << TTY_IO_ERROR)) + return -EIO; + } + + switch (cmd) { +#if (LINUX_VERSION_CODE < 131394) // Linux 2.1.66 + 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 (signal_pending(current)) + return -EINTR; + if (!arg) { + send_break(info, HZ/4); // 1/4 second + if (signal_pending(current)) + return -EINTR; + } + return 0; + case TCSBRKP: // support for POSIX tcsendbreak() + retval = tty_check_change(tty); + if (retval) + return retval; + tty_wait_until_sent(tty, 0); + if (signal_pending(current)) + return -EINTR; + send_break(info, arg ? arg*(HZ/10) : HZ/4); + if (signal_pending(current)) + return -EINTR; + return 0; + case TIOCGSOFTCAR: + tmp = C_CLOCAL(tty) ? 1 : 0; + if (copy_to_user((void *)arg, &tmp, sizeof(int))) + return -EFAULT; + return 0; + case TIOCSSOFTCAR: + if (copy_from_user(&tmp, (void *)arg, sizeof(int))) + return -EFAULT; + + tty->termios->c_cflag = + ((tty->termios->c_cflag & ~CLOCAL) | + (tmp ? CLOCAL : 0)); + return 0; +#endif + + case TIOCMGET: + 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: + return get_serial_info(info, + (struct serial_struct *) arg); + case TIOCSSERIAL: + return set_serial_info(info, + (struct serial_struct *) arg); + case TIOCSERCONFIG: + return do_autoconfig(info); + + case TIOCSERGETLSR: // Get line status register + return get_lsr_info(info, (unsigned int *) arg); + + case TIOCSERGSTRUCT: + if (copy_to_user((struct async_struct *) arg, + info, sizeof(struct async_struct))) + return -EFAULT; + return 0; + +#ifdef CONFIG_SERIAL_MULTIPORT + case TIOCSERGETMULTI: + return get_multiport_struct(info, + (struct serial_multiport_struct *) arg); + case TIOCSERSETMULTI: + return set_multiport_struct(info, + (struct serial_multiport_struct *) arg); +#endif + + // + // 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: + save_flags(flags); cli(); + // note the counters on entry + cprev = info->state->icount; + restore_flags(flags); + // Force modem status interrupts on +/* + info->IER |= UART_IER_MSI; + serial_out(info, UART_IER, info->IER); +*/ + while (1) { + interruptible_sleep_on(&info->delta_msr_wait); + // see if a signal did it + if (signal_pending(current)) + return -ERESTARTSYS; + save_flags(flags); cli(); + cnow = info->state->icount; // atomic copy + restore_flags(flags); + 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: + save_flags(flags); cli(); + cnow = info->state->icount; + restore_flags(flags); + icount.cts = cnow.cts; + icount.dsr = cnow.dsr; + icount.rng = cnow.rng; + icount.dcd = cnow.dcd; + icount.rx = cnow.rx; + icount.tx = cnow.tx; + icount.frame = cnow.frame; + icount.overrun = cnow.overrun; + icount.parity = cnow.parity; + icount.brk = cnow.brk; + icount.buf_overrun = cnow.buf_overrun; + + if (copy_to_user((void *)arg, &icount, sizeof(icount))) + return -EFAULT; + return 0; + case TIOCSERGWILD: + case TIOCSERSWILD: + // "setserial -W" is called in Debian boot + printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); + 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; +// unsigned long flags; + unsigned int cflag = tty->termios->c_cflag; + + if ( (cflag == old_termios->c_cflag) + && ( RELEVANT_IFLAG(tty->termios->c_iflag) + == RELEVANT_IFLAG(old_termios->c_iflag))) + return; + + change_speed(info, old_termios); + +/* + // Handle transition to B0 status + if ((old_termios->c_cflag & CBAUD) && + !(cflag & CBAUD)) { + info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } + + // Handle transition away from B0 status + if (!(old_termios->c_cflag & CBAUD) && + (cflag & CBAUD)) { + info->MCR |= UART_MCR_DTR; + if (!(tty->termios->c_cflag & CRTSCTS) || + !test_bit(TTY_THROTTLED, &tty->flags)) { + info->MCR |= UART_MCR_RTS; + } + save_flags(flags); cli(); + serial_out(info, UART_MCR, info->MCR); + restore_flags(flags); + } +*/ + + // Handle turning off CRTSCTS + 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; + struct serial_state *state; + unsigned long flags; + + if (!info || serial_paranoia_check(info, tty->device, "rs_close")) + return; + + state = info->state; + + save_flags(flags); cli(); + + 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, state->count); +#endif + if ((tty->count == 1) && (state->count != 1)) { + // + // Uh, oh. tty->count is 1, which means that the tty + // structure will be freed. state->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, " + "state->count is %d\n", state->count); + state->count = 1; + } + if (--state->count < 0) { + printk("rs_close: bad serial port count for ttys%d: %d\n", + info->line, state->count); + state->count = 0; + } + if (state->count) { + DBG_CNT("before DEC-2"); + MOD_DEC_USE_COUNT; + restore_flags(flags); + return; + } + info->flags |= ASYNC_CLOSING; + restore_flags(flags); + // + // Save the termios structure, since this port may have + // separate termios for callout and dialin. + // + if (info->flags & ASYNC_NORMAL_ACTIVE) + info->state->normal_termios = *tty->termios; + if (info->flags & ASYNC_CALLOUT_ACTIVE) + info->state->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); + enable_uart_rx_interrupt_3(info->line); + // + // Before we drop DTR, make sure the UART transmitter + // has completely drained; this is especially + // important if there is a transmit FIFO! + // + rs_wait_until_sent(tty, info->timeout); + } + 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) { + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(info->close_delay); + } + 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; +} + +/* + * rs_wait_until_sent() --- wait until the transmitter is empty + */ +static void rs_wait_until_sent(struct tty_struct *tty, int timeout) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + unsigned long orig_jiffies, char_time; + int lsr; + + if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) + return; + + if (info->state->type == PORT_UNKNOWN) + return; + + if (info->xmit_fifo_size == 0) + return; // Just in case.... + + orig_jiffies = jiffies; + // + // Set the check interval to be 1/5 of the estimated time to + // send a single character, and make it at least 1. The check + // interval should also be less than the timeout. + // + // Note: we have to use pretty tight timings here to satisfy + // the NIST-PCTS. + // + char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; + char_time = char_time / 5; + if (char_time == 0) + char_time = 1; + if (timeout && timeout < char_time) + char_time = timeout; + // + // If the transmitter hasn't cleared in twice the approximate + // amount of time to send the entire FIFO, it probably won't + // ever clear. This assumes the UART isn't doing flow + // control, which is currently the case. Hence, if it ever + // takes longer than info->timeout, this is probably due to a + // UART bug of some kind. So, we clamp the timeout parameter at + // 2*info->timeout. + // + if (!timeout || timeout > 2*info->timeout) + timeout = 2*info->timeout; +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); + printk("jiff=%lu...", jiffies); +#endif + while (!((lsr = serial_inp(info, COM_LSR_3)) & UART_LSR_TEMT)) { +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...", lsr, jiffies); +#endif + set_current_state(TASK_INTERRUPTIBLE); + schedule_timeout(char_time); + if (signal_pending(current)) + break; + if (timeout && time_after(jiffies, orig_jiffies + timeout)) + break; + } +#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT + printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies); +#endif +} + +/* + * rs_hangup() --- called by tty_hangup() when a hangup is signaled. + */ +static void rs_hangup(struct tty_struct *tty) +{ + struct async_struct * info = (struct async_struct *)tty->driver_data; + struct serial_state *state = info->state; + + if (serial_paranoia_check(info, tty->device, "rs_hangup")) + return; + + state = info->state; + + rs_flush_buffer(tty); + if (info->flags & ASYNC_CLOSING) + return; + shutdown(info); + info->event = 0; + state->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) +{ + DECLARE_WAITQUEUE(wait, current); + struct serial_state *state = info->state; + int retval; + int do_clocal = 0, extra_count = 0; + unsigned long flags; + + // + // 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 + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -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 (state->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, state->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", + state->line, state->count); +#endif + save_flags(flags); cli(); + if (!tty_hung_up_p(filp)) { + extra_count = 1; + state->count--; + } + restore_flags(flags); + info->blocked_open++; + while (1) { + save_flags(flags); cli(); +/* + if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && + (tty->termios->c_cflag & CBAUD)) + serial_out(info, UART_MCR, + serial_inp(info, UART_MCR) | + (UART_MCR_DTR | UART_MCR_RTS)); +*/ + restore_flags(flags); + set_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; +*/ + break; + if (signal_pending(current)) { + retval = -ERESTARTSYS; + break; + } +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + schedule(); + } + set_current_state(TASK_RUNNING); + remove_wait_queue(&info->open_wait, &wait); + if (extra_count) + state->count++; + info->blocked_open--; +#ifdef SERIAL_DEBUG_OPEN + printk("block_til_ready after blocking: ttys%d, count = %d\n", + info->line, state->count); +#endif + if (retval) + return retval; + info->flags |= ASYNC_NORMAL_ACTIVE; + return 0; +} + +static int get_async_struct(int line, struct async_struct **ret_info) +{ + struct async_struct *info; + struct serial_state *sstate; + + sstate = rs_table + line; + sstate->count++; + if (sstate->info) { + *ret_info = sstate->info; + return 0; + } + info = kmalloc(sizeof(struct async_struct), GFP_KERNEL); + if (!info) { + sstate->count--; + return -ENOMEM; + } + memset(info, 0, sizeof(struct async_struct)); + init_waitqueue_head(&info->open_wait); + init_waitqueue_head(&info->close_wait); + init_waitqueue_head(&info->delta_msr_wait); + info->magic = SERIAL_MAGIC; + info->port = sstate->port; + info->flags = sstate->flags; + info->io_type = sstate->io_type; + info->iomem_base = sstate->iomem_base; + info->iomem_reg_shift = sstate->iomem_reg_shift; + info->xmit_fifo_size = sstate->xmit_fifo_size; + info->line = line; + info->tqueue.routine = do_softint; + info->tqueue.data = info; + info->state = sstate; + if (sstate->info) { + kfree(info); + *ret_info = sstate->info; + return 0; + } + *ret_info = sstate->info = info; + 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. + */ +// struct tty_struct *key_tty=NULL; //mcli +static int rs_open(struct tty_struct *tty, struct file * filp) +{ + struct async_struct *info; + int retval, line; + unsigned long page; + + //printk("%s %s %d\n",__FILE__,__FUNCTION__,__LINE__); + + MOD_INC_USE_COUNT; + line = MINOR(tty->device) - tty->driver.minor_start; + if ((line < 0) || (line >= NR_PORTS)) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + retval = get_async_struct(line, &info); + if (retval) { + MOD_DEC_USE_COUNT; + return retval; + } + tty->driver_data = info; + info->tty = tty; + //key_tty=tty; //mcli + if (serial_paranoia_check(info, tty->device, "rs_open")) { + MOD_DEC_USE_COUNT; + return -ENODEV; + } + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, + info->state->count); +#endif +#if (LINUX_VERSION_CODE > 0x20100) + info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0; +#endif + + // + // This relies on lock_kernel() stuff so wants tidying for 2.5 + // + if (!tmp_buf) { + page = get_zeroed_page(GFP_KERNEL); + if (!page) { + MOD_DEC_USE_COUNT; + return -ENOMEM; + } + if (tmp_buf) + free_page(page); + else + tmp_buf = (unsigned char *) page; + } + + // + // If the port is the middle of closing, bail out now + // + if (tty_hung_up_p(filp) || + (info->flags & ASYNC_CLOSING)) { + if (info->flags & ASYNC_CLOSING) + interruptible_sleep_on(&info->close_wait); + MOD_DEC_USE_COUNT; +#ifdef SERIAL_DO_RESTART + return ((info->flags & ASYNC_HUP_NOTIFY) ? + -EAGAIN : -ERESTARTSYS); +#else + return -EAGAIN; +#endif + } + info->closing_wait = 3000; + // + // Start up serial port + // + retval = startup(info); + if (retval) { + MOD_DEC_USE_COUNT; + 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 + MOD_DEC_USE_COUNT; + return retval; + } + + if ((info->state->count == 1) && + (info->flags & ASYNC_SPLIT_TERMIOS)) { + if (tty->driver.subtype == SERIAL_TYPE_NORMAL) + *tty->termios = info->state->normal_termios; + else + *tty->termios = info->state->callout_termios; + change_speed(info, 0); + } +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE + if (sercons.cflag && sercons.index == line) { + tty->termios->c_cflag = sercons.cflag; + sercons.cflag = 0; + change_speed(info, 0); + } +#endif + info->session = current->session; + info->pgrp = current->pgrp; + +#ifdef SERIAL_DEBUG_OPEN + printk("rs_open ttys%d successful...", info->line); +#endif + + return 0; +} + +/* + * /proc fs routines.... + */ +static inline int line_info(char *buf, struct serial_state *state) +{ + struct async_struct *info = state->info, scr_info; +// char stat_buf[30], control, status; + char stat_buf[30]; + int ret; + //unsigned long flags; + + ret = sprintf(buf, "%d: uart:%s port:%lX irq:%d", + state->line, uart_config[state->type].name, + state->port, state->irq); + + if (!state->port || (state->type == PORT_UNKNOWN)) { + ret += sprintf(buf+ret, "\n"); + return ret; + } + + // + // Figure out the current RS-232 lines + // + if (!info) { + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->port = state->port; + info->flags = state->flags; + info->hub6 = state->hub6; + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + info->quot = 0; + info->tty = 0; + } + stat_buf[0] = 0; + stat_buf[1] = 0; +/* + save_flags(flags); cli(); + status = serial_in(info, UART_MSR); + control = info != &scr_info ? info->MCR : serial_in(info, UART_MCR); + restore_flags(flags); + + stat_buf[0] = 0; + stat_buf[1] = 0; + if (control & UART_MCR_RTS) + strcat(stat_buf, "|RTS"); + if (status & UART_MSR_CTS) + strcat(stat_buf, "|CTS"); + if (control & UART_MCR_DTR) + strcat(stat_buf, "|DTR"); + if (status & UART_MSR_DSR) + strcat(stat_buf, "|DSR"); + if (status & UART_MSR_DCD) + strcat(stat_buf, "|CD"); + if (status & UART_MSR_RI) + strcat(stat_buf, "|RI"); +*/ + if (info->quot) { + ret += sprintf(buf+ret, " baud:%d", + state->baud_base / info->quot); + } + + ret += sprintf(buf+ret, " tx:%d rx:%d", + state->icount.tx, state->icount.rx); + + if (state->icount.frame) + ret += sprintf(buf+ret, " fe:%d", state->icount.frame); + + if (state->icount.parity) + ret += sprintf(buf+ret, " pe:%d", state->icount.parity); + + if (state->icount.brk) + ret += sprintf(buf+ret, " brk:%d", state->icount.brk); + + if (state->icount.overrun) + ret += sprintf(buf+ret, " oe:%d", state->icount.overrun); + + // + // Last thing is the RS-232 status lines + // + ret += sprintf(buf+ret, " %s\n", stat_buf+1); + return ret; +} + +static int rs_read_proc(char *page, char **start, off_t off, int count, + int *eof, void *data) +{ + int i, len = 0, l; + off_t begin = 0; + + len += sprintf(page, "serinfo:1.0 driver:%s%s revision:%s\n", + serial_version, LOCAL_VERSTRING, serial_revdate); + for (i = 0; i < NR_PORTS && len < 4000; i++) { + l = line_info(page + len, &rs_table[i]); + len += l; + if (len+begin > off+count) + goto done; + if (len+begin < off) { + begin += len; + len = 0; + } + } + *eof = 1; +done: + if (off >= len+begin) + return 0; + *start = page + (off-begin); + return ((count < begin+len-off) ? count : begin+len-off); +} + +/* + * --------------------------------------------------------------------- + * 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 char serial_options[] __initdata = +#ifdef CONFIG_HUB6 + " HUB-6" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MANY_PORTS + " MANY_PORTS" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_MULTIPORT + " MULTIPORT" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_SHARE_IRQ + " SHARE_IRQ" +#define SERIAL_OPT +#endif +#ifdef CONFIG_SERIAL_DETECT_IRQ + " DETECT_IRQ" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PCI + " SERIAL_PCI" +#define SERIAL_OPT +#endif +#ifdef ENABLE_SERIAL_PNP + " ISAPNP" +#define SERIAL_OPT +#endif +#ifdef SERIAL_OPT + " enabled\n"; +#else + " no serial options enabled\n"; +#endif +#undef SERIAL_OPT + +static _INLINE_ void show_serial_version(void) +{ + printk(KERN_INFO "%s version %s%s (%s) with%s", serial_name, + serial_version, LOCAL_VERSTRING, serial_revdate, + serial_options); +} + +/* + * This routine detect the IRQ of a serial port by clearing OUT2 when + * no UART interrupt are requested (IER = 0) (*GPL*). This seems to work at + * each time, as long as no other device permanently request the IRQ. + * If no IRQ is detected, or multiple IRQ appear, this function returns 0. + * The variable "state" and the field "state->port" should not be null. + */ +static unsigned detect_uart_irq (struct serial_state * state) +{ +/* + int irq; + unsigned long irqs; + unsigned char save_mcr, save_ier; + struct async_struct scr_info; // serial_{in,out} because HUB6 + +#ifdef CONFIG_SERIAL_MANY_PORTS + unsigned char save_ICP=0; // no warning + unsigned short ICP=0; + + if (state->flags & ASYNC_FOURPORT) { + ICP = (state->port & 0xFE0) | 0x01F; + save_ICP = inb_p(ICP); + outb_p(0x80, ICP); + (void) inb_p(ICP); + } +#endif + scr_info.magic = SERIAL_MAGIC; + scr_info.state = state; + scr_info.port = state->port; + scr_info.flags = state->flags; +#ifdef CONFIG_HUB6 + scr_info.hub6 = state->hub6; +#endif + scr_info.io_type = state->io_type; + scr_info.iomem_base = state->iomem_base; + scr_info.iomem_reg_shift = state->iomem_reg_shift; + + // forget possible initially masked and pending IRQ + probe_irq_off(probe_irq_on()); + save_mcr = serial_inp(&scr_info, UART_MCR); + save_ier = serial_inp(&scr_info, UART_IER); + serial_outp(&scr_info, UART_MCR, UART_MCR_OUT1 | UART_MCR_OUT2); + + irqs = probe_irq_on(); + serial_outp(&scr_info, UART_MCR, 0); + udelay (10); + if (state->flags & ASYNC_FOURPORT) { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS); + } else { + serial_outp(&scr_info, UART_MCR, + UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2); + } + serial_outp(&scr_info, UART_IER, 0x0f); // enable all intrs + (void)serial_inp(&scr_info, UART_LSR); + (void)serial_inp(&scr_info, UART_RX); + (void)serial_inp(&scr_info, UART_IIR); + (void)serial_inp(&scr_info, UART_MSR); + serial_outp(&scr_info, UART_TX, 0xFF); + udelay (20); + irq = probe_irq_off(irqs); + + serial_outp(&scr_info, UART_MCR, save_mcr); + serial_outp(&scr_info, UART_IER, save_ier); +#ifdef CONFIG_SERIAL_MANY_PORTS + if (state->flags & ASYNC_FOURPORT) + outb_p(save_ICP, ICP); +#endif + return (irq > 0)? irq : 0; +*/ + return 0; +} + +/* + * This is a quickie test to see how big the FIFO is. + * It doesn't work at all the time, more's the pity. + */ +/* +static int size_fifo(struct async_struct *info) +{ + unsigned char old_fcr, old_mcr, old_dll, old_dlm; + int count; + + old_fcr = serial_inp(info, UART_FCR); + old_mcr = serial_inp(info, UART_MCR); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT); + serial_outp(info, UART_MCR, UART_MCR_LOOP); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + old_dll = serial_inp(info, UART_DLL); + old_dlm = serial_inp(info, UART_DLM); + serial_outp(info, UART_DLL, 0x01); + serial_outp(info, UART_DLM, 0x00); + serial_outp(info, UART_LCR, 0x03); + for (count = 0; count < 256; count++) + serial_outp(info, UART_TX, count); + mdelay(20); + for (count = 0; (serial_inp(info, UART_LSR) & UART_LSR_DR) && + (count < 256); count++) + serial_inp(info, UART_RX); + serial_outp(info, UART_FCR, old_fcr); + serial_outp(info, UART_MCR, old_mcr); + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, old_dll); + serial_outp(info, UART_DLM, old_dlm); + + return count; +} +*/ +/* + * This is a helper routine to autodetect StarTech/Exar/Oxsemi UART's. + * When this function is called we know it is at least a StarTech + * 16650 V2, but it might be one of several StarTech UARTs, or one of + * its clones. (We treat the broken original StarTech 16650 V1 as a + * 16550, and why not? Startech doesn't seem to even acknowledge its + * existence.) + * + * What evil have men's minds wrought... + */ +/* +static void autoconfig_startech_uarts(struct async_struct *info, + struct serial_state *state, + unsigned long flags) +{ + unsigned char scratch, scratch2, scratch3, scratch4; + + // + // First we check to see if it's an Oxford Semiconductor UART. + // + // If we have to do this here because some non-National + // Semiconductor clone chips lock up if you try writing to the + // LSR register (which serial_icr_read does) + // + if (state->type == PORT_16550A) { + // + // EFR [4] must be set else this test fails + // + // This shouldn't be necessary, but Mike Hudson + // (Exoray@isys.ca) claims that it's needed for 952 + // dual UART's (which are not recommended for new designs). + // + info->ACR = 0; + serial_out(info, UART_LCR, 0xBF); + serial_out(info, UART_EFR, 0x10); + serial_out(info, UART_LCR, 0x00); + // Check for Oxford Semiconductor 16C950 + scratch = serial_icr_read(info, UART_ID1); + scratch2 = serial_icr_read(info, UART_ID2); + scratch3 = serial_icr_read(info, UART_ID3); + + if (scratch == 0x16 && scratch2 == 0xC9 && + (scratch3 == 0x50 || scratch3 == 0x52 || + scratch3 == 0x54)) { + state->type = PORT_16C950; + state->revision = serial_icr_read(info, UART_REV) | + (scratch3 << 8); + return; + } + } + + // + // We check for a XR16C850 by setting DLL and DLM to 0, and + // then reading back DLL and DLM. If DLM reads back 0x10, + // then the UART is a XR16C850 and the DLL contains the chip + // revision. If DLM reads back 0x14, then the UART is a + // XR16C854. + // + // + + // Save the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + scratch3 = serial_inp(info, UART_DLL); + scratch4 = serial_inp(info, UART_DLM); + + serial_outp(info, UART_DLL, 0); + serial_outp(info, UART_DLM, 0); + scratch2 = serial_inp(info, UART_DLL); + scratch = serial_inp(info, UART_DLM); + serial_outp(info, UART_LCR, 0); + + if (scratch == 0x10 || scratch == 0x14) { + if (scratch == 0x10) + state->revision = scratch2; + state->type = PORT_16850; + return; + } + + // Restore the DLL and DLM + + serial_outp(info, UART_LCR, UART_LCR_DLAB); + serial_outp(info, UART_DLL, scratch3); + serial_outp(info, UART_DLM, scratch4); + serial_outp(info, UART_LCR, 0); + // + // We distinguish between the '654 and the '650 by counting + // how many bytes are in the FIFO. I'm using this for now, + // since that's the technique that was sent to me in the + // serial driver update, but I'm not convinced this works. + // I've had problems doing this in the past. -TYT + // + if (size_fifo(info) == 64) + state->type = PORT_16654; + else + state->type = PORT_16650V2; +} +*/ +/* + * 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 serial_state * state) +{ +// unsigned char status1, status2, scratch, scratch2, scratch3; +// unsigned char save_lcr, save_mcr; + struct async_struct *info, scr_info; + unsigned long flags; + + state->type = PORT_UNKNOWN; + +#ifdef SERIAL_DEBUG_AUTOCONF + printk("Testing ttyS%d (0x%04lx, 0x%04x)...\n", state->line, + state->port, (unsigned) state->iomem_base); +#endif + + if (!CONFIGURED_SERIAL_PORT(state)) + return; + + info = &scr_info; // This is just for serial_{in,out} + + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + + save_flags(flags); cli(); + +/* + if (!(state->flags & ASYNC_BUGGY_UART) && + !state->iomem_base) { + // + // 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); +#ifdef __i386__ + outb(0xff, 0x080); +#endif + scratch2 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, 0x0F); +#ifdef __i386__ + outb(0, 0x080); +#endif + scratch3 = serial_inp(info, UART_IER); + serial_outp(info, UART_IER, scratch); + if (scratch2 || scratch3 != 0x0F) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: simple autoconfig failed " + "(%02x, %02x)\n", state->line, + scratch2, scratch3); +#endif + restore_flags(flags); + return; // We failed; there's nothing here + } + } +*/ +/* + save_mcr = serial_in(info, UART_MCR); + save_lcr = serial_in(info, UART_LCR); +*/ + // + // 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 (!(state->flags & ASYNC_SKIP_TEST)) { + serial_outp(info, UART_MCR, UART_MCR_LOOP | 0x0A); + status1 = serial_inp(info, UART_MSR) & 0xF0; + serial_outp(info, UART_MCR, save_mcr); + if (status1 != 0x90) { +#ifdef SERIAL_DEBUG_AUTOCONF + printk("serial: ttyS%d: no UART loopback failed\n", + state->line); +#endif + restore_flags(flags); + return; + } + } + serial_outp(info, UART_LCR, 0xBF); // set up for StarTech test + serial_outp(info, UART_EFR, 0); // EFR is the same as FCR + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + scratch = serial_in(info, UART_IIR) >> 6; + switch (scratch) { + case 0: + state->type = PORT_16450; + break; + case 1: + state->type = PORT_UNKNOWN; + break; + case 2: + state->type = PORT_16550; + break; + case 3: + state->type = PORT_16550A; + break; + } + if (state->type == PORT_16550A) { + // Check for Startech UART's + serial_outp(info, UART_LCR, UART_LCR_DLAB); + if (serial_in(info, UART_EFR) == 0) { + state->type = PORT_16650; + } else { + serial_outp(info, UART_LCR, 0xBF); + if (serial_in(info, UART_EFR) == 0) + autoconfig_startech_uarts(info, state, flags); + } + } + if (state->type == PORT_16550A) { + // Check for TI 16750 + serial_outp(info, UART_LCR, save_lcr | UART_LCR_DLAB); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 7) { + // + // If this is a 16750, and not a cheap UART + // clone, then it should only go into 64 byte + // mode if the UART_FCR7_64BYTE bit was set + // while UART_LCR_DLAB was latched. + // + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + serial_outp(info, UART_LCR, 0); + serial_outp(info, UART_FCR, + UART_FCR_ENABLE_FIFO | UART_FCR7_64BYTE); + scratch = serial_in(info, UART_IIR) >> 5; + if (scratch == 6) + state->type = PORT_16750; + } + serial_outp(info, UART_FCR, UART_FCR_ENABLE_FIFO); + } +#if defined(CONFIG_SERIAL_RSA) && defined(MODULE) + if (state->type == PORT_16550A) { + int i; + + for (i = 0 ; i < PORT_RSA_MAX ; ++i) { + if (!probe_rsa[i] && !force_rsa[i]) + break; + if (((probe_rsa[i] != state->port) || + check_region(state->port + UART_RSA_BASE, 16)) && + (force_rsa[i] != state->port)) + continue; + if (!enable_rsa(info)) + continue; + state->type = PORT_RSA; + state->baud_base = SERIAL_RSA_BAUD_BASE; + break; + } + } +#endif + serial_outp(info, UART_LCR, save_lcr); + if (state->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)) + state->type = PORT_8250; + } +*/ + state->type = PORT_W90N745; + state->xmit_fifo_size = uart_config[state->type].dfl_xmit_fifo_size; + + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + return; + } + + if (info->port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + request_region(info->port + UART_RSA_BASE, 16, + "serial_rsa(auto)"); + else +#endif +*/ + request_region(info->port,8,"serial(auto)"); + } + + // + // Reset the UART. + // +/* +#ifdef CONFIG_SERIAL_RSA + if (state->type == PORT_RSA) + serial_outp(info, UART_RSA_FRR, 0); +#endif + serial_outp(info, UART_MCR, save_mcr); + serial_outp(info, UART_FCR, (UART_FCR_ENABLE_FIFO | + UART_FCR_CLEAR_RCVR | + UART_FCR_CLEAR_XMIT)); + serial_outp(info, UART_FCR, 0); +*/ + + serial_outp (info,COM_LCR_3, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_3, 6);//115200 + serial_outp (info,COM_DLM_3, 0); + + serial_outp (info,COM_LCR_3, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + //serial_outp (info,COM_IER_3, 0x03); /* RDA interrupt source on / LOOP disable */ + serial_outp (info,COM_MCR_3, 0x00); + + //serial_outp (info,COM_TOR_3, 0x80+0x20); /* Timeout if more than ??? bits xfer time */ + + serial_outp (info,COM_FCR_3, 0x8F); /* 8-byte FIFO trigger level, reset Tx and Rx FIFO */ + + + restore_flags(flags); +} + +int register_serial(struct serial_struct *req); +void unregister_serial(int line); + + +#if (LINUX_VERSION_CODE > 0x20100) +//EXPORT_SYMBOL(register_serial); +//EXPORT_SYMBOL(unregister_serial); +#else +static struct symbol_table serial_syms = { +#include + X(register_serial), + X(unregister_serial), +#include +}; +#endif + +/* + * The serial driver boot-time initialization code! + */ +static int __init rs_init(void) +{ + int i; + struct serial_state * state; + + init_bh(SERIAL_BH, do_serial_bh); + init_timer(&serial_timer); + serial_timer.function = rs_timer; + mod_timer(&serial_timer, jiffies + RS_STROBE_TIME); + + for (i = 0; i < NR_IRQS; i++) { + IRQ_ports[i] = 0; + IRQ_timeout[i] = 0; +#ifdef CONFIG_SERIAL_MULTIPORT + memset(&rs_multiport[i], 0, + sizeof(struct rs_multiport_struct)); +#endif + } +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE + // + // The interrupt of the serial console port + // can't be shared. + // + if (sercons.flags & CON_CONSDEV) { + for(i = 0; i < NR_PORTS; i++) + if (i != sercons.index && + rs_table[i].irq == rs_table[sercons.index].irq) + rs_table[i].irq = 0; + } +#endif + show_serial_version(); + + // Initialize the tty_driver structure + + memset(&serial_driver, 0, sizeof(struct tty_driver)); + serial_driver.magic = TTY_DRIVER_MAGIC; +#if (LINUX_VERSION_CODE > 0x20100) + serial_driver.driver_name = "serial3"; +#endif +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + serial_driver.name = "tts/%d"; +#else + serial_driver.name = "ttyS"; +#endif + serial_driver.major = TTY_MAJOR; + serial_driver.minor_start = 67 + SERIAL_DEV_OFFSET; + 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 = + B115200 | CS8 | CREAD | HUPCL | CLOCAL; + serial_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_NO_DEVFS; + 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; +#if (LINUX_VERSION_CODE >= 131394) // Linux 2.1.66 + serial_driver.break_ctl = rs_break; +#endif +#if (LINUX_VERSION_CODE >= 131343) + serial_driver.send_xchar = rs_send_xchar; + serial_driver.wait_until_sent = rs_wait_until_sent; + serial_driver.read_proc = rs_read_proc; +#endif + + // + // The callout device is just like normal device except for + // major number and the subtype code. + // + callout_driver = serial_driver; +#if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS)) + callout_driver.name = "cua/%d"; +#else + callout_driver.name = "cua"; +#endif + callout_driver.major = TTYAUX_MAJOR; + callout_driver.subtype = SERIAL_TYPE_CALLOUT; +#if (LINUX_VERSION_CODE >= 131343) + callout_driver.read_proc = 0; + callout_driver.proc_entry = 0; +#endif + + 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, state = rs_table; i < NR_PORTS; i++,state++) { + state->magic = SSTATE_MAGIC; + state->line = i; + state->type = PORT_UNKNOWN; + state->custom_divisor = 0; + state->close_delay = 5*HZ/10; + state->closing_wait = 30*HZ; + state->callout_termios = callout_driver.init_termios; + state->normal_termios = serial_driver.init_termios; + state->icount.cts = state->icount.dsr = + state->icount.rng = state->icount.dcd = 0; + state->icount.rx = state->icount.tx = 0; + state->icount.frame = state->icount.parity = 0; + state->icount.overrun = state->icount.brk = 0; + state->irq = irq_cannonicalize(state->irq); + if (state->hub6) + state->io_type = SERIAL_IO_HUB6; + if (state->port && check_region(state->port,8)) + continue; +#ifdef CONFIG_MCA + if ((state->flags & ASYNC_BOOT_ONLYMCA) && !MCA_bus) + continue; +#endif + if (state->flags & ASYNC_BOOT_AUTOCONF) + autoconfig(state); + } + + for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) { + if (state->type == PORT_UNKNOWN) + continue; + if ( (state->flags & ASYNC_BOOT_AUTOCONF) + && (state->flags & ASYNC_AUTO_IRQ) + && (state->port != 0)) + state->irq = detect_uart_irq(state); + printk(KERN_INFO "ttyS%02d%s at 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + (state->flags & ASYNC_FOURPORT) ? " FourPort" : "", + state->port, state->irq, + uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + } +#ifdef ENABLE_SERIAL_PCI + probe_serial_pci(); +#endif +#ifdef ENABLE_SERIAL_PNP + probe_serial_pnp(); +#endif + *(unsigned int volatile *)(0xfff83000) |= 0x3c0; + return 0; +} +/* + * This is for use by architectures that know their serial console + * attributes only at run time. Not to be invoked after rs_init(). + */ +/* +int __init early_serial_setup(struct serial_struct *req) +{ + int i = req->line; + + if (i >= NR_IRQS) + return(-ENOENT); + rs_table[i].magic = 0; + rs_table[i].baud_base = req->baud_base; + rs_table[i].port = req->port; + if (HIGH_BITS_OFFSET) + rs_table[i].port += (unsigned long) req->port_high << + HIGH_BITS_OFFSET; + rs_table[i].irq = req->irq; + rs_table[i].flags = req->flags; + rs_table[i].close_delay = req->close_delay; + rs_table[i].io_type = req->io_type; + rs_table[i].hub6 = req->hub6; + rs_table[i].iomem_base = req->iomem_base; + rs_table[i].iomem_reg_shift = req->iomem_reg_shift; + rs_table[i].type = req->type; + rs_table[i].xmit_fifo_size = req->xmit_fifo_size; + rs_table[i].custom_divisor = req->custom_divisor; + rs_table[i].closing_wait = req->closing_wait; + return(0); +} +*/ +/* + * register_serial and unregister_serial allows for 16x50 serial ports to be + * configured at run-time, to support PCMCIA modems. + */ + +/** + * register_serial - configure a 16x50 serial port at runtime + * @req: request structure + * + * Configure the serial port specified by the request. If the + * port exists and is in use an error is returned. If the port + * is not currently in the table it is added. + * + * The port is then probed and if neccessary the IRQ is autodetected + * If this fails an error is returned. + * + * On success the port is ready to use and the line number is returned. + */ +/* +int register_serial(struct serial_struct *req) +{ + int i; + unsigned long flags; + struct serial_state *state; + struct async_struct *info; + unsigned long port; + + port = req->port; + if (HIGH_BITS_OFFSET) + port += (unsigned long) req->port_high << HIGH_BITS_OFFSET; + + save_flags(flags); cli(); + for (i = 0; i < NR_PORTS; i++) { + if ((rs_table[i].port == port) && + (rs_table[i].iomem_base == req->iomem_base)) + break; + } +#ifdef __i386__ + if (i == NR_PORTS) { + for (i = 4; i < NR_PORTS; i++) + if ((rs_table[i].type == PORT_UNKNOWN) && + (rs_table[i].count == 0)) + break; + } +#endif + 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; + } + state = &rs_table[i]; + if (rs_table[i].count) { + restore_flags(flags); + printk("Couldn't configure serial #%d (port=%ld,irq=%d): " + "device already open\n", i, port, req->irq); + return -1; + } + state->irq = req->irq; + state->port = port; + state->flags = req->flags; + state->io_type = req->io_type; + state->iomem_base = req->iomem_base; + state->iomem_reg_shift = req->iomem_reg_shift; + if (req->baud_base) + state->baud_base = req->baud_base; + if ((info = state->info) != NULL) { + info->port = port; + info->flags = req->flags; + info->io_type = req->io_type; + info->iomem_base = req->iomem_base; + info->iomem_reg_shift = req->iomem_reg_shift; + } + autoconfig(state); + if (state->type == PORT_UNKNOWN) { + restore_flags(flags); + printk("register_serial(): autoconfig failed\n"); + return -1; + } + restore_flags(flags); + + if ((state->flags & ASYNC_AUTO_IRQ) && CONFIGURED_SERIAL_PORT(state)) + state->irq = detect_uart_irq(state); + + printk(KERN_INFO "ttyS%02d at %s 0x%04lx (irq = %d) is a %s\n", + state->line + SERIAL_DEV_OFFSET, + state->iomem_base ? "iomem" : "port", + state->iomem_base ? (unsigned long)state->iomem_base : + state->port, state->irq, uart_config[state->type].name); + tty_register_devfs(&serial_driver, 0, + serial_driver.minor_start + state->line); + tty_register_devfs(&callout_driver, 0, + callout_driver.minor_start + state->line); + return state->line + SERIAL_DEV_OFFSET; +} +*/ +/** + * unregister_serial - deconfigure a 16x50 serial port + * @line: line to deconfigure + * + * The port specified is deconfigured and its resources are freed. Any + * user of the port is disconnected as if carrier was dropped. Line is + * the port number returned by register_serial(). + */ +/* +void unregister_serial(int line) +{ + unsigned long flags; + struct serial_state *state = &rs_table[line]; + + save_flags(flags); cli(); + if (state->info && state->info->tty) + tty_hangup(state->info->tty); + state->type = PORT_UNKNOWN; + printk(KERN_INFO "tty%02d unloaded\n", state->line); + // These will be hidden, because they are devices that will no longer + // be available to the system. (ie, PCMCIA modems, once ejected) + // + tty_unregister_devfs(&serial_driver, + serial_driver.minor_start + state->line); + tty_unregister_devfs(&callout_driver, + callout_driver.minor_start + state->line); + restore_flags(flags); +} +*/ +static void __exit rs_fini(void) +{ + unsigned long flags; + int e1, e2; + int i; + struct async_struct *info; + + // printk("Unloading %s: version %s\n", serial_name, serial_version); + del_timer_sync(&serial_timer); + save_flags(flags); cli(); + remove_bh(SERIAL_BH); + 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 ((info = rs_table[i].info)) { + rs_table[i].info = NULL; + kfree(info); + } + if ((rs_table[i].type != PORT_UNKNOWN) && rs_table[i].port) { +/* +#ifdef CONFIG_SERIAL_RSA + if (rs_table[i].type == PORT_RSA) + release_region(rs_table[i].port + + UART_RSA_BASE, 16); + else +#endif +*/ + release_region(rs_table[i].port, 8); + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + if (rs_table[i].iomem_base) + iounmap(rs_table[i].iomem_base); +#endif +*/ + } +/* +#if defined(ENABLE_SERIAL_PCI) || defined(ENABLE_SERIAL_PNP) + for (i=0; i < NR_PCI_BOARDS; i++) { + struct pci_board_inst *brd = &serial_pci_board[i]; + + if (serial_pci_board[i].dev == 0) + continue; + if (brd->board.init_fn) + (brd->board.init_fn)(brd->dev, &brd->board, 0); + if (DEACTIVATE_FUNC(brd->dev)) + (DEACTIVATE_FUNC(brd->dev))(brd->dev); + } +#endif +*/ + if (tmp_buf) { + unsigned long pg = (unsigned long) tmp_buf; + tmp_buf = NULL; + free_page(pg); + } +/* +#ifdef ENABLE_SERIAL_PCI + if (serial_pci_driver.name[0]) + pci_unregister_driver (&serial_pci_driver); +#endif +*/ +} + +module_init(rs_init); +module_exit(rs_fini); +MODULE_DESCRIPTION("Standard/generic (dumb) serial driver"); +MODULE_AUTHOR("Theodore Ts'o "); +MODULE_LICENSE("GPL"); + +/* + * ------------------------------------------------------------ + * Serial console driver + * ------------------------------------------------------------ + */ +#ifdef CONFIG_SERIAL_W90N7453_CONSOLE + +#define BOTH_EMPTY (UART_LSR_TEMT | UART_LSR_THRE) + +static struct async_struct async_sercons; + +/* + * Wait for transmitter & holding register to empty + */ +static inline void wait_for_xmitr(struct async_struct *info) +{ + unsigned int status, tmout = 1000000; + + do { + status = serial_in(info, COM_LSR_3); + + if (status & UART_LSR_BI) + lsr_break_flag = UART_LSR_BI; + + if (--tmout == 0) + break; + } while((status & BOTH_EMPTY) != BOTH_EMPTY); +} + + +/* + * Print a string to the serial port trying not to disturb + * any possible real use of the port... + * + * The console must be locked when we get here.- + */ +static void serial_console_write(struct console *co, const char *s, + unsigned count) +{ + static struct async_struct *info = &async_sercons; + unsigned i; + /* + * Now, do each character + */ + for (i = 0; i < count; i++, s++) { + wait_for_xmitr(info); + + /* + * Send the character out. + * If a LF, also do CR... + */ + serial_out(info, COM_TX_3, *s); + if (*s == 10) { + wait_for_xmitr(info); + serial_out(info, COM_TX_3, 13); + } + } + + /* + * Finally, Wait for transmitter & holding register to empty + * and restore the IER + */ + wait_for_xmitr(info); +} + +static kdev_t serial_console_device(struct console *c) +{ + return MKDEV(TTY_MAJOR, 64 + c->index); +} + +/* + * Setup initial baud/bits/parity/flow control. We do two things here: + * - construct a cflag setting for the first rs_open() + * - initialize the serial port + * Return non-zero if we didn't find a serial port. + */ +static int __init serial_console_setup(struct console *co, char *options) +{ + static struct async_struct *info; + struct serial_state *state; + unsigned cval; + int baud = 115200; + int bits = 8; + int parity = 'n'; + int doflow = 0; + int cflag = CREAD | HUPCL | CLOCAL; + int quot = 0; + char *s; + if (options) { + baud = simple_strtoul(options, NULL, 10); + s = options; + while(*s >= '0' && *s <= '9') + s++; + if (*s) parity = *s++; + if (*s) bits = *s++ - '0'; + if (*s) doflow = (*s++ == 'r'); + } + + /* + * Now construct a cflag setting. + */ + switch(baud) { + case 1200: + cflag |= B1200; + break; + case 2400: + cflag |= B2400; + break; + case 4800: + cflag |= B4800; + break; + case 19200: + cflag |= B19200; + break; + case 38400: + cflag |= B38400; + break; + case 57600: + cflag |= B57600; + break; + case 115200: + cflag |= B115200; + break; + case 9600: + default: + cflag |= B9600; + /* + * Set this to a sane value to prevent a divide error + */ + baud = 9600; + break; + } + switch(bits) { + case 7: + cflag |= CS7; + break; + default: + case 8: + cflag |= CS8; + break; + } + switch(parity) { + case 'o': case 'O': + cflag |= PARODD; + break; + case 'e': case 'E': + cflag |= PARENB; + break; + } + co->cflag = cflag; + + /* + * Divisor, bytesize and parity + */ + state = rs_table + co->index; + if (doflow) + state->flags |= ASYNC_CONS_FLOW; + info = &async_sercons; + info->magic = SERIAL_MAGIC; + info->state = state; + info->port = state->port; + info->flags = state->flags; +#ifdef CONFIG_HUB6 + info->hub6 = state->hub6; +#endif + info->io_type = state->io_type; + info->iomem_base = state->iomem_base; + info->iomem_reg_shift = state->iomem_reg_shift; + quot = state->baud_base / baud; + cval = cflag & (CSIZE | CSTOPB); +#if defined(__powerpc__) || defined(__alpha__) + cval >>= 8; +#else /* !__powerpc__ && !__alpha__ */ + cval >>= 4; +#endif /* !__powerpc__ && !__alpha__ */ + if (cflag & PARENB) + { + cval |= UART_LCR_PARITY; + if (!(cflag & PARODD)) + cval |= UART_LCR_EPAR; + } + + /* + * Disable UART interrupts, set DTR and RTS high + * and set speed. + */ + /* + serial_out(info, UART_LCR, cval | UART_LCR_DLAB); // set DLAB + serial_out(info, UART_DLL, quot & 0xff); // LS of divisor + serial_out(info, UART_DLM, quot >> 8); // MS of divisor + serial_out(info, UART_LCR, cval); // reset DLAB + serial_out(info, UART_IER, 0); + serial_out(info, UART_MCR, UART_MCR_DTR | UART_MCR_RTS); + */ + /* + * If we read 0xff from the LSR, there is no UART here. + */ + /* + if (serial_in(info, UART_LSR) == 0xff) + return -1; + */ + serial_outp (info,COM_LCR_3, 0x80); /* select divisor latch registers */ + + serial_outp (info,COM_DLL_3, 6); + serial_outp (info,COM_DLM_3, 0); + + serial_outp (info,COM_LCR_3, 0x03); /* none parity, 8 data bits, 1 stop bits */ + + + return 0; +} + +static struct console sercons = { + name: "ttyS", + write: serial_console_write, + device: serial_console_device, + setup: serial_console_setup, + flags: CON_PRINTBUFFER, + index: -1, +}; + +/* + * Register console. + */ +void __init winbond_console_init(void) +{ + register_console(&sercons); +} +#endif + +/* + Local variables: + compile-command: "gcc -D__KERNEL__ -I../../include -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -fno-strict-aliasing -pipe -fno-strength-reduce -march=i586 -DMODULE -DMODVERSIONS -include ../../include/linux/modversions.h -DEXPORT_SYMTAB -c serial.c" + End: +*/ diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.c b/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.c new file mode 100755 index 0000000..a8765c4 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.c @@ -0,0 +1,331 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. + * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_usi.c + * + * VERSION + * 1.0 + * + * DESCRIPTION + * USI driver supported for W90n710. + * + * FUNCTIONS + * all functions, if they has return value, return 0 if they success, others failed. + * + * HISTORY + * 2006/01/10 Created by QFu + * + * REMARK + * None + *************************************************************************/ +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include "w90n745_usi.h" + +//#define USI_DEBUG +//#define USI_DEBUG_ENABLE_ENTER_LEAVE +//#define USI_DEBUG_ENABLE_MSG +//#define USI_DEBUG_ENABLE_MSG2 + +#ifdef USI_DEBUG +#define PDEBUG(fmt, arg...) printk(fmt, ##arg) +#else +#define PDEBUG(fmt, arg...) +#endif + +#ifdef USI_DEBUG_ENABLE_ENTER_LEAVE +#define ENTER() PDEBUG("[%-20s] : Enter...\n", __FUNCTION__) +#define LEAVE() PDEBUG("[%-20s] : Leave...\n", __FUNCTION__) +#else +#define ENTER() +#define LEAVE() +#endif + +#ifdef USI_DEBUG_ENABLE_MSG +#define MSG(msg) PDEBUG("[%-20s] : %s\n", __FUNCTION__, msg) +#else +#define MSG(msg) +#endif + +#ifdef USI_DEBUG_ENABLE_MSG2 +#define MSG2(fmt, arg...) PDEBUG("[%-20s] : "fmt, __FUNCTION__, ##arg) +#define PRNBUF(buf, count) {int i;MSG2("Data: ");for(i=0;ibit_len) - 1; + + MSG2("bit_len : %d, mask : %x\n", data_ptr->bit_len, mask); + + usi_outl(data_ptr->write_data & mask , USI_TX0); /* write data to hardware buffer */ + + MSG2("-> %x\n", data_ptr->write_data & mask); + + reg = (global_parameter.sleep << 12) | + (global_parameter.lsb << 10) | + (data_ptr->bit_len << 3) | + (global_parameter.tx_neg << 2) | + (global_parameter.rx_neg << 1) | 0x20001; + + trans_finish = 0; + usi_outl(reg, USI_CNTRL); /* start */ + wait_event_interruptible(wq, trans_finish != 0); + + data_ptr->read_data = usi_inl(USI_RX0) & mask; + + MSG2("<- %x\n", data_ptr->read_data & mask); + + LEAVE(); + + return 0; + +} + +static int usi_ioctl(struct inode *inode, struct file *flip, unsigned int cmd, unsigned long arg) +{ + int err = 0; + struct usi_parameter tmp_parameter; + struct usi_data tmp_data; + + ENTER(); + + if(_IOC_TYPE(cmd) != USI_IOC_MAGIC) return -ENOTTY; + if(_IOC_NR(cmd) > USI_IOC_MAXNR) return -ENOTTY; + + if(_IOC_DIR(cmd) & _IOC_READ) + err = !access_ok(VERIFY_WRITE, (void *)arg, _IOC_SIZE(cmd)); + else if(_IOC_DIR(cmd) & _IOC_WRITE) + err = !access_ok(VERIFY_READ, (void *)arg, _IOC_SIZE(cmd)); + + if(err) + return -EFAULT; + + switch(cmd) { + case USI_IOC_GETPARAMETER: + if (copy_to_user((void *)arg, &global_parameter, + sizeof(struct usi_parameter))) + return -EFAULT; + break; + case USI_IOC_SETPARAMETER: + if (copy_from_user(&tmp_parameter, (void *)arg, + sizeof(struct usi_parameter))) + return -EFAULT; + memcpy(&global_parameter, &tmp_parameter, + sizeof(struct usi_parameter)); + + usi_outl(global_parameter.divider, USI_DIVIDER); /* update clock */ + + break; + + case USI_IOC_SELECTSLAVE: + if (arg < -1 && arg > 1) + return -EPERM; + if (arg == -1) + usi_deselect_slave(); + else + usi_select_slave(arg); + break; + + case USI_IOC_TRANSIT: + if (copy_from_user(&tmp_data, (void *)arg, sizeof(tmp_data))) + return -EFAULT; + err = usi_transit(&tmp_data); + if (err) + return err; + if (copy_to_user((void *)arg, &tmp_data, sizeof(tmp_data))) + return -EFAULT; + break; + + default: + return -ENOTTY; + + } + + LEAVE(); + + return 0; +} + + +static int usi_open(struct inode *inode, struct file *filp) +{ + u32 reg; + int retval = -EBUSY; + + ENTER(); + + if (! atomic_dec_and_test (&usi_available)) + goto failed; + + global_parameter.active_level = 0; + global_parameter.lsb = 0; + global_parameter.rx_neg = 0; + global_parameter.tx_neg = 1; + global_parameter.divider = 0x1; + global_parameter.sleep = 0; + slave_select = 0; + + reg = usi_inl(0xFFF83050) & 0xfff00fff; + reg |= 0xaa000; + usi_outl(reg, 0xFFF83050); + + reg = usi_inl(USI_CNTRL); + reg |= 0x20000; + usi_outl(reg, USI_CNTRL); + + MSG2("GPIO 5 : %x\n", usi_inl(0xFFF83050)); + + usi_outl(global_parameter.divider, USI_DIVIDER); /* set clock */ + + if ((retval = request_irq(INT_SPI, usi_interrupt, SA_INTERRUPT, "usi", NULL)) < 0) { + printk("usi : request irq error\n"); + goto failed; + } + + LEAVE(); + + return 0; /* success */ + +failed: + + atomic_inc(&usi_available); /* release the device */ + + return retval; +} + +static int usi_release(struct inode *inode, struct file *flip) +{ + u32 reg; + + ENTER(); + + reg = usi_inl(USI_CNTRL); + reg &= 0xffff; + usi_outl(reg, USI_CNTRL); + + free_irq(INT_SPI, NULL); + usi_deselect_slave(); + atomic_inc(&usi_available); /* release the device */ + + LEAVE(); + + return 0; +} + + +struct file_operations usi_fops = +{ + owner: THIS_MODULE, + open: usi_open, + release: usi_release, + ioctl: usi_ioctl, +}; + +static int __init usi_init(void) +{ + u32 reg; + int result; + + init_waitqueue_head(&wq); + + /* every things ok, now, we can register char device safely */ + + result = register_chrdev(USI_MAJOR, "usi", &usi_fops); + if( result < 0){ + unregister_chrdev(USI_MAJOR, "usi"); + printk("usi : can't get major %d\n", USI_MAJOR); + goto failed; + } + + printk("USI driver has been installed successfully!\n"); + +failed: + + return result; + +} + +static void __exit usi_exit(void) +{ + unregister_chrdev(USI_MAJOR, "usi"); +} + +module_init(usi_init); +module_exit(usi_exit); + diff --git a/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.h b/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.h new file mode 100755 index 0000000..14c8867 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/w90n745_usi.h @@ -0,0 +1,73 @@ +/**************************************************************************** + * + * Copyright (c) 2004 - 2006 Winbond Electronics Corp. All rights reserved. + * + ****************************************************************************/ + +/**************************************************************************** + * + * FILENAME + * w90n745_usi.h + * + * VERSION + * 1.0 + * + * DESCRIPTION + * USI driver supported for W90n710. + * + * FUNCTIONS + * all functions, if they has return value, return 0 if they success, others failed. + * + * HISTORY + * 2006/01/10 Created by QFu + * + * REMARK + * None + *************************************************************************/ +#ifndef _W90N745_USI_H_ +#define _W90N745_USI_H_ + +#include +#include + +#define USI_BASE 0xfff86200 +#define USI_SIZE 0x30 + +#define USI_CNTRL (USI_BASE | 0x00) +#define USI_DIVIDER (USI_BASE | 0x04) +#define USI_SSR (USI_BASE | 0x08) + +#define USI_RX0 (USI_BASE | 0x10) +#define USI_RX1 (USI_BASE | 0x14) +#define USI_RX2 (USI_BASE | 0x18) +#define USI_RX3 (USI_BASE | 0x1c) + +#define USI_TX0 (USI_BASE | 0x10) +#define USI_TX1 (USI_BASE | 0x14) +#define USI_TX2 (USI_BASE | 0x18) +#define USI_TX3 (USI_BASE | 0x1c) + +struct usi_parameter{ + unsigned int active_level:1; + unsigned int lsb:1, tx_neg:1, rx_neg:1, divider:16; + unsigned int sleep:4; +}; + +struct usi_data{ + unsigned int write_data; + unsigned int read_data; + unsigned int bit_len; +}; + +#define USI_MAJOR 231 + +#define USI_IOC_MAGIC 'u' +#define USI_IOC_MAXNR 3 + +#define USI_IOC_GETPARAMETER _IOR(USI_IOC_MAGIC, 0, struct usi_parameter *) +#define USI_IOC_SETPARAMETER _IOW(USI_IOC_MAGIC, 1, struct usi_parameter *) +#define USI_IOC_SELECTSLAVE _IOW(USI_IOC_MAGIC, 2, int) +#define USI_IOC_TRANSIT _IOW(USI_IOC_MAGIC, 3, struct usi_data *) + +#endif /* _W90N745_USI_H_ */ + diff --git a/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c b/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c new file mode 100644 index 0000000..0cecacc --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c @@ -0,0 +1,374 @@ +/* Do not edit this file! It was automatically generated by */ +/* loadkeys --mktable defkeymap.map > defkeymap.c */ + +#include +#include +#include + +u_short plain_map[NR_KEYS] = { + 0xf200, 0xf108, 0xf200, 0xf104, 0xf102, 0xf100, 0xf101, 0xf10b, + 0xf200, 0xf109, 0xf107, 0xf105, 0xf103, 0xf009, 0xf060, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xfb71, 0xf031, 0xf200, + 0xf200, 0xf200, 0xfb7a, 0xfb73, 0xfb61, 0xfb77, 0xf032, 0xf200, + 0xf200, 0xfb63, 0xfb78, 0xfb64, 0xfb65, 0xf034, 0xf033, 0xf200, + 0xf200, 0xf020, 0xfb76, 0xfb66, 0xfb74, 0xfb72, 0xf035, 0xf200, + 0xf200, 0xfb6e, 0xfb62, 0xfb68, 0xfb67, 0xfb79, 0xf036, 0xf200, + 0xf200, 0xf200, 0xfb6d, 0xfb6a, 0xfb75, 0xf037, 0xf038, 0xf200, + 0xf200, 0xf02c, 0xfb6b, 0xfb69, 0xfb6f, 0xf030, 0xf039, 0xf200, + 0xf114, 0xf02e, 0xf30d, 0xfb6c, 0xf03b, 0xfb70, 0xf02d, 0xf200, + 0xf200, 0xf200, 0xf027, 0xf200, 0xf05b, 0xf03d, 0xf03c, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf05d, 0xf200, 0xf05c, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf07f, 0xf603, + 0xf118, 0xf301, 0xf602, 0xf304, 0xf307, 0xf119, 0xf115, 0xf116, + 0xf300, 0xf310, 0xf302, 0xf305, 0xf306, 0xf308, 0xf01b, 0xf208, + 0xf10a, 0xf30a, 0xf303, 0xf30b, 0xf30c, 0xf309, 0xf209, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf106, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_map[NR_KEYS] = { + 0xf200, 0xf112, 0xf200, 0xf10e, 0xf10c, 0xf10a, 0xf10b, 0xf10b, + 0xf200, 0xf113, 0xf111, 0xf10f, 0xf10d, 0xf009, 0xf07e, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xfb51, 0xf021, 0xf200, + 0xf200, 0xf200, 0xfb5a, 0xfb53, 0xfb41, 0xfb57, 0xf040, 0xf200, + 0xf200, 0xfb43, 0xfb58, 0xfb44, 0xfb45, 0xf024, 0xf023, 0xf200, + 0xf200, 0xf020, 0xfb56, 0xfb46, 0xfb54, 0xfb52, 0xf025, 0xf200, + 0xf200, 0xfb4e, 0xfb42, 0xfb48, 0xfb47, 0xfb59, 0xf05e, 0xf200, + 0xf200, 0xf200, 0xfb4d, 0xfb4a, 0xfb55, 0xf026, 0xf02a, 0xf200, + 0xf200, 0xf03c, 0xfb4b, 0xfb49, 0xfb4f, 0xf029, 0xf028, 0xf200, + 0xf114, 0xf03e, 0xf30d, 0xfb4c, 0xf03a, 0xfb50, 0xf05f, 0xf200, + 0xf200, 0xf200, 0xf022, 0xf200, 0xf07b, 0xf02b, 0xf03e, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf07d, 0xf200, 0xf07c, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf07f, 0xf603, + 0xf20b, 0xf301, 0xf602, 0xf304, 0xf307, 0xf20a, 0xf115, 0xf116, + 0xf300, 0xf310, 0xf302, 0xf305, 0xf306, 0xf308, 0xf01b, 0xf213, + 0xf10a, 0xf30a, 0xf303, 0xf30b, 0xf30c, 0xf309, 0xf203, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf110, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short altgr_map[NR_KEYS] = { + 0xf200, 0xf514, 0xf200, 0xf510, 0xf50e, 0xf50c, 0xf50d, 0xf517, + 0xf200, 0xf515, 0xf513, 0xf511, 0xf50f, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xfb71, 0xf200, 0xf200, + 0xf200, 0xf200, 0xfb7a, 0xfb73, 0xf914, 0xfb77, 0xf040, 0xf200, + 0xf200, 0xf916, 0xfb78, 0xf917, 0xf918, 0xf024, 0xf200, 0xf200, + 0xf200, 0xf200, 0xfb76, 0xf919, 0xfb74, 0xfb72, 0xf200, 0xf200, + 0xf200, 0xfb6e, 0xf915, 0xfb68, 0xfb67, 0xfb79, 0xf200, 0xf200, + 0xf200, 0xf200, 0xfb6d, 0xfb6a, 0xfb75, 0xf07b, 0xf05b, 0xf200, + 0xf200, 0xf200, 0xfb6b, 0xfb69, 0xfb6f, 0xf07d, 0xf05d, 0xf200, + 0xf114, 0xf200, 0xf30d, 0xfb6c, 0xf200, 0xfb70, 0xf05c, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf07c, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf07e, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf200, 0xf603, + 0xf118, 0xf90b, 0xf602, 0xf90e, 0xf911, 0xf119, 0xf115, 0xf116, + 0xf90a, 0xf310, 0xf90c, 0xf90f, 0xf910, 0xf912, 0xf200, 0xf208, + 0xf516, 0xf30a, 0xf90d, 0xf30b, 0xf30c, 0xf913, 0xf202, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf512, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_map[NR_KEYS] = { + 0xf200, 0xf108, 0xf200, 0xf104, 0xf102, 0xf100, 0xf101, 0xf10b, + 0xf200, 0xf109, 0xf107, 0xf105, 0xf103, 0xf200, 0xf000, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xf011, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf01a, 0xf013, 0xf001, 0xf017, 0xf000, 0xf200, + 0xf200, 0xf003, 0xf018, 0xf004, 0xf005, 0xf01c, 0xf01b, 0xf200, + 0xf200, 0xf000, 0xf016, 0xf006, 0xf014, 0xf012, 0xf01d, 0xf200, + 0xf200, 0xf00e, 0xf002, 0xf008, 0xf007, 0xf019, 0xf01e, 0xf200, + 0xf200, 0xf200, 0xf00d, 0xf00a, 0xf015, 0xf01f, 0xf07f, 0xf200, + 0xf200, 0xf200, 0xf00b, 0xf009, 0xf00f, 0xf200, 0xf200, 0xf200, + 0xf114, 0xf20e, 0xf30d, 0xf00c, 0xf200, 0xf010, 0xf01f, 0xf200, + 0xf200, 0xf200, 0xf007, 0xf200, 0xf01b, 0xf200, 0xf200, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf01d, 0xf200, 0xf01c, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf008, 0xf603, + 0xf118, 0xf301, 0xf602, 0xf304, 0xf307, 0xf119, 0xf115, 0xf116, + 0xf300, 0xf310, 0xf302, 0xf305, 0xf306, 0xf308, 0xf200, 0xf208, + 0xf10a, 0xf30a, 0xf303, 0xf30b, 0xf30c, 0xf309, 0xf204, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf106, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short shift_ctrl_map[NR_KEYS] = { + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xf011, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf01a, 0xf013, 0xf001, 0xf017, 0xf000, 0xf200, + 0xf200, 0xf003, 0xf018, 0xf004, 0xf005, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf016, 0xf006, 0xf014, 0xf012, 0xf200, 0xf200, + 0xf200, 0xf00e, 0xf002, 0xf008, 0xf007, 0xf019, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf00d, 0xf00a, 0xf015, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf00b, 0xf009, 0xf00f, 0xf200, 0xf200, 0xf200, + 0xf114, 0xf200, 0xf30d, 0xf00c, 0xf200, 0xf010, 0xf01f, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf200, 0xf603, + 0xf118, 0xf301, 0xf602, 0xf304, 0xf307, 0xf119, 0xf115, 0xf116, + 0xf300, 0xf310, 0xf302, 0xf305, 0xf306, 0xf308, 0xf200, 0xf208, + 0xf200, 0xf30a, 0xf303, 0xf30b, 0xf30c, 0xf309, 0xf200, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf200, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short alt_map[NR_KEYS] = { + 0xf200, 0xf508, 0xf200, 0xf504, 0xf502, 0xf500, 0xf501, 0xf50b, + 0xf200, 0xf509, 0xf507, 0xf505, 0xf503, 0xf809, 0xf860, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xf871, 0xf831, 0xf200, + 0xf200, 0xf200, 0xf87a, 0xf873, 0xf861, 0xf877, 0xf200, 0xf200, + 0xf200, 0xf863, 0xf878, 0xf864, 0xf865, 0xf834, 0xf833, 0xf200, + 0xf200, 0xf820, 0xf876, 0xf866, 0xf874, 0xf872, 0xf835, 0xf200, + 0xf200, 0xf86e, 0xf862, 0xf868, 0xf867, 0xf879, 0xf836, 0xf200, + 0xf200, 0xf200, 0xf86d, 0xf86a, 0xf875, 0xf837, 0xf838, 0xf200, + 0xf200, 0xf82c, 0xf86b, 0xf869, 0xf86f, 0xf830, 0xf839, 0xf200, + 0xf114, 0xf82e, 0xf30d, 0xf86c, 0xf83b, 0xf870, 0xf82d, 0xf200, + 0xf200, 0xf200, 0xf827, 0xf200, 0xf85b, 0xf83d, 0xf83c, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf85d, 0xf200, 0xf85c, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf87f, 0xf603, + 0xf118, 0xf901, 0xf211, 0xf904, 0xf907, 0xf119, 0xf115, 0xf116, + 0xf900, 0xf310, 0xf902, 0xf905, 0xf906, 0xf908, 0xf81b, 0xf208, + 0xf50a, 0xf30a, 0xf903, 0xf30b, 0xf30c, 0xf909, 0xf209, 0xf200, + 0xf210, 0xf117, 0xf600, 0xf506, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +u_short ctrl_alt_map[NR_KEYS] = { + 0xf200, 0xf508, 0xf200, 0xf504, 0xf502, 0xf500, 0xf501, 0xf50b, + 0xf200, 0xf509, 0xf507, 0xf505, 0xf503, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf703, 0xf700, 0xf200, 0xf702, 0xf811, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf81a, 0xf813, 0xf801, 0xf817, 0xf200, 0xf200, + 0xf200, 0xf803, 0xf818, 0xf804, 0xf805, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf816, 0xf806, 0xf814, 0xf812, 0xf200, 0xf200, + 0xf200, 0xf80e, 0xf802, 0xf808, 0xf807, 0xf819, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf80d, 0xf80a, 0xf815, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf80b, 0xf809, 0xf80f, 0xf200, 0xf200, 0xf200, + 0xf114, 0xf200, 0xf30d, 0xf80c, 0xf200, 0xf810, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf206, + 0xf207, 0xf700, 0xf30e, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf01c, 0xf701, 0xf205, 0xf200, 0xf603, + 0xf118, 0xf301, 0xf602, 0xf304, 0xf307, 0xf119, 0xf115, 0xf20c, + 0xf300, 0xf20c, 0xf302, 0xf305, 0xf306, 0xf308, 0xf200, 0xf208, + 0xf50a, 0xf30a, 0xf303, 0xf30b, 0xf30c, 0xf309, 0xf200, 0xf200, + 0xf601, 0xf117, 0xf600, 0xf506, 0xf11a, 0xf10c, 0xf10d, 0xf11b, + 0xf11c, 0xf110, 0xf311, 0xf11d, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, + 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, 0xf200, +}; + +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; diff --git a/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.map b/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.map new file mode 100755 index 0000000..1e289e9 --- /dev/null +++ b/uClinux-2.4.20-uc1/drivers/char/winbond_keymap.map @@ -0,0 +1,357 @@ +# W90N745 kernel keymap. This uses 7 modifier combinations. +keymaps 0-2,4-5,8,12 +# Change the above line into +# keymaps 0-2,4-6,8,12 +# in case you want the entries +# altgr control keycode 83 = Boot +# altgr control keycode 111 = Boot +# below. +# +# In fact AltGr is used very little, and one more keymap can +# be saved by mapping AltGr to Alt (and adapting a few entries): +# keycode 100 = Alt +# +keycode 118 = Escape Escape + alt keycode 118 = Meta_Escape +keycode 22 = one exclam + alt keycode 22 = Meta_one +keycode 30 = two at at + control keycode 30 = nul + shift control keycode 30 = nul + alt keycode 3 = Meta_two +keycode 38 = three numbersign + control keycode 38 = Escape + alt keycode 38 = Meta_three +keycode 37 = four dollar dollar + control keycode 37 = Control_backslash + alt keycode 37 = Meta_four +keycode 46 = five percent + control keycode 46 = Control_bracketright + alt keycode 46 = Meta_five +keycode 54 = six asciicircum + control keycode 54 = Control_asciicircum + alt keycode 54 = Meta_six +keycode 61 = seven ampersand braceleft + control keycode 61 = Control_underscore + alt keycode 61 = Meta_seven +keycode 62 = eight asterisk bracketleft + control keycode 62 = Delete + alt keycode 62 = Meta_eight +keycode 70 = nine parenleft bracketright + alt keycode 70 = Meta_nine +keycode 69 = zero parenright braceright + alt keycode 69 = Meta_zero +keycode 78 = minus underscore backslash + control keycode 78 = Control_underscore + shift control keycode 78 = Control_underscore + alt keycode 78 = Meta_minus +keycode 85 = equal plus + alt keycode 85 = Meta_equal +keycode 102 = Delete Delete + control keycode 102 = BackSpace + alt keycode 102 = Meta_Delete +keycode 13 = Tab Tab + alt keycode 13 = Meta_Tab +keycode 21 = q +keycode 29 = w +keycode 36 = e + altgr keycode 36 = Hex_E +keycode 45 = r +keycode 44 = t +keycode 53 = y +keycode 60 = u +keycode 67 = i +keycode 68 = o +keycode 77 = p +keycode 84 = bracketleft braceleft + control keycode 84 = Escape + alt keycode 84 = Meta_bracketleft +keycode 91 = bracketright braceright asciitilde + control keycode 91 = Control_bracketright + alt keycode 91 = Meta_bracketright +keycode 90 = Return + alt keycode 90 = Meta_Control_m +keycode 20 = Control +keycode 28 = a + altgr keycode 28 = Hex_A +keycode 27 = s +keycode 35 = d + altgr keycode 35 = Hex_D +keycode 43 = f + altgr keycode 43 = Hex_F +keycode 52 = g +keycode 51 = h +keycode 59 = j +keycode 66 = k +keycode 75 = l +keycode 76 = semicolon colon + alt keycode 76 = Meta_semicolon +keycode 82 = apostrophe quotedbl + control keycode 82 = Control_g + alt keycode 82 = Meta_apostrophe +keycode 14 = grave asciitilde + control keycode 14 = nul + alt keycode 14 = Meta_grave +keycode 18 = Shift +keycode 93 = backslash bar + control keycode 93 = Control_backslash + alt keycode 93 = Meta_backslash +keycode 26 = z +keycode 34 = x +keycode 33 = c + altgr keycode 33 = Hex_C +keycode 42 = v +keycode 50 = b + altgr keycode 50 = Hex_B +keycode 49 = n +keycode 58 = m +keycode 65 = comma less + alt keycode 65 = Meta_comma +keycode 73 = period greater + control keycode 73 = Compose + alt keycode 73 = Meta_period +keycode 74 = slash question + control keycode 74 = Delete + alt keycode 74 = Meta_slash +keycode 89 = Shift +keycode 124 = KP_Multiply +keycode 17 = Alt +keycode 41 = space space + control keycode 41 = nul + alt keycode 41 = Meta_space +keycode 88 = Caps_Lock +keycode 5 = F1 F11 Console_13 + control keycode 5 = F1 + alt keycode 5 = Console_1 + control alt keycode 5 = Console_1 +keycode 6 = F2 F12 Console_14 + control keycode 6 = F2 + alt keycode 6 = Console_2 + control alt keycode 6 = Console_2 +keycode 4 = F3 F13 Console_15 + control keycode 4 = F3 + alt keycode 4 = Console_3 + control alt keycode 4 = Console_3 +keycode 12 = F4 F14 Console_16 + control keycode 12 = F4 + alt keycode 12 = Console_4 + control alt keycode 12 = Console_4 +keycode 3 = F5 F15 Console_17 + control keycode 3 = F5 + alt keycode 3 = Console_5 + control alt keycode 3 = Console_5 +keycode 11 = F6 F16 Console_18 + control keycode 11 = F6 + alt keycode 11 = Console_6 + control alt keycode 11 = Console_6 +keycode 131 = F7 F17 Console_19 + control keycode 131 = F7 + alt keycode 131 = Console_7 + control alt keycode 131 = Console_7 +keycode 10 = F8 F18 Console_20 + control keycode 10 = F8 + alt keycode 10 = Console_8 + control alt keycode 10 = Console_8 +keycode 1 = F9 F19 Console_21 + control keycode 1 = F9 + alt keycode 1 = Console_9 + control alt keycode 1 = Console_9 +keycode 9 = F10 F20 Console_22 + control keycode 9 = F10 + alt keycode 9 = Console_10 + control alt keycode 9 = Console_10 +keycode 119 = Num_Lock + shift keycode 119 = Bare_Num_Lock +keycode 126 = Scroll_Lock Show_Memory Show_Registers + control keycode 126 = Show_State + alt keycode 126 = Scroll_Lock +keycode 108 = KP_7 + alt keycode 108 = Ascii_7 + altgr keycode 108 = Hex_7 +keycode 117 = KP_8 + alt keycode 117 = Ascii_8 + altgr keycode 117 = Hex_8 +keycode 125 = KP_9 + alt keycode 125 = Ascii_9 + altgr keycode 125 = Hex_9 +keycode 123 = KP_Subtract +keycode 107 = KP_4 + alt keycode 107 = Ascii_4 + altgr keycode 107 = Hex_4 +keycode 115 = KP_5 + alt keycode 115 = Ascii_5 + altgr keycode 115 = Hex_5 +keycode 116 = KP_6 + alt keycode 116 = Ascii_6 + altgr keycode 116 = Hex_6 +keycode 121 = KP_Add +keycode 105 = KP_1 + alt keycode 105 = Ascii_1 + altgr keycode 105 = Hex_1 +keycode 114 = KP_2 + alt keycode 114 = Ascii_2 + altgr keycode 114 = Hex_2 +keycode 122 = KP_3 + alt keycode 122 = Ascii_3 + altgr keycode 122 = Hex_3 +keycode 112 = KP_0 + alt keycode 112 = Ascii_0 + altgr keycode 112 = Hex_0 +keycode 113 = KP_Period +# altgr control keycode 113 = Boot + control alt keycode 113 = Boot +keycode 87 = Last_Console +keycode 96 = +keycode 86 = less greater bar + alt keycode 86 = Meta_less +keycode 120 = F11 F11 Console_23 + control keycode 120 = F11 + alt keycode 120 = Console_11 + control alt keycode 120 = Console_11 +keycode 7 = F12 F12 Console_24 + control keycode 7 = F12 + alt keycode 7 = Console_12 + control alt keycode 7 = Console_12 +keycode 83 = +keycode 97 = +keycode 98 = +keycode 92 = +keycode 71 = +keycode 94 = +keycode 95 = +keycode 90 = KP_Enter +keycode 20 = Control +keycode 74 = KP_Divide +keycode 99 = Control_backslash + control keycode 99 = Control_backslash + alt keycode 99 = Control_backslash +keycode 100 = AltGr +keycode 101 = Break +keycode 72 = Find +keycode 103 = Up +keycode 104 = Prior + shift keycode 104 = Scroll_Backward +keycode 128 = Left + alt keycode 128 = Decr_Console +keycode 106 = Right + alt keycode 106 = Incr_Console +keycode 129 = Select +keycode 130 = Down +keycode 109 = Next + shift keycode 109 = Scroll_Forward +keycode 110 = Insert +keycode 111 = Remove +# altgr control keycode 111 = Boot + control alt keycode 111 = Boot +keycode 132 = Macro +keycode 133 = F13 +keycode 134 = F14 +keycode 135 = Help +keycode 136 = Do +keycode 137 = F17 +keycode 138 = KP_MinPlus +keycode 139 = Pause +keycode 140 = +keycode 141 = +keycode 142 = +keycode 143 = +keycode 144 = +keycode 145 = +keycode 146 = +keycode 127 = +string F1 = "\033[[A" +string F2 = "\033[[B" +string F3 = "\033[[C" +string F4 = "\033[[D" +string F5 = "\033[[E" +string F6 = "\033[17~" +string F7 = "\033[18~" +string F8 = "\033[19~" +string F9 = "\033[20~" +string F10 = "\033[21~" +string F11 = "\033[23~" +string F12 = "\033[24~" +string F13 = "\033[25~" +string F14 = "\033[26~" +string F15 = "\033[28~" +string F16 = "\033[29~" +string F17 = "\033[31~" +string F18 = "\033[32~" +string F19 = "\033[33~" +string F20 = "\033[34~" +string Find = "\033[1~" +string Insert = "\033[2~" +string Remove = "\033[3~" +string Select = "\033[4~" +string Prior = "\033[5~" +string Next = "\033[6~" +string Macro = "\033[M" +string Pause = "\033[P" +compose '`' 'A' to 'À' +compose '`' 'a' to 'à' +compose '\'' 'A' to 'Á' +compose '\'' 'a' to 'á' +compose '^' 'A' to 'Â' +compose '^' 'a' to 'â' +compose '~' 'A' to 'Ã' +compose '~' 'a' to 'ã' +compose '"' 'A' to 'Ä' +compose '"' 'a' to 'ä' +compose 'O' 'A' to 'Å' +compose 'o' 'a' to 'å' +compose '0' 'A' to 'Å' +compose '0' 'a' to 'å' +compose 'A' 'A' to 'Å' +compose 'a' 'a' to 'å' +compose 'A' 'E' to 'Æ' +compose 'a' 'e' to 'æ' +compose ',' 'C' to 'Ç' +compose ',' 'c' to 'ç' +compose '`' 'E' to 'È' +compose '`' 'e' to 'è' +compose '\'' 'E' to 'É' +compose '\'' 'e' to 'é' +compose '^' 'E' to 'Ê' +compose '^' 'e' to 'ê' +compose '"' 'E' to 'Ë' +compose '"' 'e' to 'ë' +compose '`' 'I' to 'Ì' +compose '`' 'i' to 'ì' +compose '\'' 'I' to 'Í' +compose '\'' 'i' to 'í' +compose '^' 'I' to 'Î' +compose '^' 'i' to 'î' +compose '"' 'I' to 'Ï' +compose '"' 'i' to 'ï' +compose '-' 'D' to 'Ð' +compose '-' 'd' to 'ð' +compose '~' 'N' to 'Ñ' +compose '~' 'n' to 'ñ' +compose '`' 'O' to 'Ò' +compose '`' 'o' to 'ò' +compose '\'' 'O' to 'Ó' +compose '\'' 'o' to 'ó' +compose '^' 'O' to 'Ô' +compose '^' 'o' to 'ô' +compose '~' 'O' to 'Õ' +compose '~' 'o' to 'õ' +compose '"' 'O' to 'Ö' +compose '"' 'o' to 'ö' +compose '/' 'O' to 'Ø' +compose '/' 'o' to 'ø' +compose '`' 'U' to 'Ù' +compose '`' 'u' to 'ù' +compose '\'' 'U' to 'Ú' +compose '\'' 'u' to 'ú' +compose '^' 'U' to 'Û' +compose '^' 'u' to 'û' +compose '"' 'U' to 'Ü' +compose '"' 'u' to 'ü' +compose '\'' 'Y' to 'Ý' +compose '\'' 'y' to 'ý' +compose 'T' 'H' to 'Þ' +compose 't' 'h' to 'þ' +compose 's' 's' to 'ß' +compose '"' 'y' to 'ÿ' +compose 's' 'z' to 'ß' +compose 'i' 'j' to 'ÿ' diff --git a/uClinux-2.4.20-uc1/include/asm-armnommu/arch-W90N745/keyboard.h b/uClinux-2.4.20-uc1/include/asm-armnommu/arch-W90N745/keyboard.h new file mode 100644 index 0000000..c566b60 --- /dev/null +++ b/uClinux-2.4.20-uc1/include/asm-armnommu/arch-W90N745/keyboard.h @@ -0,0 +1,73 @@ +/* + * linux/include/asm-armnommu/arch-W90N745/keyboard.h + * + * Created 3 Nov 1996 by Geert Uytterhoeven + */ + +/* + * This file contains the W90N745 architecture specific keyboard definitions + */ + +#ifndef _ASM_ARM_ARCH_W90N745_KEYBOARD_H +#define _ASM_ARM_ARCH_W90N745_KEYBOARD_H + +#ifdef __KERNEL__ + +#include +#include +#include +#include +#include + +#define KEYBOARD_IRQ 4 +#define DISABLE_KBD_DURING_INTERRUPTS 0 + +extern int pckbd_setkeycode(unsigned int scancode, unsigned int keycode); +extern int pckbd_getkeycode(unsigned int scancode); +extern int pckbd_translate(unsigned char scancode, unsigned char *keycode, + char raw_mode); +extern char pckbd_unexpected_up(unsigned char keycode); +extern void pckbd_leds(unsigned char leds); +extern void pckbd_init_hw(void); +extern int pckbd_pm_resume(struct pm_dev *, pm_request_t, void *); +extern pm_callback pm_kbd_request_override; +extern unsigned char pckbd_sysrq_xlate[128]; + +#define kbd_setkeycode pckbd_setkeycode +#define kbd_getkeycode pckbd_getkeycode +#define kbd_translate pckbd_translate +#define kbd_unexpected_up pckbd_unexpected_up +#define kbd_leds pckbd_leds +#define kbd_init_hw pckbd_init_hw +#define kbd_sysrq_xlate pckbd_sysrq_xlate + +#define SYSRQ_KEY 0x54 + +/* resource allocation */ +#define kbd_request_region() +#define kbd_request_irq(handler) request_irq(KEYBOARD_IRQ, handler, SA_SHIRQ, \ + "keyboard", 1) + +/* How to access the keyboard macros on this platform. */ +#define kbd_read_input() DWORD_READ(KBD_DATA_REG) +#define kbd_read_ascii() DWORD_READ(PS2_ASCII_REG) +#define kbd_read_status() DWORD_READ(KBD_STATUS_REG) +#define kbd_write_output(val) DWORD_WRITE(KBD_DATA_REG,val) +#define kbd_write_command(val) DWORD_WRITE(KBD_CNTL_REG,val) + +/* Some stoneage hardware needs delays after some operations. */ +#define kbd_pause() do { } while(0) + +/* + * Machine specific bits for the PS/2 driver + */ + +#define AUX_IRQ 12 + +#define aux_request_irq(hand, dev_id) \ + request_irq(AUX_IRQ, hand, SA_SHIRQ, "PS/2 Mouse", dev_id) + +#define aux_free_irq(dev_id) free_irq(AUX_IRQ, dev_id) + +#endif /* __KERNEL__ */ +#endif /* _ASM_ARM_ARCH_W90N745_KEYBOARD_H */ -- cgit v0.12