simserial.c revision d6c53c0e9bd0a83f9f9ddbc9fd80141a54d83896
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
562fcd5caf6d9dbf274ac7ef277f1cc541f1be9784Jiri Slabystatic void receive_chars(struct tty_struct *tty)
571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
5892a19f9cec9a80ad93c06e115822deb729e2c6adJiri Slaby	struct tty_port *port = tty->port;
591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned char ch;
601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	static unsigned char seen_esc = 0;
611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while ( (ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR)) ) {
63f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		if (ch == 27 && seen_esc == 0) {
641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			seen_esc = 1;
651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			continue;
66f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		} else if (seen_esc == 1 && ch == 'O') {
67f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			seen_esc = 2;
68f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			continue;
69f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		} else if (seen_esc == 2) {
70f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			if (ch == 'P') /* F1 */
71f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				show_state();
72819c67e69c4e0062787e27dd989f5f9d00d4834eDavid Mosberger-Tang#ifdef CONFIG_MAGIC_SYSRQ
73f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			if (ch == 'S') { /* F4 */
74f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				do {
75f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby					ch = ia64_ssc(0, 0, 0, 0, SSC_GETCHAR);
76f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				} while (!ch);
77f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby				handle_sysrq(ch);
781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			}
79f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby#endif
80f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			seen_esc = 0;
81f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			continue;
821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		seen_esc = 0;
841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
8592a19f9cec9a80ad93c06e115822deb729e2c6adJiri Slaby		if (tty_insert_flip_char(port, ch, TTY_NORMAL) == 0)
86d50f5c5ca0c3426669fbe11ad4d5708d333eb9fbAndreas Schwab			break;
871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_flip_buffer_push(tty);
891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This is the serial driver's interrupt routine for a single port
931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
945dcded1b0b4f1537bb6dff453fb805517756c94bAl Virostatic irqreturn_t rs_interrupt_single(int irq, void *dev_id)
951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
96916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = dev_id;
973a5c24232463b4978acf8d8668becbf515d30a36Jiri Slaby	struct tty_struct *tty = tty_port_tty_get(&info->port);
981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
997f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	if (!tty) {
1006e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby		printk(KERN_INFO "%s: tty=0 problem\n", __func__);
1011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return IRQ_NONE;
1021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * pretty simple in our case, because we only get interrupts
1051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * on inbound traffic
1061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1077f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	receive_chars(tty);
1083a5c24232463b4978acf8d8668becbf515d30a36Jiri Slaby	tty_kref_put(tty);
1091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return IRQ_HANDLED;
1101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
1131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
1141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * Here ends the serial interrupt routines.
1151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * -------------------------------------------------------------------
1161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
1171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
118f34d7a5b7010b82fe97da95496b9971435530062Alan Coxstatic int rs_put_char(struct tty_struct *tty, unsigned char ch)
1191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
120916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
1211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1236e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	if (!info->xmit.buf)
124f34d7a5b7010b82fe97da95496b9971435530062Alan Cox		return 0;
1251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
1271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) == 0) {
1281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		local_irq_restore(flags);
129f34d7a5b7010b82fe97da95496b9971435530062Alan Cox		return 0;
1301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.buf[info->xmit.head] = ch;
1321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = (info->xmit.head + 1) & (SERIAL_XMIT_SIZE-1);
1331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
134f34d7a5b7010b82fe97da95496b9971435530062Alan Cox	return 1;
1351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1375e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slabystatic void transmit_chars(struct tty_struct *tty, struct serial_state *info,
1385e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		int *intr_done)
1391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
1401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int count;
1411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
1421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
1441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (info->x_char) {
1461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		char c = info->x_char;
1471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, &c, 1);
1491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->x_char = 0;
1511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1555e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby	if (info->xmit.head == info->xmit.tail || tty->stopped ||
1565e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby			tty->hw_stopped) {
1571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifdef SIMSERIAL_DEBUG
1581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		printk("transmit_chars: head=%d, tail=%d, stopped=%d\n",
1595e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		       info->xmit.head, info->xmit.tail, tty->stopped);
1601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
1611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		goto out;
1621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We removed the loop and try to do it in to chunks. We need
1651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * 2 operations maximum because it's a ring buffer.
1661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 *
1671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * First from current to tail if possible.
1681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Then from the beginning of the buffer until necessary
1691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = min(CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE),
1721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    SERIAL_XMIT_SIZE - info->xmit.tail);
1731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console->write(console, info->xmit.buf+info->xmit.tail, count);
1741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.tail = (info->xmit.tail+count) & (SERIAL_XMIT_SIZE-1);
1761da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
1781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * We have more at the beginning of the buffer
1791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
1801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	count = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
1811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (count) {
1821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console->write(console, info->xmit.buf, count);
1831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.tail += count;
1841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
1851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsout:
1861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
1871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_chars(struct tty_struct *tty)
1901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
191916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
1921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
193f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->xmit.head == info->xmit.tail || tty->stopped ||
194f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			tty->hw_stopped || !info->xmit.buf)
1951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return;
1961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
1975e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby	transmit_chars(tty, info, NULL);
1981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
1991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write(struct tty_struct * tty,
2011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    const unsigned char *buf, int count)
2021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
203916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	int	c, ret = 0;
2051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2076e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	if (!info->xmit.buf)
208d88405d44fd30fcbe77a9db540afd8823b30afdcJiri Slaby		return 0;
2091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (1) {
2121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		c = CIRC_SPACE_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (count < c)
2141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			c = count;
2151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (c <= 0) {
2161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			break;
2171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		}
2181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		memcpy(info->xmit.buf + info->xmit.head, buf, c);
2191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		info->xmit.head = ((info->xmit.head + c) &
2201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds				   (SERIAL_XMIT_SIZE-1));
2211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		buf += c;
2221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		count -= c;
2231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		ret += c;
2241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
2271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Hey, we transmit directly from here in our case
2281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
229f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE) &&
230f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby			!tty->stopped && !tty->hw_stopped)
2315e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		transmit_chars(tty, info, NULL);
232f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby
2331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return ret;
2341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_write_room(struct tty_struct *tty)
2371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
238916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_SPACE(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_chars_in_buffer(struct tty_struct *tty)
2441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
245916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
2481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2501da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_flush_buffer(struct tty_struct *tty)
2511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
252916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	unsigned long flags;
2541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
2561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->xmit.head = info->xmit.tail = 0;
2571da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
2581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
25915648f154a8faea97cbe931e189cf0a57fd066f4Alan Cox	tty_wakeup(tty);
2601da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This function is used to send a high-priority XON/XOFF character to
2641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the device
2651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_send_xchar(struct tty_struct *tty, char ch)
2671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
268916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2691da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2701da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	info->x_char = ch;
2711da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (ch) {
2721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		/*
2731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * I guess we could call console->write() directly but
2741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 * let's do that for now.
2751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		 */
2765e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		transmit_chars(tty, info, NULL);
2771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
2781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2791da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
2811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
2821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * rs_throttle()
2831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds *
2841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called by the upper-layer tty layer to signal that
2851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * incoming characters should be throttled.
2861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * ------------------------------------------------------------
2871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
2881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_throttle(struct tty_struct * tty)
2891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
290f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (I_IXOFF(tty))
291f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		rs_send_xchar(tty, STOP_CHAR(tty));
2921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_throttle called\n");
2941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
2951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
2961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_unthrottle(struct tty_struct * tty)
2971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
298916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
2991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3001da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (I_IXOFF(tty)) {
3011da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (info->x_char)
3021da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			info->x_char = 0;
3031da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		else
3041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			rs_send_xchar(tty, START_CHAR(tty));
3051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	printk(KERN_INFO "simrs_unthrottle called\n");
3071da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
30910e82f6ce76351425644bccc56f8e2c2ad596ce6Tony Luckstatic int rs_ioctl(struct tty_struct *tty, unsigned int cmd, unsigned long arg)
3101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
3121da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&
3130587102cf9f427c185bfdeb2cef41e13ee0264b1Alan Cox	    (cmd != TIOCMIWAIT)) {
3141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if (tty->flags & (1 << TTY_IO_ERROR))
3151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		    return -EIO;
3161da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	switch (cmd) {
319f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCGSERIAL:
320f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSSERIAL:
321f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGSTRUCT:
322f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCMIWAIT:
323f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return 0;
324f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERCONFIG:
325f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGETLSR: /* Get line status register */
326f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return -EINVAL;
327f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERGWILD:
328f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	case TIOCSERSWILD:
329f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		/* "setserial -W" is called in Debian boot */
330f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		printk (KERN_INFO "TIOCSER?WILD ioctl obsolete, ignored.\n");
331f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		return 0;
332f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	}
333f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	return -ENOIOCTLCMD;
3341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3351da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3361da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#define RELEVANT_IFLAG(iflag) (iflag & (IGNBRK|BRKINT|IGNPAR|PARMRK|INPCK))
3371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
338f889a26a703b03c774849685583cec7746738f3cTony Luckstatic void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
3391da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Handle turning off CRTSCTS */
3411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if ((old_termios->c_cflag & CRTSCTS) &&
342adc8d746caa67fff4b53ba3e5163a6cbacc3b523Alan Cox	    !(tty->termios.c_cflag & CRTSCTS)) {
3431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		tty->hw_stopped = 0;
3441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
3471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine will shutdown a serial port; interrupts are disabled, and
3481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * DTR is dropped if the hangup on close termio flag is on.
3491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
350458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slabystatic void shutdown(struct tty_port *port)
3511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
352458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	struct serial_state *info = container_of(port, struct serial_state,
353458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby			port);
354458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	unsigned long flags;
3551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
357f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->irq)
358f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		free_irq(info->irq, info);
3591da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
360f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	if (info->xmit.buf) {
361f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		free_page((unsigned long) info->xmit.buf);
362f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby		info->xmit.buf = NULL;
3631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
3641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
3651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_close(struct tty_struct *tty, struct file * filp)
3681da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
369916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
37037343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby
371458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	tty_port_close(&info->port, tty, filp);
3721da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3731da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3741da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic void rs_hangup(struct tty_struct *tty)
3751da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
376916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = tty->driver_data;
3771da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3781da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	rs_flush_buffer(tty);
379458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	tty_port_hangup(&info->port);
3801da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
3811da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3829aead90a7f5772fc74f733242d953688748b0ce4Jiri Slabystatic int activate(struct tty_port *port, struct tty_struct *tty)
3831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
3849aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	struct serial_state *state = container_of(port, struct serial_state,
3859aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby			port);
386f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	unsigned long flags, page;
387f66279cd7d5cdf43686da0d9ed20378581b88d0fJiri Slaby	int retval = 0;
3881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	page = get_zeroed_page(GFP_KERNEL);
3901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!page)
3911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
3921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
3931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_save(flags);
3941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
395916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	if (state->xmit.buf)
3961da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		free_page(page);
3971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	else
398916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby		state->xmit.buf = (unsigned char *) page;
3991da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
400964105b501071e8a0e9feb1d0e4b3e46508bc38eJiri Slaby	if (state->irq) {
4012f8c521a1d41faf96f729c76991eb4ad70294513Jiri Slaby		retval = request_irq(state->irq, rs_interrupt_single, 0,
402916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby				"simserial", state);
4039e12dd5fce1c676e709625bd2f55dc83664c3c93Jiri Slaby		if (retval)
4041da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds			goto errout;
4051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4061da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
407916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	state->xmit.head = state->xmit.tail = 0;
4081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * Set up the tty->alt_speed kludge
4111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
41201bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
4135e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 57600;
41401bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
4155e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 115200;
41601bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
4175e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 230400;
41801bd730d92bd002adc3f3317d8e3328c629b436cJiri Slaby	if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
4195e99d5458729b0eb763ca83a2fbb95f6276c4243Jiri Slaby		tty->alt_speed = 460800;
4201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldserrout:
4221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	local_irq_restore(flags);
4231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return retval;
4241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * This routine is called whenever a serial port is opened.  It
4291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * enables interrupts for a serial port, linking in its async structure into
4301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * the IRQ chain.   It also performs the serial-specific
4311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * initialization for the tty structure.
4321da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4331da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvaldsstatic int rs_open(struct tty_struct *tty, struct file * filp)
4341da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
435916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	struct serial_state *info = rs_table + tty->index;
4367f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	struct tty_port *port = &info->port;
4371da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
438916b765675b7044bd5895b7430a2aa2c63ea4545Jiri Slaby	tty->driver_data = info;
439d6c53c0e9bd0a83f9f9ddbc9fd80141a54d83896Jiri Slaby	port->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
4401da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4411da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/*
4421da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 * figure out which console to use (should be one already)
4431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	 */
4441da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	console = console_drivers;
4451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	while (console) {
4461da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		if ((console->flags & CON_ENABLED) && console->write) break;
4471da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		console = console->next;
4481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
4491da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4509aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	return tty_port_open(port, tty, filp);
4511da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4521da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
4531da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds/*
4541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds * /proc fs routines....
4551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds */
4561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
457bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic int rs_proc_show(struct seq_file *m, void *v)
4581da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
459bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	int i;
460bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
4616e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	seq_printf(m, "simserinfo:1.0\n");
462bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	for (i = 0; i < NR_PORTS; i++)
46398e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby		seq_printf(m, "%d: uart:16550 port:3F8 irq:%d\n",
46498e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby		       i, rs_table[i].irq);
465bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	return 0;
4661da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
4671da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
468bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic int rs_proc_open(struct inode *inode, struct file *file)
469bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan{
470bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	return single_open(file, rs_proc_show, NULL);
471bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan}
472bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
473bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyanstatic const struct file_operations rs_proc_fops = {
474bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.owner		= THIS_MODULE,
475bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.open		= rs_proc_open,
476bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.read		= seq_read,
477bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.llseek		= seq_lseek,
478bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.release	= single_release,
479bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan};
480bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan
481b68e31d0ebbcc909d1941f9f230c9d062a3a13d3Jeff Dikestatic const struct tty_operations hp_ops = {
4821da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.open = rs_open,
4831da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.close = rs_close,
4841da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write = rs_write,
4851da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.put_char = rs_put_char,
4861da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_chars = rs_flush_chars,
4871da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.write_room = rs_write_room,
4881da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.chars_in_buffer = rs_chars_in_buffer,
4891da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.flush_buffer = rs_flush_buffer,
4901da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.ioctl = rs_ioctl,
4911da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.throttle = rs_throttle,
4921da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.unthrottle = rs_unthrottle,
4931da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.send_xchar = rs_send_xchar,
4941da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.set_termios = rs_set_termios,
4951da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	.hangup = rs_hangup,
496bf54215ef86a1bd83affd8ecdf833c053aefb49dAlexey Dobriyan	.proc_fops = &rs_proc_fops,
4971da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds};
4981da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
49937343030458c0eea3f1093b09fc604d4f300eac7Jiri Slabystatic const struct tty_port_operations hp_port_ops = {
5009aead90a7f5772fc74f733242d953688748b0ce4Jiri Slaby	.activate = activate,
501458cd31a4c33ce489eb538193f801ac73ff4010bJiri Slaby	.shutdown = shutdown,
50237343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby};
50337343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby
504fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slabystatic int __init simrs_init(void)
5051da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds{
506fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	struct serial_state *state;
507fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	int retval;
5081da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5091da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!ia64_platform_is("hpsim"))
5101da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENODEV;
5111da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
512410235fd4d20b8feaf8930a0575d23acc088aa87Jiri Slaby	hp_simserial_driver = alloc_tty_driver(NR_PORTS);
5131da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	if (!hp_simserial_driver)
5141da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		return -ENOMEM;
5151da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5166e9ebcfa20b15f0ccdd4b5395a3c63f79c21fa57Jiri Slaby	printk(KERN_INFO "SimSerial driver with no serial options enabled\n");
5171da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5181da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	/* Initialize the tty_driver structure */
5191da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5201da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->driver_name = "simserial";
5211da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->name = "ttyS";
5221da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->major = TTY_MAJOR;
5231da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->minor_start = 64;
5241da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->type = TTY_DRIVER_TYPE_SERIAL;
5251da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->subtype = SERIAL_TYPE_NORMAL;
5261da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios = tty_std_termios;
5271da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->init_termios.c_cflag =
5281da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds		B9600 | CS8 | CREAD | HUPCL | CLOCAL;
5291da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	hp_simserial_driver->flags = TTY_DRIVER_REAL_RAW;
5301da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	tty_set_operations(hp_simserial_driver, &hp_ops);
5311da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
532fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	state = rs_table;
5337f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	tty_port_init(&state->port);
53437343030458c0eea3f1093b09fc604d4f300eac7Jiri Slaby	state->port.ops = &hp_port_ops;
5357f32f8dd349bae106eccb0b9759c932875d6622eJiri Slaby	state->port.close_delay = 0; /* XXX really 0? */
536fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby
537fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	retval = hpsim_get_irq(KEYBOARD_INTR);
538fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	if (retval < 0) {
539fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		printk(KERN_ERR "%s: out of interrupt vectors!\n",
540fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby				__func__);
541fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		goto err_free_tty;
542fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	}
5431da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
544fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	state->irq = retval;
5451da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
546fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	/* the port is imaginary */
54798e3a9e6dd99f1b8ac2a03b8b4942eec16ef911bJiri Slaby	printk(KERN_INFO "ttyS0 at 0x03f8 (irq = %d) is a 16550\n", state->irq);
5481da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
549b19e2ca77ee4becadc85341bb0c1cee454dd4fd5Jiri Slaby	tty_port_link_device(&state->port, hp_simserial_driver, 0);
550fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	retval = tty_register_driver(hp_simserial_driver);
551fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	if (retval) {
552fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		printk(KERN_ERR "Couldn't register simserial driver\n");
553fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby		goto err_free_tty;
5541da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	}
5551da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5561da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds	return 0;
557fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slabyerr_free_tty:
558fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	put_tty_driver(hp_simserial_driver);
559191c5f10275cfbb36802edadbdb10c73537327b4Jiri Slaby	tty_port_destroy(&state->port);
560fd2d7a6e60068779bc72029f867b51d3dc2fe0ccJiri Slaby	return retval;
5611da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds}
5621da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds
5631da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#ifndef MODULE
5641da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds__initcall(simrs_init);
5651da177e4c3f41524e886b7f1b8a0c1fc7321cacLinus Torvalds#endif
566