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