summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOliver Schinagl <oliver@schinagl.nl>2011-03-16 14:07:34 (GMT)
committerOliver Schinagl <oliver@schinagl.nl>2011-03-16 14:07:34 (GMT)
commitb9a923b66abdc8f16839bc324d4927aa4d5ea1f6 (patch)
treee51150dab00fd24185c0e346a90147da6027b476
parent4bdc429b2893234d5b11514cb3a25de3757b9e98 (diff)
downloadopenipcam-b9a923b66abdc8f16839bc324d4927aa4d5ea1f6.zip
openipcam-b9a923b66abdc8f16839bc324d4927aa4d5ea1f6.tar.gz
openipcam-b9a923b66abdc8f16839bc324d4927aa4d5ea1f6.tar.bz2
Keyoard, serial, ps2 and i2c drivers for the W90N745
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/Config.in35
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/Makefile14
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/keyboard.c33
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/tty_io.c5
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/vdd.c472
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_i2c.c600
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_i2c.h123
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_keypad.c253
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_keypad.h57
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.c809
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_ps2.h115
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_uart.c4650
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_uart_1.c4719
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_uart_2.c4668
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/w90n745_uart_3.c4669
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_usi.c331
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/w90n745_usi.h73
-rw-r--r--uClinux-2.4.20-uc1/drivers/char/winbond_keymap.c374
-rwxr-xr-xuClinux-2.4.20-uc1/drivers/char/winbond_keymap.map357
-rw-r--r--uClinux-2.4.20-uc1/include/asm-armnommu/arch-W90N745/keyboard.h73
20 files changed, 22428 insertions, 2 deletions
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 <linux/config.h>
@@ -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 <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/page.h>
+#include <linux/errno.h>
+
+#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 <linux/devfs_fs_kernel.h>
+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 <linux/module.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#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;i<count;i++)\
+ PDEBUG("%02x ", buf[i]);PDEBUG("\n");}
+#else
+#define MSG2(fmt, arg...)
+#define PRNBUF(buf, count)
+#endif
+
+
+
+#define i2c_outl(dev, dword, addr) outl(dword, base[dev->no] + 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 <linux/types.h>
+#include <linux/ioctl.h>
+
+#include <asm/arch/irqs.h>
+
+#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 <linux/init.h>
+#include <linux/slab.h>
+#include <asm/errno.h>
+#include <asm/delay.h>
+//#include <asm/fcntl.h>
+#include <asm/arch/irqs.h>
+#include <linux/mm.h>
+#include <linux/poll.h>
+#include <linux/module.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+#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 <linux/config.h>
+
+
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/mm.h>
+#include <linux/signal.h>
+#include <linux/init.h>
+#include <linux/kbd_ll.h>
+#include <linux/delay.h>
+#include <linux/random.h>
+#include <linux/poll.h>
+#include <linux/miscdevice.h>
+#include <linux/slab.h>
+#include <linux/kbd_kern.h>
+#include <linux/vt_kern.h>
+#include <linux/smp_lock.h>
+#include <linux/kd.h>
+#include <linux/pm.h>
+
+#undef KBD_DATA_REG
+#undef KBD_STATUS_REG
+#undef KBD_CNTL_REG
+
+#include "keyboard.h"
+#include "w90n745_ps2.h"
+
+#include <asm/bitops.h>
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+
+#include <asm/io.h>
+
+/* 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 <mcli2@winbond.com.tw>
+ *
+ * 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 <ah@doc.ic.ac.uk>
+ *
+ * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ * rs_set_termios fixed to look also for changes of the input
+ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
+ * Bernd Anhäupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
+ *
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
+ * 3/98: Change the IRQ detection, use of probe_irq_o*(),
+ * suppress TIOCSERGWILD and TIOCSERSWILD
+ * Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ * 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 <stuartm@connecttech.com>
+ *
+ * 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 <alan@linuxcare.com>
+ *
+ * 5/00: Support for the RSA-DV II/S card added.
+ * Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+ *
+ * 6/00: Remove old-style timer, use timer_list
+ * Andrew Morton <andrewm@uow.edu.au>
+ *
+ * 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 <acme@conectiva.com.br>
+ *
+ * 10/00: add in optional software flow control for serial console.
+ * Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o)
+ *
+ * 08/30/2005:Porting to Winbond uart <mcli2@winbond.com.tw>
+ *
+ */
+
+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 <linux/config.h>
+#include <linux/version.h>
+
+#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 <linux/module.h>
+
+#include <linux/types.h>
+#ifdef LOCAL_HEADERS
+#include "serial_local.h"
+#else
+#include <linux/serial.h>
+#include <linux/serialP.h>
+//#include <linux/serial_reg.h>
+#include <asm/serial.h>
+#define LOCAL_VERSTRING ""
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#if (LINUX_VERSION_CODE >= 131343)
+#include <linux/init.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
+#endif
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_WINBOND_CONSOLE
+#include <linux/console.h>
+#endif
+#ifdef ENABLE_SERIAL_PCI
+#include <linux/pci.h>
+#endif
+#ifdef ENABLE_SERIAL_PNP
+#include <linux/isapnp.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
+#ifdef CONFIG_LEDMAN
+#include <linux/ledman.h>
+#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 <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#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 <linux/symtab_begin.h>
+ X(register_serial),
+ X(unregister_serial),
+#include <linux/symtab_end.h>
+};
+#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 <tytso@mit.edu>");
+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 <ah@doc.ic.ac.uk>
+ *
+ * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ * rs_set_termios fixed to look also for changes of the input
+ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
+ * Bernd Anhäupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
+ *
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
+ * 3/98: Change the IRQ detection, use of probe_irq_o*(),
+ * suppress TIOCSERGWILD and TIOCSERSWILD
+ * Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ * 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 <stuartm@connecttech.com>
+ *
+ * 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 <alan@linuxcare.com>
+ *
+ * 5/00: Support for the RSA-DV II/S card added.
+ * Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+ *
+ * 6/00: Remove old-style timer, use timer_list
+ * Andrew Morton <andrewm@uow.edu.au>
+ *
+ * 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 <acme@conectiva.com.br>
+ *
+ * 10/00: add in optional software flow control for serial console.
+ * Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o)
+ *
+ * 08/30/2005:Porting to Winbond uart <mcli2@winbond.com.tw>
+ *
+ */
+
+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 <linux/config.h>
+#include <linux/version.h>
+
+#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 <linux/module.h>
+
+#include <linux/types.h>
+#ifdef LOCAL_HEADERS
+#include "serial_local.h"
+#else
+#include <linux/serial.h>
+#include <linux/serialP.h>
+//#include <linux/serial_reg.h>
+#include <asm/serial.h>
+#define LOCAL_VERSTRING ""
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#if (LINUX_VERSION_CODE >= 131343)
+#include <linux/init.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
+#endif
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_W90N7451_CONSOLE
+#include <linux/console.h>
+#endif
+#ifdef ENABLE_SERIAL_PCI
+#include <linux/pci.h>
+#endif
+#ifdef ENABLE_SERIAL_PNP
+#include <linux/isapnp.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
+#ifdef CONFIG_LEDMAN
+#include <linux/ledman.h>
+#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 <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <linux/serial_reg.h>
+
+#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 <linux/symtab_begin.h>
+ X(register_serial),
+ X(unregister_serial),
+#include <linux/symtab_end.h>
+};
+#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 <tytso@mit.edu>");
+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 <ah@doc.ic.ac.uk>
+ *
+ * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ * rs_set_termios fixed to look also for changes of the input
+ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
+ * Bernd Anhäupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
+ *
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
+ * 3/98: Change the IRQ detection, use of probe_irq_o*(),
+ * suppress TIOCSERGWILD and TIOCSERSWILD
+ * Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ * 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 <stuartm@connecttech.com>
+ *
+ * 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 <alan@linuxcare.com>
+ *
+ * 5/00: Support for the RSA-DV II/S card added.
+ * Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+ *
+ * 6/00: Remove old-style timer, use timer_list
+ * Andrew Morton <andrewm@uow.edu.au>
+ *
+ * 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 <acme@conectiva.com.br>
+ *
+ * 10/00: add in optional software flow control for serial console.
+ * Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o)
+ *
+ * 08/30/2005:Porting to Winbond uart <mcli2@winbond.com.tw>
+ *
+ */
+
+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 <linux/config.h>
+#include <linux/version.h>
+
+#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 <linux/module.h>
+
+#include <linux/types.h>
+#ifdef LOCAL_HEADERS
+#include "serial_local.h"
+#else
+#include <linux/serial.h>
+#include <linux/serialP.h>
+//#include <linux/serial_reg.h>
+#include <asm/serial.h>
+#define LOCAL_VERSTRING ""
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#if (LINUX_VERSION_CODE >= 131343)
+#include <linux/init.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
+#endif
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_W90N7452_CONSOLE
+#include <linux/console.h>
+#endif
+#ifdef ENABLE_SERIAL_PCI
+#include <linux/pci.h>
+#endif
+#ifdef ENABLE_SERIAL_PNP
+#include <linux/isapnp.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
+#ifdef CONFIG_LEDMAN
+#include <linux/ledman.h>
+#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 <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <linux/serial_reg.h> //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 <linux/symtab_begin.h>
+ X(register_serial),
+ X(unregister_serial),
+#include <linux/symtab_end.h>
+};
+#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 <tytso@mit.edu>");
+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 <ah@doc.ic.ac.uk>
+ *
+ * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
+ *
+ * rs_set_termios fixed to look also for changes of the input
+ * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
+ * Bernd Anhäupl 05/17/96.
+ *
+ * 1/97: Extended dumb serial ports are a config option now.
+ * Saves 4k. Michael A. Griffith <grif@acm.org>
+ *
+ * 8/97: Fix bug in rs_set_termios with RTS
+ * Stanislav V. Voronyi <stas@uanet.kharkov.ua>
+ *
+ * 3/98: Change the IRQ detection, use of probe_irq_o*(),
+ * suppress TIOCSERGWILD and TIOCSERSWILD
+ * Etienne Lorrain <etienne.lorrain@ibm.net>
+ *
+ * 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 <stuartm@connecttech.com>
+ *
+ * 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 <alan@linuxcare.com>
+ *
+ * 5/00: Support for the RSA-DV II/S card added.
+ * Kiyokazu SUTO <suto@ks-and-ks.ne.jp>
+ *
+ * 6/00: Remove old-style timer, use timer_list
+ * Andrew Morton <andrewm@uow.edu.au>
+ *
+ * 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 <acme@conectiva.com.br>
+ *
+ * 10/00: add in optional software flow control for serial console.
+ * Kanoj Sarcar <kanoj@sgi.com> (Modified by Theodore Ts'o)
+ *
+ * 08/30/2005:Porting to Winbond uart <mcli2@winbond.com.tw>
+ *
+ */
+
+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 <linux/config.h>
+#include <linux/version.h>
+
+#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 <linux/module.h>
+
+#include <linux/types.h>
+#ifdef LOCAL_HEADERS
+#include "serial_local.h"
+#else
+#include <linux/serial.h>
+#include <linux/serialP.h>
+//#include <linux/serial_reg.h>
+#include <asm/serial.h>
+#define LOCAL_VERSTRING ""
+#endif
+
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/slab.h>
+#if (LINUX_VERSION_CODE >= 131343)
+#include <linux/init.h>
+#endif
+#if (LINUX_VERSION_CODE >= 131336)
+#include <asm/uaccess.h>
+#endif
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_W90N7453_CONSOLE
+#include <linux/console.h>
+#endif
+#ifdef ENABLE_SERIAL_PCI
+#include <linux/pci.h>
+#endif
+#ifdef ENABLE_SERIAL_PNP
+#include <linux/isapnp.h>
+#endif
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
+#ifdef CONFIG_LEDMAN
+#include <linux/ledman.h>
+#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 <asm/system.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/bitops.h>
+
+#include <linux/serial_reg.h> //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 <linux/symtab_begin.h>
+ X(register_serial),
+ X(unregister_serial),
+#include <linux/symtab_end.h>
+};
+#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 <tytso@mit.edu>");
+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 <linux/module.h>
+#include <linux/fs.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+
+#include <asm/arch/hardware.h>
+#include <asm/arch/irqs.h>
+
+#include <asm/uaccess.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/string.h>
+#include <asm/atomic.h>
+
+#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;i<count;i++)\
+ PDEBUG("%02x ", buf[i]);PDEBUG("\n");}
+#else
+#define MSG2(fmt, arg...)
+#define PRNBUF(buf, count)
+#endif
+
+#define usi_inl(addr) (*((volatile u32 *)(addr)))
+#define usi_outl(val, addr) (*((volatile u32 *)(addr))=(val))
+
+
+static atomic_t usi_available = ATOMIC_INIT(1);
+static struct usi_parameter global_parameter;
+static wait_queue_head_t wq;
+static volatile int trans_finish, slave_select;
+
+void usi_deselect_slave(void)
+{
+ usi_outl(usi_inl(USI_SSR)&0xc, USI_SSR);
+ slave_select = 0;
+}
+
+void usi_select_slave(int x)
+{
+ usi_outl((global_parameter.active_level << 2)|(1 << x), USI_SSR);
+ slave_select = 1;
+}
+
+static void usi_interrupt(int irq, void * dev_id, struct pt_regs *regs)
+{
+ u32 reg;
+
+ ENTER();
+
+ reg = usi_inl(USI_CNTRL);
+
+ if (!(reg & 0x10000)) /* it not me ? */
+ return;
+
+ reg |= 0x10000;
+ usi_outl(reg, USI_CNTRL); /* clear interrupt flag */
+
+ trans_finish = 1;
+
+ wake_up_interruptible(&wq);
+
+ LEAVE();
+}
+
+static int usi_transit(struct usi_data *data_ptr)
+{
+ u32 reg,mask;
+
+ ENTER();
+
+ if (slave_select == 0)
+ return -ENODEV;
+
+ mask = (1 << data_ptr->bit_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 <linux/types.h>
+#include <linux/ioctl.h>
+
+#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 <linux/types.h>
+#include <linux/keyboard.h>
+#include <linux/kd.h>
+
+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 <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/kd.h>
+#include <linux/pm.h>
+#include <asm/io.h>
+
+#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 */