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