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
7adb636fe0845bf1b4dbcb546a1673f40a95062fdJiri Slaby * case means sys_sim.c console (goes via the simulator).
81da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
91da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Copyright (C) 1999-2000, 2002-2003 Hewlett-Packard Co
101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	Stephane Eranian <eranian@hpl.hp.com>
111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *	David Mosberger-Tang <davidm@hpl.hp.com>
121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/init.h>
151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/errno.h>
161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/sched.h>
171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty.h>
181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/tty_flip.h>
191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/major.h>
201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/fcntl.h>
211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/mm.h>
22bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan#include <linux/seq_file.h>
231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/slab.h>
24a9415644583ef344e02f84faf5fe24bfadb2af8eRandy Dunlap#include <linux/capability.h>
253c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby#include <linux/circ_buf.h>
261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/console.h>
278b916330567b0b788f35ded5d99a7ef7dc30b293Jiri Slaby#include <linux/irq.h>
281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/module.h>
291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#include <linux/serial.h>
30819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang#include <linux/sysrq.h>
318b916330567b0b788f35ded5d99a7ef7dc30b293Jiri Slaby#include <linux/uaccess.h>
321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
33035cfe5ac55d399169b7f61f7a111d3d7075190cJiri Slaby#include <asm/hpsim.h>
341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
35035cfe5ac55d399169b7f61f7a111d3d7075190cJiri Slaby#include "hpsim_ssc.h"
36035cfe5ac55d399169b7f61f7a111d3d7075190cJiri Slaby
371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#undef SIMSERIAL_DEBUG	/* define this to get some debug information */
381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define KEYBOARD_INTR	3	/* must match with simulator! */
401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define NR_PORTS	1	/* only one port for now */
421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
433c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slabystruct serial_state {
447f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	struct tty_port port;
453c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby	struct circ_buf xmit;
463c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby	int irq;
473c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby	int x_char;
483c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby};
493c4782dcd9b8d02e79f0f0bd1fe6e30a79790526Jiri Slaby
50fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slabystatic struct serial_state rs_table[NR_PORTS];
511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstruct tty_driver *hp_simserial_driver;
531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic struct console *console;
551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
562e124b4a390ca85325fae75764bef92f0547fa25Jiri Slabystatic void receive_chars(struct tty_port *port)
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char ch;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned char seen_esc = 0;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
62f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		if (ch == 27 && seen_esc == 0) {
631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			seen_esc = 1;
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
65f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		} else if (seen_esc == 1 && ch == 'O') {
66f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			seen_esc = 2;
67f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			continue;
68f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		} else if (seen_esc == 2) {
69f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			if (ch == 'P') /* F1 */
70f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				show_state();
71819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang#ifdef CONFIG_MAGIC_SYSRQ
72f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			if (ch == 'S') { /* F4 */
73f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				do {
74f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby					ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR);
75f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				} while (!ch);
76f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				handle_sysrq(ch);
771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
78f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby#endif
79f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			seen_esc = 0;
80f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			continue;
811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		seen_esc = 0;
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8492a19f9cec9a80ad93c06e115822deb729e2c6adJiri Slaby		if (tty_insert_flip_char(port, ch, TTY_NORMAL) == 0)
85d50f5c5ca0c3426669fbe11ad4d5708d333eb9fbAndreas Schwab			break;
861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
872e124b4a390ca85325fae75764bef92f0547fa25Jiri Slaby	tty_flip_buffer_push(port);
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the serial driver's interrupt routine for a single port
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
935dcded1b0b4f1537bb6dff453fb805517756c94bAl Virostatic irqreturn_t rs_interrupt_single(int irq, void *dev_id)
941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
95916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = dev_id;
961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
972e124b4a390ca85325fae75764bef92f0547fa25Jiri Slaby	receive_chars(&info->port);
982e124b4a390ca85325fae75764bef92f0547fa25Jiri Slaby
991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
1001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Here ends the serial interrupt routines.
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
108f34d7a5b7010b82fe97da95496b9971435530062Alan Coxstatic int rs_put_char(struct tty_struct *tty, unsigned char ch)
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
110916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1136e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	if (!info->xmit.buf)
114f34d7a5b7010b82fe97da95496b9971435530062Alan Cox		return 0;
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
1181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
119f34d7a5b7010b82fe97da95496b9971435530062Alan Cox		return 0;
1201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.buf[info->xmit.head] = ch;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
1231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
124f34d7a5b7010b82fe97da95496b9971435530062Alan Cox	return 1;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1275e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slabystatic void transmit_chars(struct tty_struct *tty, struct serial_state *info,
1285e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		int *intr_done)
1291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int count;
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
1341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->x_char) {
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		char c = info->x_char;
1371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, &c, 1);
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->x_char = 0;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
145ee7970690568b0c875467f475d9c957ec0d9e099Jiri Slaby	if (info->xmit.head == info->xmit.tail || tty->stopped) {
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
1485e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		       info->xmit.head, info->xmit.tail, tty->stopped);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We removed the loop and try to do it in to chunks. We need
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 2 operations maximum because it's a ring buffer.
1551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
1561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * First from current to tail if possible.
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Then from the beginning of the buffer until necessary
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    SERIAL_XMIT_SIZE - info->xmit.tail);
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console->write(console, info->xmit.buf+info->xmit.tail, count);
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We have more at the beginning of the buffer
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count) {
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, info->xmit.buf, count);
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.tail += count;
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_chars(struct tty_struct *tty)
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
180916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
182f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->xmit.head == info->xmit.tail || tty->stopped ||
183ee7970690568b0c875467f475d9c957ec0d9e099Jiri Slaby			!info->xmit.buf)
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1865e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby	transmit_chars(tty, info, NULL);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write(struct tty_struct * tty,
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    const unsigned char *buf, int count)
1911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
192916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
1931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	c, ret = 0;
1941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1966e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	if (!info->xmit.buf)
197d88405d44fd30fcbe77a9db540afd8823b30afdcJiri Slaby		return 0;
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (1) {
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (count < c)
2031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			c = count;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c <= 0) {
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(info->xmit.buf + info->xmit.head, buf, c);
2081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.head = ((info->xmit.head + c) &
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   (SERIAL_XMIT_SIZE-1));
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf += c;
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= c;
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret += c;
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Hey, we transmit directly from here in our case
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
218f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) &&
219ee7970690568b0c875467f475d9c957ec0d9e099Jiri Slaby			!tty->stopped)
2205e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		transmit_chars(tty, info, NULL);
221f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write_room(struct tty_struct *tty)
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
227916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_chars_in_buffer(struct tty_struct *tty)
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
234916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2381da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_buffer(struct tty_struct *tty)
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
241916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = info->xmit.tail = 0;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
24815648f154a8faea97cbe931e189cf0a57fd066f4Alan Cox	tty_wakeup(tty);
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function is used to send a high-priority XON/XOFF character to
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the device
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_send_xchar(struct tty_struct *tty, char ch)
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
257916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->x_char = ch;
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ch) {
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * I guess we could call console->write() directly but
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * let's do that for now.
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
2655e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		transmit_chars(tty, info, NULL);
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_throttle()
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called by the upper-layer tty layer to signal that
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * incoming characters should be throttled.
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
2761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_throttle(struct tty_struct * tty)
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
279f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (I_IXOFF(tty))
280f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		rs_send_xchar(tty, STOP_CHAR(tty));
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_throttle called\n");
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_unthrottle(struct tty_struct * tty)
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
287916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (I_IXOFF(tty)) {
2901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->x_char)
2911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->x_char = 0;
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rs_send_xchar(tty, START_CHAR(tty));
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_unthrottle called\n");
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
29810e82f6ce76351425644bccc56f8e2c2ad596ce6Tony Luckstatic int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
3020587102cf9f427c185bfdeb2cef41e13ee0264b1Alan Cox	    (cmd != TIOCMIWAIT)) {
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tty->flags & (1 << TTY_IO_ERROR))
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    return -EIO;
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
308f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCGSERIAL:
309f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSSERIAL:
310f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGSTRUCT:
311f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCMIWAIT:
312f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return 0;
313f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERCONFIG:
314f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGETLSR: /* Get line status register */
315f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return -EINVAL;
316f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGWILD:
317f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERSWILD:
318f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		/* "setserial -W" is called in Debian boot */
319f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n");
320f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return 0;
321f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	}
322f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	return -ENOIOCTLCMD;
3231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
3261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine will shutdown a serial port; interrupts are disabled, and
3291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * DTR is dropped if the hangup on close termio flag is on.
3301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
331458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slabystatic void shutdown(struct tty_port *port)
3321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
333458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	struct serial_state *info = container_of(port, struct serial_state,
334458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby			port);
335458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	unsigned long flags;
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
338f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->irq)
339f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		free_irq(info->irq, info);
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
341f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->xmit.buf) {
342f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		free_page((unsigned long) info->xmit.buf);
343f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		info->xmit.buf = NULL;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_close(struct tty_struct *tty, struct file * filp)
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
350916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
35137343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby
352458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	tty_port_close(&info->port, tty, filp);
3531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_hangup(struct tty_struct *tty)
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
357916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
3581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rs_flush_buffer(tty);
360458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	tty_port_hangup(&info->port);
3611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3639aead90a7f5772fc74f733242d953688748b0ce4Jiri Slabystatic int activate(struct tty_port *port, struct tty_struct *tty)
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3659aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	struct serial_state *state = container_of(port, struct serial_state,
3669aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby			port);
367f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	unsigned long flags, page;
368f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	int retval = 0;
3691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	page = get_zeroed_page(GFP_KERNEL);
3711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!page)
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
376916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	if (state->xmit.buf)
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page(page);
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
379916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby		state->xmit.buf = (unsigned char *) page;
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
381964105b501071e8a0e9feb1d0e4b3e46508bc38eJiri Slaby	if (state->irq) {
3822f8c521a1d41faf96f729c76991eb4ad70294513Jiri Slaby		retval = request_irq(state->irq, rs_interrupt_single, 0,
383916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby				"simserial", state);
3849e12dd5fce1c676e709625bd2f55dc83664c3c93Jiri Slaby		if (retval)
3851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto errout;
3861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
388916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	state->xmit.head = state->xmit.tail = 0;
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Set up the tty->alt_speed kludge
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
39301bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
3945e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 57600;
39501bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
3965e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 115200;
39701bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
3985e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 230400;
39901bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
4005e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 460800;
4011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserrout:
4031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called whenever a serial port is opened.  It
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * enables interrupts for a serial port, linking in its async structure into
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the IRQ chain.   It also performs the serial-specific
4121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * initialization for the tty structure.
4131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_open(struct tty_struct *tty, struct file * filp)
4151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
416916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = rs_table + tty->index;
4177f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	struct tty_port *port = &info->port;
4181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
419916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	tty->driver_data = info;
420d6c53c0e9bd0a83f9f9ddbc9fd80141a54d83896Jiri Slaby	port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * figure out which console to use (should be one already)
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console = console_drivers;
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (console) {
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((console->flags & CON_ENABLED) && console->write) break;
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console = console->next;
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4319aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	return tty_port_open(port, tty, filp);
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * /proc fs routines....
4361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
438bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic int rs_proc_show(struct seq_file *m, void *v)
4391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
440bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	int i;
441bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
4426e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	seq_printf(m, "simserinfo:1.0\n");
443bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	for (i = 0; i < NR_PORTS; i++)
44498e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby		seq_printf(m, "%d: uart:16550 port:3F8 irq:%d\n",
44598e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby		       i, rs_table[i].irq);
446bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	return 0;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
449bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic int rs_proc_open(struct inode *inode, struct file *file)
450bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan{
451bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	return single_open(file, rs_proc_show, NULL);
452bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan}
453bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
454bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic const struct file_operations rs_proc_fops = {
455bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.owner		= THIS_MODULE,
456bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.open		= rs_proc_open,
457bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.read		= seq_read,
458bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.llseek		= seq_lseek,
459bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.release	= single_release,
460bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan};
461bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
462b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations hp_ops = {
4631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open = rs_open,
4641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close = rs_close,
4651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = rs_write,
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.put_char = rs_put_char,
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_chars = rs_flush_chars,
4681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room = rs_write_room,
4691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer = rs_chars_in_buffer,
4701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_buffer = rs_flush_buffer,
4711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl = rs_ioctl,
4721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle = rs_throttle,
4731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle = rs_unthrottle,
4741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.send_xchar = rs_send_xchar,
4751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hangup = rs_hangup,
476bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.proc_fops = &rs_proc_fops,
4771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
47937343030458c0eea3f1093b09fc604d4f300eac7Jiri Slabystatic const struct tty_port_operations hp_port_ops = {
4809aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	.activate = activate,
481458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	.shutdown = shutdown,
48237343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby};
48337343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby
484fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slabystatic int __init simrs_init(void)
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
486fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	struct serial_state *state;
487fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	int retval;
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ia64_platform_is("hpsim"))
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
492410235fd4d20b8feaf8930a0575d23acc088aa87Jiri Slaby	hp_simserial_driver = alloc_tty_driver(NR_PORTS);
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!hp_simserial_driver)
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4966e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	printk(KERN_INFO "SimSerial driver with no serial options enabled\n");
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize the tty_driver structure */
4991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->driver_name = "simserial";
5011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->name = "ttyS";
5021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->major = TTY_MAJOR;
5031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->minor_start = 64;
5041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL;
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL;
5061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios = tty_std_termios;
5071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios.c_cflag =
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW;
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(hp_simserial_driver, &hp_ops);
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
512fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	state = rs_table;
5137f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	tty_port_init(&state->port);
51437343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby	state->port.ops = &hp_port_ops;
5157f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	state->port.close_delay = 0; /* XXX really 0? */
516fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby
517fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	retval = hpsim_get_irq(KEYBOARD_INTR);
518fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	if (retval < 0) {
519fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		printk(KERN_ERR "%s: out of interrupt vectors!\n",
520fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby				__func__);
521fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		goto err_free_tty;
522fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	}
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
524fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	state->irq = retval;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
526fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	/* the port is imaginary */
52798e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby	printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq);
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
529b19e2ca77ee4becadc85341bb0c1cee454dd4fd5Jiri Slaby	tty_port_link_device(&state->port, hp_simserial_driver, 0);
530fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	retval = tty_register_driver(hp_simserial_driver);
531fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	if (retval) {
532fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		printk(KERN_ERR "Couldn't register simserial driver\n");
533fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		goto err_free_tty;
5341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
537fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slabyerr_free_tty:
538fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	put_tty_driver(hp_simserial_driver);
539191c5f10275cfbb36802edadbdb10c73537327b4Jiri Slaby	tty_port_destroy(&state->port);
540fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	return retval;
5411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
5441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__initcall(simrs_init);
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
546