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