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