simserial.c revision d50f5c5ca0c3426669fbe11ad4d5708d333eb9fb
11da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
21da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Simulated Serial Driver (fake serial)
31da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
41da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This driver is mostly used for bringup purposes and will go away.
51da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * It has a strong dependency on the system console. All outputs
61da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * are rerouted to the same facility as the one used by printk which, in our
71da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * case means sys_sim.c console (goes via the simulator). The code hereafter
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * is completely leveraged from the serial.c driver.
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Stephane Eranian <eranian@hpl.hp.com>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	David Mosberger-Tang <davidm@hpl.hp.com>
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 02/04/00 D. Mosberger	Merged in serial.c bug fixes in rs_close().
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 02/25/00 D. Mosberger	Synced up with 2.3.99pre-5 version of serial.c.
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * 07/30/02 D. Mosberger	Replace sti()/cli() with explicit spinlocks & local irq masking
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/config.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/major.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fcntl.h>
271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
29a9415644583ef344e02f84faf5fe24bfadb2af8eRandy Dunlap#include <linux/capability.h>
301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/console.h>
311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial.h>
331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serialP.h>
34819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang#include <linux/sysrq.h>
351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/irq.h>
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/hw_irq.h>
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <asm/uaccess.h>
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef CONFIG_KDB
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds# include <linux/kdb.h>
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef SIMSERIAL_DEBUG	/* define this to get some debug information */
451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define KEYBOARD_INTR	3	/* must match with simulator! */
471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NR_PORTS	1	/* only one port for now */
491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SERIAL_INLINE	1
501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SERIAL_INLINE
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define _INLINE_ inline
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define IRQ_T(info) ((info->flags & ASYNC_SHARE_IRQ) ? SA_SHIRQ : SA_INTERRUPT)
561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define SSC_GETCHAR	21
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern long ia64_ssc (long, long, long, long, int);
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern void ia64_ssc_connect_irq (long intr, long irq);
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char *serial_name = "SimSerial driver";
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic char *serial_version = "0.6";
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This has been extracted from asm/serial.h. We need one eventually but
671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * I don't know exactly what we're going to put in it so just fake one
681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * for now.
691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define BASE_BAUD ( 1843200 / 16 )
711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Most of the values here are meaningless to this particular driver.
761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * However some values must be preserved for the code (leveraged from serial.c
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * to work correctly).
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * port must not be 0
791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * type must not be UNKNOWN
801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * So I picked arbitrary (guess from where?) values instead
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serial_state rs_table[NR_PORTS]={
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  /* UART CLK   PORT IRQ     FLAGS        */
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds  { 0, BASE_BAUD, 0x3F8, 0, STD_COM_FLAGS,0,PORT_16550 }  /* ttyS0 */
851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Just for the fun of it !
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct serial_uart_config uart_config[] = {
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "unknown", 1, 0 },
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "8250", 1, 0 },
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "16450", 1, 0 },
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "16550", 1, 0 },
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "16550A", 16, UART_CLEAR_FIFO | UART_USE_FIFO },
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "cirrus", 1, 0 },
971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "ST16650", 1, UART_CLEAR_FIFO | UART_STARTECH },
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "ST16650V2", 32, UART_CLEAR_FIFO | UART_USE_FIFO |
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		  UART_STARTECH },
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ "TI16750", 64, UART_CLEAR_FIFO | UART_USE_FIFO},
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{ 0, 0}
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct tty_driver *hp_simserial_driver;
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct async_struct *IRQ_ports[NR_IRQS];
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct console *console;
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic unsigned char *tmp_buf;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DECLARE_MUTEX(tmp_buf_sem);
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsextern struct console *console_drivers; /* from kernel/printk.c */
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_stop() and rs_start()
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routines are called before setting or resetting tty->stopped.
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * They enable or disable transmitter interrupts, as necessary.
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_stop(struct tty_struct *tty)
1241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_stop: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty->stopped, tty->hw_stopped, tty->flow_stopped);
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_start(struct tty_struct *tty)
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
134e72225d160a2529d6ce6d5898a267f7dae02aa6eAl Viro#ifdef SIMSERIAL_DEBUG
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_start: tty->stopped=%d tty->hw_stopped=%d tty->flow_stopped=%d\n",
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty->stopped, tty->hw_stopped, tty->flow_stopped);
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic  void receive_chars(struct tty_struct *tty, struct pt_regs *regs)
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char ch;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned char seen_esc = 0;
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ( ch == 27 && seen_esc == 0 ) {
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			seen_esc = 1;
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else {
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if ( seen_esc==1 && ch == 'O' ) {
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				seen_esc = 2;
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else if ( seen_esc == 2 ) {
154819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang				if ( ch == 'P' ) /* F1 */
155819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang					show_state();
156819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang#ifdef CONFIG_MAGIC_SYSRQ
157819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang				if ( ch == 'S' ) { /* F4 */
158819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang					do
159819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang						ch = ia64_ssc(0, 0, 0, 0,
160819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang							      SSC_GETCHAR);
161819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang					while (!ch);
162819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang					handle_sysrq(ch, regs, NULL);
163819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang				}
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				seen_esc = 0;
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				continue;
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		seen_esc = 0;
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
171d50f5c5ca0c3426669fbe11ad4d5708d333eb9fbAndreas Schwab		if (tty_insert_flip_char(tty, ch, TTY_NORMAL) == 0)
172d50f5c5ca0c3426669fbe11ad4d5708d333eb9fbAndreas Schwab			break;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_flip_buffer_push(tty);
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the serial driver's interrupt routine for a single port
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic irqreturn_t rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct * info;
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * I don't know exactly why they don't use the dev_id opaque data
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * pointer instead of this extra lookup table
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info = IRQ_ports[irq];
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!info || !info->tty) {
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "simrs_interrupt_single: info|tty=0 info=%p problem\n", info);
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IRQ_NONE;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * pretty simple in our case, because we only get interrupts
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * on inbound traffic
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	receive_chars(info->tty, regs);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Here ends the serial interrupt routines.
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * not really used in our situation so keep them commented out for now
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic DECLARE_TASK_QUEUE(tq_serial); /* used to be at the top of the file */
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_serial_bh(void)
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	run_task_queue(&tq_serial);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_ERR "do_serial_bh: called\n");
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void do_softint(void *private_)
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_ERR "simserial: do_softint called\n");
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_put_char(struct tty_struct *tty, unsigned char ch)
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!tty || !info->xmit.buf) return;
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.buf[info->xmit.head] = ch;
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int count;
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->x_char) {
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		char c = info->x_char;
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, &c, 1);
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->state->icount.tx++;
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->x_char = 0;
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->xmit.head == info->xmit.tail || info->tty->stopped || info->tty->hw_stopped) {
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       info->xmit.head, info->xmit.tail, info->tty->stopped);
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We removed the loop and try to do it in to chunks. We need
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 2 operations maximum because it's a ring buffer.
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * First from current to tail if possible.
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Then from the beginning of the buffer until necessary
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    SERIAL_XMIT_SIZE - info->xmit.tail);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console->write(console, info->xmit.buf+info->xmit.tail, count);
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We have more at the beginning of the buffer
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count) {
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, info->xmit.buf, count);
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.tail += count;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_chars(struct tty_struct *tty)
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->xmit.head == info->xmit.tail || tty->stopped || tty->hw_stopped ||
2981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !info->xmit.buf)
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	transmit_chars(info, NULL);
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write(struct tty_struct * tty,
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    const unsigned char *buf, int count)
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	c, ret = 0;
3091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!tty || !info->xmit.buf || !tmp_buf) return 0;
3131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (1) {
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (count < c)
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			c = count;
3191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c <= 0) {
3201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
3211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
3221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(info->xmit.buf + info->xmit.head, buf, c);
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.head = ((info->xmit.head + c) &
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   (SERIAL_XMIT_SIZE-1));
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf += c;
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= c;
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret += c;
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Hey, we transmit directly from here in our case
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
3331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE)
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    && !tty->stopped && !tty->hw_stopped) {
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		transmit_chars(info, NULL);
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
3381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write_room(struct tty_struct *tty)
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_chars_in_buffer(struct tty_struct *tty)
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
3501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
3521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_buffer(struct tty_struct *tty)
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
3571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = info->xmit.tail = 0;
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&tty->write_wait);
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    tty->ldisc.write_wakeup)
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		(tty->ldisc.write_wakeup)(tty);
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function is used to send a high-priority XON/XOFF character to
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the device
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_send_xchar(struct tty_struct *tty, char ch)
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->x_char = ch;
3791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ch) {
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * I guess we could call console->write() directly but
3821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * let's do that for now.
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
3841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		transmit_chars(info, NULL);
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_throttle()
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called by the upper-layer tty layer to signal that
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * incoming characters should be throttled.
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
3951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_throttle(struct tty_struct * tty)
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (I_IXOFF(tty)) rs_send_xchar(tty, STOP_CHAR(tty));
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_throttle called\n");
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_unthrottle(struct tty_struct * tty)
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info = (struct async_struct *)tty->driver_data;
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (I_IXOFF(tty)) {
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->x_char)
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->x_char = 0;
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rs_send_xchar(tty, START_CHAR(tty));
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_unthrottle called\n");
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_break() --- routine which turns the break handling on or off
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_break(struct tty_struct *tty, int break_state)
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_ioctl(struct tty_struct *tty, struct file * file,
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    unsigned int cmd, unsigned long arg)
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tty->flags & (1 << TTY_IO_ERROR))
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    return -EIO;
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCMGET:
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCMGET called\n");
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCMBIS:
4381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCMBIC:
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCMSET:
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCMBIS/BIC/SET called\n");
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCGSERIAL:
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "simrs_ioctl TIOCGSERIAL called\n");
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSSERIAL:
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "simrs_ioctl TIOCSSERIAL called\n");
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSERCONFIG:
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCSERCONFIG called\n");
4501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -EINVAL;
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSERGETLSR: /* Get line status register */
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCSERGETLSR called\n");
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return  -EINVAL;
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSERGSTRUCT:
4571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCSERGSTRUCT called\n");
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
4591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (copy_to_user((struct async_struct *) arg,
4601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					 info, sizeof(struct async_struct)))
4611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				return -EFAULT;
4621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * - mask passed in arg for lines of interest
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *   (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Caller should use TIOCGICOUNT to see which one it was
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCMIWAIT:
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCMIWAIT: called\n");
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
4761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Return: write counters to the user passed counter struct
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * NB: both 1->0 and 0->1 transitions are counted except for
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 *     RI where only 0->1 is counted.
4791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
4801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCGICOUNT:
4811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk(KERN_INFO "rs_ioctl: TIOCGICOUNT called\n");
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSERGWILD:
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		case TIOCSERSWILD:
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			/* "setserial -W" is called in Debian boot */
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n");
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return 0;
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		default:
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOIOCTLCMD;
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned int cflag = tty->termios->c_cflag;
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (   (cflag == old_termios->c_cflag)
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    && (   RELEVANT_IFLAG(tty->termios->c_iflag)
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		== RELEVANT_IFLAG(old_termios->c_iflag)))
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	  return;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Handle turning off CRTSCTS */
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((old_termios->c_cflag & CRTSCTS) &&
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    !(tty->termios->c_cflag & CRTSCTS)) {
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty->hw_stopped = 0;
5121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		rs_start(tty);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine will shutdown a serial port; interrupts are disabled, and
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * DTR is dropped if the hangup on close termio flag is on.
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void shutdown(struct async_struct * info)
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long	flags;
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state *state;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int		retval;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!(info->flags & ASYNC_INITIALIZED)) return;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state = info->state;
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("Shutting down serial port %d (irq %d)....", info->line,
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	       state->irq);
5321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
5331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	{
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
5371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * First unlink the serial port from the IRQ chain...
5381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
5391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->next_port)
5401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->next_port->prev_port = info->prev_port;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->prev_port)
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->prev_port->next_port = info->next_port;
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			IRQ_ports[state->irq] = info->next_port;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
5471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Free the IRQ, if necessary
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
5491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (state->irq && (!IRQ_ports[state->irq] ||
5501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   !IRQ_ports[state->irq]->next_port)) {
5511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (IRQ_ports[state->irq]) {
5521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				free_irq(state->irq, NULL);
5531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				retval = request_irq(state->irq, rs_interrupt_single,
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						     IRQ_T(info), "serial", NULL);
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (retval)
5571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					printk(KERN_ERR "serial shutdown: request_irq: error %d"
5581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					       "  Couldn't reacquire IRQ.\n", retval);
5591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			} else
5601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				free_irq(state->irq, NULL);
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->xmit.buf) {
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_page((unsigned long) info->xmit.buf);
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->xmit.buf = 0;
5661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
5671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
5691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->flags &= ~ASYNC_INITIALIZED;
5711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
5731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
5761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
5771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_close()
5781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
5791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called when the serial port gets closed.  First, we
5801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * wait for the last remaining data to be sent.  Then, we unlink its
5811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * async structure from the interrupt chain if necessary, and we free
5821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * that IRQ if nothing is left in the chain.
5831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
5841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
5851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_close(struct tty_struct *tty, struct file * filp)
5861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct * info = (struct async_struct *)tty->driver_data;
5881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state *state;
5891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
5901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!info ) return;
5921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state = info->state;
5941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
5961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty_hung_up_p(filp)) {
5971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
5981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("rs_close: hung_up\n");
5991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
6011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
6041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_close ttys%d, count = %d\n", info->line, state->count);
6051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((tty->count == 1) && (state->count != 1)) {
6071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
6081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * Uh, oh.  tty->count is 1, which means that the tty
6091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * structure will be freed.  state->count should always
6101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * be one in these conditions.  If it's greater than
6111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * one, we've got real problems, since it means the
6121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * serial port won't be shutdown.
6131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
6141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "rs_close: bad serial port count; tty->count is 1, "
6151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       "state->count is %d\n", state->count);
6161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		state->count = 1;
6171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (--state->count < 0) {
6191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n",
6201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       info->line, state->count);
6211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		state->count = 0;
6221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (state->count) {
6241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
6251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->flags |= ASYNC_CLOSING;
6281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
6291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
6311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Now we wait for the transmit buffer to clear; and we notify
6321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * the line discipline to only process XON/XOFF characters.
6331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
6341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	shutdown(info);
6351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty);
6361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty);
6371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->event = 0;
6381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->tty = 0;
6391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->blocked_open) {
6409e173c031a7542b1f66b6da853772e5de1804399Nishanth Aravamudan		if (info->close_delay)
6419e173c031a7542b1f66b6da853772e5de1804399Nishanth Aravamudan			schedule_timeout_interruptible(info->close_delay);
6421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		wake_up_interruptible(&info->open_wait);
6431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
6451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&info->close_wait);
6461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_wait_until_sent() --- wait until the transmitter is empty
6501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_wait_until_sent(struct tty_struct *tty, int timeout)
6521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
6571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_hangup() --- called by tty_hangup() when a hangup is signaled.
6581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
6591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_hangup(struct tty_struct *tty)
6601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct * info = (struct async_struct *)tty->driver_data;
6621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state *state = info->state;
6631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
6651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_hangup: called\n");
6661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
6671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state = info->state;
6691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rs_flush_buffer(tty);
6711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->flags & ASYNC_CLOSING)
6721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
6731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	shutdown(info);
6741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->event = 0;
6761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	state->count = 0;
6771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->flags &= ~ASYNC_NORMAL_ACTIVE;
6781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->tty = 0;
6791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	wake_up_interruptible(&info->open_wait);
6801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
6811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int get_async_struct(int line, struct async_struct **ret_info)
6841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
6851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct *info;
6861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state *sstate;
6871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
6881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sstate = rs_table + line;
6891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	sstate->count++;
6901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sstate->info) {
6911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*ret_info = sstate->info;
6921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
6931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info = kmalloc(sizeof(struct async_struct), GFP_KERNEL);
6951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!info) {
6961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		sstate->count--;
6971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
6981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
6991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	memset(info, 0, sizeof(struct async_struct));
7001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&info->open_wait);
7011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&info->close_wait);
7021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	init_waitqueue_head(&info->delta_msr_wait);
7031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->magic = SERIAL_MAGIC;
7041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->port = sstate->port;
7051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->flags = sstate->flags;
7061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit_fifo_size = sstate->xmit_fifo_size;
7071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->line = line;
7081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	INIT_WORK(&info->work, do_softint, info);
7091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->state = sstate;
7101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (sstate->info) {
7111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		kfree(info);
7121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		*ret_info = sstate->info;
7131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
7141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*ret_info = sstate->info = info;
7161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
7171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
7181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int
7201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstartup(struct async_struct *info)
7211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
7221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
7231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	retval=0;
7241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	irqreturn_t (*handler)(int, void *, struct pt_regs *);
7251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state *state= info->state;
7261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long page;
7271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	page = get_zeroed_page(GFP_KERNEL);
7291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!page)
7301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
7311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
7331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->flags & ASYNC_INITIALIZED) {
7351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page(page);
7361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto errout;
7371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!state->port || !state->type) {
7401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags);
7411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page(page);
7421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto errout;
7431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->xmit.buf)
7451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page(page);
7461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
7471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.buf = (unsigned char *) page;
7481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
7501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("startup: ttys%d (irq %d)...", info->line, state->irq);
7511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
7521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Allocate the IRQ if necessary
7551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (state->irq && (!IRQ_ports[state->irq] ||
7571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			  !IRQ_ports[state->irq]->next_port)) {
7581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (IRQ_ports[state->irq]) {
7591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			retval = -EBUSY;
7601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto errout;
7611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		} else
7621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			handler = rs_interrupt_single;
7631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		retval = request_irq(state->irq, handler, IRQ_T(info), "simserial", NULL);
7651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (retval) {
7661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			if (capable(CAP_SYS_ADMIN)) {
7671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				if (info->tty)
7681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds					set_bit(TTY_IO_ERROR,
7691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds						&info->tty->flags);
7701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				retval = 0;
7711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
7721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto errout;
7731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
7741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
7751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Insert serial port into IRQ chain.
7781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->prev_port = 0;
7801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->next_port = IRQ_ports[state->irq];
7811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->next_port)
7821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->next_port->prev_port = info;
7831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	IRQ_ports[state->irq] = info;
7841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags);
7861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = info->xmit.tail = 0;
7881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#if 0
7901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Set up serial timers...
7921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
7931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timer_table[RS_TIMER].expires = jiffies + 2*HZ/100;
7941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	timer_active |= 1 << RS_TIMER;
7951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
7961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
7971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
7981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Set up the tty->alt_speed kludge
7991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->tty) {
8011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
8021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->tty->alt_speed = 57600;
8031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
8041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->tty->alt_speed = 115200;
8051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
8061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->tty->alt_speed = 230400;
8071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
8081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->tty->alt_speed = 460800;
8091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->flags |= ASYNC_INITIALIZED;
8121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
8131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserrout:
8161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
8171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
8181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called whenever a serial port is opened.  It
8231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * enables interrupts for a serial port, linking in its async structure into
8241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the IRQ chain.   It also performs the serial-specific
8251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * initialization for the tty structure.
8261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_open(struct tty_struct *tty, struct file * filp)
8281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
8291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct async_struct	*info;
8301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int			retval, line;
8311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long		page;
8321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	line = tty->index;
8341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((line < 0) || (line >= NR_PORTS))
8351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
8361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = get_async_struct(line, &info);
8371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval)
8381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
8391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty->driver_data = info;
8401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->tty = tty;
8411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
8431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_open %s, count = %d\n", tty->name, info->state->count);
8441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
8461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!tmp_buf) {
8481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		page = get_zeroed_page(GFP_KERNEL);
8491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!page)
8501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			return -ENOMEM;
8511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tmp_buf)
8521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			free_page(page);
8531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
8541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			tmp_buf = (unsigned char *) page;
8551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * If the port is the middle of closing, bail out now
8591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty_hung_up_p(filp) ||
8611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (info->flags & ASYNC_CLOSING)) {
8621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->flags & ASYNC_CLOSING)
8631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			interruptible_sleep_on(&info->close_wait);
8641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SERIAL_DO_RESTART
8651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return ((info->flags & ASYNC_HUP_NOTIFY) ?
8661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			-EAGAIN : -ERESTARTSYS);
8671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#else
8681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -EAGAIN;
8691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Start up serial port
8741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	retval = startup(info);
8761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (retval) {
8771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return retval;
8781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
8811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * figure out which console to use (should be one already)
8821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
8831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console = console_drivers;
8841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (console) {
8851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((console->flags & CON_ENABLED) && console->write) break;
8861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console = console->next;
8871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
8881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
8901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk("rs_open ttys%d successful\n", info->line);
8911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
8921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
8931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
8941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
8961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * /proc fs routines....
8971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
8981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline int line_info(char *buf, struct serial_state *state)
9001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return sprintf(buf, "%d: uart:%s port:%lX irq:%d\n",
9021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       state->line, uart_config[state->type].name,
9031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       state->port, state->irq);
9041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_read_proc(char *page, char **start, off_t off, int count,
9071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 int *eof, void *data)
9081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int i, len = 0, l;
9101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	off_t	begin = 0;
9111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	len += sprintf(page, "simserinfo:1.0 driver:%s\n", serial_version);
9131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0; i < NR_PORTS && len < 4000; i++) {
9141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		l = line_info(page + len, &rs_table[i]);
9151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		len += l;
9161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len+begin > off+count)
9171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto done;
9181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (len+begin < off) {
9191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			begin += len;
9201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			len = 0;
9211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
9221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
9231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*eof = 1;
9241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsdone:
9251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (off >= len+begin)
9261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return 0;
9271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	*start = page + (begin-off);
9281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ((count < begin+len-off) ? count : begin+len-off);
9291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
9321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ---------------------------------------------------------------------
9331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_init() and friends
9341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
9351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_init() is called at boot-time to initialize the serial driver.
9361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ---------------------------------------------------------------------
9371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
9401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine prints out the appropriate serial driver version
9411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * number, and identifies which options were configured into this
9421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * driver.
9431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic inline void show_serial_version(void)
9451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "%s version %s with", serial_name, serial_version);
9471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO " no serial options enabled\n");
9481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
9491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct tty_operations hp_ops = {
9511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open = rs_open,
9521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close = rs_close,
9531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = rs_write,
9541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.put_char = rs_put_char,
9551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_chars = rs_flush_chars,
9561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room = rs_write_room,
9571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer = rs_chars_in_buffer,
9581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_buffer = rs_flush_buffer,
9591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl = rs_ioctl,
9601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle = rs_throttle,
9611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle = rs_unthrottle,
9621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.send_xchar = rs_send_xchar,
9631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios = rs_set_termios,
9641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.stop = rs_stop,
9651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.start = rs_start,
9661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hangup = rs_hangup,
9671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.break_ctl = rs_break,
9681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.wait_until_sent = rs_wait_until_sent,
9691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.read_proc = rs_read_proc,
9701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
9711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
9731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * The serial driver boot-time initialization code!
9741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
9751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int __init
9761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldssimrs_init (void)
9771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
9783b5cc09033f49d004006acf44e5b05036bd46a85Kenji Kaneshige	int			i, rc;
9791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	struct serial_state	*state;
9801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ia64_platform_is("hpsim"))
9821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
9831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver = alloc_tty_driver(1);
9851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!hp_simserial_driver)
9861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
9871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	show_serial_version();
9891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize the tty_driver structure */
9911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
9921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->owner = THIS_MODULE;
9931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->driver_name = "simserial";
9941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->name = "ttyS";
9951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->major = TTY_MAJOR;
9961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->minor_start = 64;
9971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL;
9981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL;
9991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios = tty_std_termios;
10001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios.c_cflag =
10011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
10021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW;
10031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(hp_simserial_driver, &hp_ops);
10041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
10061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Let's have a little bit of fun !
10071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
10081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	for (i = 0, state = rs_table; i < NR_PORTS; i++,state++) {
10091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (state->type == PORT_UNKNOWN) continue;
10111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (!state->irq) {
10133b5cc09033f49d004006acf44e5b05036bd46a85Kenji Kaneshige			if ((rc = assign_irq_vector(AUTO_ASSIGN)) < 0)
10143b5cc09033f49d004006acf44e5b05036bd46a85Kenji Kaneshige				panic("%s: out of interrupt vectors!\n",
10153b5cc09033f49d004006acf44e5b05036bd46a85Kenji Kaneshige				      __FUNCTION__);
10163b5cc09033f49d004006acf44e5b05036bd46a85Kenji Kaneshige			state->irq = rc;
10171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			ia64_ssc_connect_irq(KEYBOARD_INTR, state->irq);
10181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
10191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk(KERN_INFO "ttyS%d at 0x%04lx (irq = %d) is a %s\n",
10211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       state->line,
10221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       state->port, state->irq,
10231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		       uart_config[state->type].name);
10241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
10251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (tty_register_driver(hp_simserial_driver))
10271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		panic("Couldn't register simserial driver\n");
10281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
10301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
10311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
10321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
10331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__initcall(simrs_init);
10341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1035