aboutsummaryrefslogtreecommitdiff
path: root/drivers/char
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-12-11 15:34:40 -0800
committerLinus Torvalds <torvalds@linux-foundation.org>2009-12-11 15:34:40 -0800
commit0f4974c439dd7826c85bae4e6a8088ce2db0f498 (patch)
treefdabc7d9bb7d7bc49aad547c0aac3a633ce01f09 /drivers/char
parent3126c136bc30225d7a43af741778aa50e95e467a (diff)
parent36ba782e9674cdc29ec7003757df0b375e99fa96 (diff)
Merge git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6
* git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty-2.6: (58 commits) tty: split the lock up a bit further tty: Move the leader test in disassociate tty: Push the bkl down a bit in the hangup code tty: Push the lock down further into the ldisc code tty: push the BKL down into the handlers a bit tty: moxa: split open lock tty: moxa: Kill the use of lock_kernel tty: moxa: Fix modem op locking tty: moxa: Kill off the throttle method tty: moxa: Locking clean up tty: moxa: rework the locking a bit tty: moxa: Use more tty_port ops tty: isicom: fix deadlock on shutdown tty: mxser: Use the new locking rules to fix setserial properly tty: mxser: use the tty_port_open method tty: isicom: sort out the board init logic tty: isicom: switch to the new tty_port_open helper tty: tty_port: Add a kref object to the tty port tty: istallion: tty port open/close methods tty: stallion: Convert to the tty_port_open/close methods ...
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/Kconfig13
-rw-r--r--drivers/char/Makefile1
-rw-r--r--drivers/char/bfin_jtag_comm.c2
-rw-r--r--drivers/char/epca.c2
-rw-r--r--drivers/char/esp.c2533
-rw-r--r--drivers/char/isicom.c115
-rw-r--r--drivers/char/istallion.c185
-rw-r--r--drivers/char/moxa.c289
-rw-r--r--drivers/char/mxser.c248
-rw-r--r--drivers/char/pcmcia/ipwireless/tty.c2
-rw-r--r--drivers/char/pty.c2
-rw-r--r--drivers/char/riscom8.c89
-rw-r--r--drivers/char/stallion.c129
-rw-r--r--drivers/char/tty_io.c151
-rw-r--r--drivers/char/tty_ldisc.c23
-rw-r--r--drivers/char/tty_port.c97
16 files changed, 554 insertions, 3327 deletions
diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig
index 6aad99ec4e0..6f31c947210 100644
--- a/drivers/char/Kconfig
+++ b/drivers/char/Kconfig
@@ -201,19 +201,6 @@ config DIGIEPCA
To compile this driver as a module, choose M here: the
module will be called epca.
-config ESPSERIAL
- tristate "Hayes ESP serial port support"
- depends on SERIAL_NONSTANDARD && ISA && ISA_DMA_API && BROKEN
- help
- This is a driver which supports Hayes ESP serial ports. Both single
- port cards and multiport cards are supported. Make sure to read
- <file:Documentation/hayes-esp.txt>.
-
- To compile this driver as a module, choose M here: the
- module will be called esp.
-
- If unsure, say N.
-
config MOXA_INTELLIO
tristate "Moxa Intellio support"
depends on SERIAL_NONSTANDARD && (ISA || EISA || PCI)
diff --git a/drivers/char/Makefile b/drivers/char/Makefile
index 19a79dd79ee..f957edf7e45 100644
--- a/drivers/char/Makefile
+++ b/drivers/char/Makefile
@@ -18,7 +18,6 @@ obj-$(CONFIG_CONSOLE_TRANSLATIONS) += consolemap.o consolemap_deftbl.o
obj-$(CONFIG_HW_CONSOLE) += vt.o defkeymap.o
obj-$(CONFIG_AUDIT) += tty_audit.o
obj-$(CONFIG_MAGIC_SYSRQ) += sysrq.o
-obj-$(CONFIG_ESPSERIAL) += esp.o
obj-$(CONFIG_MVME147_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_MVME162_SCC) += generic_serial.o vme_scc.o
obj-$(CONFIG_BVME6000_SCC) += generic_serial.o vme_scc.o
diff --git a/drivers/char/bfin_jtag_comm.c b/drivers/char/bfin_jtag_comm.c
index 1d7c34c73b2..2628c7415ea 100644
--- a/drivers/char/bfin_jtag_comm.c
+++ b/drivers/char/bfin_jtag_comm.c
@@ -226,7 +226,7 @@ bfin_jc_wait_until_sent(struct tty_struct *tty, int timeout)
}
}
-static struct tty_operations bfin_jc_ops = {
+static const struct tty_operations bfin_jc_ops = {
.open = bfin_jc_open,
.close = bfin_jc_close,
.write = bfin_jc_write,
diff --git a/drivers/char/epca.c b/drivers/char/epca.c
index dde5134713e..17b044a71e0 100644
--- a/drivers/char/epca.c
+++ b/drivers/char/epca.c
@@ -935,7 +935,7 @@ static int info_open(struct tty_struct *tty, struct file *filp)
return 0;
}
-static struct tty_operations info_ops = {
+static const struct tty_operations info_ops = {
.open = info_open,
.ioctl = info_ioctl,
};
diff --git a/drivers/char/esp.c b/drivers/char/esp.c
deleted file mode 100644
index b19d43cd954..00000000000
--- a/drivers/char/esp.c
+++ /dev/null
@@ -1,2533 +0,0 @@
-/*
- * esp.c - driver for Hayes ESP serial cards
- *
- * --- Notices from serial.c, upon which this driver is based ---
- *
- * Copyright (C) 1991, 1992 Linus Torvalds
- *
- * Extensively rewritten by Theodore Ts'o, 8/16/92 -- 9/14/92. Now
- * much more extensible to support other serial cards based on the
- * 16450/16550A UART's. Added support for the AST FourPort and the
- * Accent Async board.
- *
- * set_serial_info fixed to set the flags, custom divisor, and uart
- * type fields. Fix suggested by Michael K. Johnson 12/12/92.
- *
- * 11/95: TIOCMIWAIT, TIOCGICOUNT by Angelo Haritsis <ah@doc.ic.ac.uk>
- *
- * 03/96: Modularised by Angelo Haritsis <ah@doc.ic.ac.uk>
- *
- * rs_set_termios fixed to look also for changes of the input
- * flags INPCK, BRKINT, PARMRK, IGNPAR and IGNBRK.
- * Bernd Anhäupl 05/17/96.
- *
- * --- End of notices from serial.c ---
- *
- * Support for the ESP serial card by Andrew J. Robinson
- * <arobinso@nyx.net> (Card detection routine taken from a patch
- * by Dennis J. Boylan). Patches to allow use with 2.1.x contributed
- * by Chris Faylor.
- *
- * Most recent changes: (Andrew J. Robinson)
- * Support for PIO mode. This allows the driver to work properly with
- * multiport cards.
- *
- * Arnaldo Carvalho de Melo <acme@conectiva.com.br> -
- * several cleanups, use module_init/module_exit, etc
- *
- * This module exports the following rs232 io functions:
- *
- * int espserial_init(void);
- */
-
-#include <linux/module.h>
-#include <linux/errno.h>
-#include <linux/signal.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
-#include <linux/tty.h>
-#include <linux/tty_flip.h>
-#include <linux/serial.h>
-#include <linux/serialP.h>
-#include <linux/serial_reg.h>
-#include <linux/major.h>
-#include <linux/string.h>
-#include <linux/fcntl.h>
-#include <linux/ptrace.h>
-#include <linux/ioport.h>
-#include <linux/mm.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/bitops.h>
-
-#include <asm/system.h>
-#include <linux/io.h>
-
-#include <asm/dma.h>
-#include <linux/slab.h>
-#include <linux/uaccess.h>
-
-#include <linux/hayesesp.h>
-
-#define NR_PORTS 64 /* maximum number of ports */
-#define NR_PRIMARY 8 /* maximum number of primary ports */
-#define REGION_SIZE 8 /* size of io region to request */
-
-/* The following variables can be set by giving module options */
-static int irq[NR_PRIMARY]; /* IRQ for each base port */
-static unsigned int divisor[NR_PRIMARY]; /* custom divisor for each port */
-static unsigned int dma = ESP_DMA_CHANNEL; /* DMA channel */
-static unsigned int rx_trigger = ESP_RX_TRIGGER;
-static unsigned int tx_trigger = ESP_TX_TRIGGER;
-static unsigned int flow_off = ESP_FLOW_OFF;
-static unsigned int flow_on = ESP_FLOW_ON;
-static unsigned int rx_timeout = ESP_RX_TMOUT;
-static unsigned int pio_threshold = ESP_PIO_THRESHOLD;
-
-MODULE_LICENSE("GPL");
-
-module_param_array(irq, int, NULL, 0);
-module_param_array(divisor, uint, NULL, 0);
-module_param(dma, uint, 0);
-module_param(rx_trigger, uint, 0);
-module_param(tx_trigger, uint, 0);
-module_param(flow_off, uint, 0);
-module_param(flow_on, uint, 0);
-module_param(rx_timeout, uint, 0);
-module_param(pio_threshold, uint, 0);
-
-/* END */
-
-static char *dma_buffer;
-static int dma_bytes;
-static struct esp_pio_buffer *free_pio_buf;
-
-#define DMA_BUFFER_SZ 1024
-
-#define WAKEUP_CHARS 1024
-
-static char serial_name[] __initdata = "ESP serial driver";
-static char serial_version[] __initdata = "2.2";
-
-static struct tty_driver *esp_driver;
-
-/*
- * Serial driver configuration section. Here are the various options:
- *
- * SERIAL_PARANOIA_CHECK
- * Check the magic number for the esp_structure where
- * ever possible.
- */
-
-#undef SERIAL_PARANOIA_CHECK
-#define SERIAL_DO_RESTART
-
-#undef SERIAL_DEBUG_INTR
-#undef SERIAL_DEBUG_OPEN
-#undef SERIAL_DEBUG_FLOW
-
-#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
-#define DBG_CNT(s) printk(KERN_DEBUG "(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
- tty->name, info->port.flags, \
- serial_driver.refcount, \
- info->port.count, tty->count, s)
-#else
-#define DBG_CNT(s)
-#endif
-
-static struct esp_struct *ports;
-
-static void change_speed(struct esp_struct *info);
-static void rs_wait_until_sent(struct tty_struct *, int);
-
-/*
- * The ESP card has a clock rate of 14.7456 MHz (that is, 2**ESPC_SCALE
- * times the normal 1.8432 Mhz clock of most serial boards).
- */
-#define BASE_BAUD ((1843200 / 16) * (1 << ESPC_SCALE))
-
-/* Standard COM flags (except for COM4, because of the 8514 problem) */
-#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
-
-static inline int serial_paranoia_check(struct esp_struct *info,
- char *name, const char *routine)
-{
-#ifdef SERIAL_PARANOIA_CHECK
- static const char badmagic[] = KERN_WARNING
- "Warning: bad magic number for serial struct (%s) in %s\n";
- static const char badinfo[] = KERN_WARNING
- "Warning: null esp_struct for (%s) in %s\n";
-
- if (!info) {
- printk(badinfo, name, routine);
- return 1;
- }
- if (info->magic != ESP_MAGIC) {
- printk(badmagic, name, routine);
- return 1;
- }
-#endif
- return 0;
-}
-
-static inline unsigned int serial_in(struct esp_struct *info, int offset)
-{
- return inb(info->io_port + offset);
-}
-
-static inline void serial_out(struct esp_struct *info, int offset,
- unsigned char value)
-{
- outb(value, info->io_port+offset);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_stop() and rs_start()
- *
- * This routines are called before setting or resetting tty->stopped.
- * They enable or disable transmitter interrupts, as necessary.
- * ------------------------------------------------------------
- */
-static void rs_stop(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_stop"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->IER & UART_IER_THRI) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_start(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_start"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->xmit_cnt && info->xmit_buf && !(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * ----------------------------------------------------------------------
- *
- * Here starts the interrupt handling routines. All of the following
- * subroutines are declared as inline and are folded into
- * rs_interrupt(). They were separated out for readability's sake.
- *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
- * runs with interrupts turned off. People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
- * possible. After you are done making modifications, it is not a bad
- * idea to do:
- *
- * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
- *
- * and look at the resulting assemble code in serial.s.
- *
- * - Ted Ts'o (tytso@mit.edu), 7-Mar-93
- * -----------------------------------------------------------------------
- */
-
-static DEFINE_SPINLOCK(pio_lock);
-
-static inline struct esp_pio_buffer *get_pio_buffer(void)
-{
- struct esp_pio_buffer *buf;
- unsigned long flags;
-
- spin_lock_irqsave(&pio_lock, flags);
- if (free_pio_buf) {
- buf = free_pio_buf;
- free_pio_buf = buf->next;
- } else {
- buf = kmalloc(sizeof(struct esp_pio_buffer), GFP_ATOMIC);
- }
- spin_unlock_irqrestore(&pio_lock, flags);
- return buf;
-}
-
-static inline void release_pio_buffer(struct esp_pio_buffer *buf)
-{
- unsigned long flags;
- spin_lock_irqsave(&pio_lock, flags);
- buf->next = free_pio_buf;
- free_pio_buf = buf;
- spin_unlock_irqrestore(&pio_lock, flags);
-}
-
-static inline void receive_chars_pio(struct esp_struct *info, int num_bytes)
-{
- struct tty_struct *tty = info->port.tty;
- int i;
- struct esp_pio_buffer *pio_buf;
- struct esp_pio_buffer *err_buf;
- unsigned char status_mask;
-
- pio_buf = get_pio_buffer();
-
- if (!pio_buf)
- return;
-
- err_buf = get_pio_buffer();
-
- if (!err_buf) {
- release_pio_buffer(pio_buf);
- return;
- }
-
- status_mask = (info->read_status_mask >> 2) & 0x07;
-
- for (i = 0; i < num_bytes - 1; i += 2) {
- *((unsigned short *)(pio_buf->data + i)) =
- inw(info->io_port + UART_ESI_RX);
- err_buf->data[i] = serial_in(info, UART_ESI_RWS);
- err_buf->data[i + 1] = (err_buf->data[i] >> 3) & status_mask;
- err_buf->data[i] &= status_mask;
- }
-
- if (num_bytes & 0x0001) {
- pio_buf->data[num_bytes - 1] = serial_in(info, UART_ESI_RX);
- err_buf->data[num_bytes - 1] =
- (serial_in(info, UART_ESI_RWS) >> 3) & status_mask;
- }
-
- /* make sure everything is still ok since interrupts were enabled */
- tty = info->port.tty;
-
- if (!tty) {
- release_pio_buffer(pio_buf);
- release_pio_buffer(err_buf);
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- return;
- }
-
- status_mask = (info->ignore_status_mask >> 2) & 0x07;
-
- for (i = 0; i < num_bytes; i++) {
- if (!(err_buf->data[i] & status_mask)) {
- int flag = 0;
-
- if (err_buf->data[i] & 0x04) {
- flag = TTY_BREAK;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (err_buf->data[i] & 0x02)
- flag = TTY_FRAME;
- else if (err_buf->data[i] & 0x01)
- flag = TTY_PARITY;
- tty_insert_flip_char(tty, pio_buf->data[i], flag);
- }
- }
-
- tty_schedule_flip(tty);
-
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- release_pio_buffer(pio_buf);
- release_pio_buffer(err_buf);
-}
-
-static void program_isa_dma(int dma, int dir, unsigned long addr, int len)
-{
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma, dir);
- set_dma_addr(dma, addr);
- set_dma_count(dma, len);
- enable_dma(dma);
- release_dma_lock(flags);
-}
-
-static void receive_chars_dma(struct esp_struct *info, int num_bytes)
-{
- info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
- dma_bytes = num_bytes;
- info->stat_flags |= ESP_STAT_DMA_RX;
-
- program_isa_dma(dma, DMA_MODE_READ, isa_virt_to_bus(dma_buffer),
- dma_bytes);
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
-}
-
-static inline void receive_chars_dma_done(struct esp_struct *info,
- int status)
-{
- struct tty_struct *tty = info->port.tty;
- int num_bytes;
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
-
- info->stat_flags &= ~ESP_STAT_DMA_RX;
- num_bytes = dma_bytes - get_dma_residue(dma);
- release_dma_lock(flags);
-
- info->icount.rx += num_bytes;
-
- if (num_bytes > 0) {
- tty_insert_flip_string(tty, dma_buffer, num_bytes - 1);
-
- status &= (0x1c & info->read_status_mask);
-
- /* Is the status significant or do we throw the last byte ? */
- if (!(status & info->ignore_status_mask)) {
- int statflag = 0;
-
- if (status & 0x10) {
- statflag = TTY_BREAK;
- (info->icount.brk)++;
- if (info->port.flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (status & 0x08) {
- statflag = TTY_FRAME;
- info->icount.frame++;
- } else if (status & 0x04) {
- statflag = TTY_PARITY;
- info->icount.parity++;
- }
- tty_insert_flip_char(tty, dma_buffer[num_bytes - 1],
- statflag);
- }
- tty_schedule_flip(tty);
- }
-
- if (dma_bytes != num_bytes) {
- num_bytes = dma_bytes - num_bytes;
- dma_bytes = 0;
- receive_chars_dma(info, num_bytes);
- } else
- dma_bytes = 0;
-}
-
-/* Caller must hold info->lock */
-
-static inline void transmit_chars_pio(struct esp_struct *info,
- int space_avail)
-{
- int i;
- struct esp_pio_buffer *pio_buf;
-
- pio_buf = get_pio_buffer();
-
- if (!pio_buf)
- return;
-
- while (space_avail && info->xmit_cnt) {
- if (info->xmit_tail + space_avail <= ESP_XMIT_SIZE) {
- memcpy(pio_buf->data,
- &(info->xmit_buf[info->xmit_tail]),
- space_avail);
- } else {
- i = ESP_XMIT_SIZE - info->xmit_tail;
- memcpy(pio_buf->data,
- &(info->xmit_buf[info->xmit_tail]), i);
- memcpy(&(pio_buf->data[i]), info->xmit_buf,
- space_avail - i);
- }
-
- info->xmit_cnt -= space_avail;
- info->xmit_tail = (info->xmit_tail + space_avail) &
- (ESP_XMIT_SIZE - 1);
-
- for (i = 0; i < space_avail - 1; i += 2) {
- outw(*((unsigned short *)(pio_buf->data + i)),
- info->io_port + UART_ESI_TX);
- }
-
- if (space_avail & 0x0001)
- serial_out(info, UART_ESI_TX,
- pio_buf->data[space_avail - 1]);
-
- if (info->xmit_cnt) {
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- space_avail = serial_in(info, UART_ESI_STAT1) << 8;
- space_avail |= serial_in(info, UART_ESI_STAT2);
-
- if (space_avail > info->xmit_cnt)
- space_avail = info->xmit_cnt;
- }
- }
-
- if (info->xmit_cnt < WAKEUP_CHARS) {
- if (info->port.tty)
- tty_wakeup(info->port.tty);
-
-#ifdef SERIAL_DEBUG_INTR
- printk("THRE...");
-#endif
-
- if (info->xmit_cnt <= 0) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1,
- ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- }
-
- release_pio_buffer(pio_buf);
-}
-
-/* Caller must hold info->lock */
-static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes)
-{
- dma_bytes = num_bytes;
-
- if (info->xmit_tail + dma_bytes <= ESP_XMIT_SIZE) {
- memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
- dma_bytes);
- } else {
- int i = ESP_XMIT_SIZE - info->xmit_tail;
- memcpy(dma_buffer, &(info->xmit_buf[info->xmit_tail]),
- i);
- memcpy(&(dma_buffer[i]), info->xmit_buf, dma_bytes - i);
- }
-
- info->xmit_cnt -= dma_bytes;
- info->xmit_tail = (info->xmit_tail + dma_bytes) & (ESP_XMIT_SIZE - 1);
-
- if (info->xmit_cnt < WAKEUP_CHARS) {
- if (info->port.tty)
- tty_wakeup(info->port.tty);
-
-#ifdef SERIAL_DEBUG_INTR
- printk("THRE...");
-#endif
-
- if (info->xmit_cnt <= 0) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
- }
-
- info->stat_flags |= ESP_STAT_DMA_TX;
-
- program_isa_dma(dma, DMA_MODE_WRITE, isa_virt_to_bus(dma_buffer),
- dma_bytes);
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
-}
-
-static inline void transmit_chars_dma_done(struct esp_struct *info)
-{
- int num_bytes;
- unsigned long flags;
-
- flags = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
-
- num_bytes = dma_bytes - get_dma_residue(dma);
- info->icount.tx += dma_bytes;
- release_dma_lock(flags);
-
- if (dma_bytes != num_bytes) {
- dma_bytes -= num_bytes;
- memmove(dma_buffer, dma_buffer + num_bytes, dma_bytes);
-
- program_isa_dma(dma, DMA_MODE_WRITE,
- isa_virt_to_bus(dma_buffer), dma_bytes);
-
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
- } else {
- dma_bytes = 0;
- info->stat_flags &= ~ESP_STAT_DMA_TX;
- }
-}
-
-static void check_modem_status(struct esp_struct *info)
-{
- int status;
-
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT2);
-
- if (status & UART_MSR_ANY_DELTA) {
- /* update input line counters */
- if (status & UART_MSR_TERI)
- info->icount.rng++;
- if (status & UART_MSR_DDSR)
- info->icount.dsr++;
- if (status & UART_MSR_DDCD)
- info->icount.dcd++;
- if (status & UART_MSR_DCTS)
- info->icount.cts++;
- wake_up_interruptible(&info->port.delta_msr_wait);
- }
-
- if ((info->port.flags & ASYNC_CHECK_CD) && (status & UART_MSR_DDCD)) {
-#if (defined(SERIAL_DEBUG_OPEN) || defined(SERIAL_DEBUG_INTR))
- printk("ttys%d CD now %s...", info->line,
- (status & UART_MSR_DCD) ? "on" : "off");
-#endif
- if (status & UART_MSR_DCD)
- wake_up_interruptible(&info->port.open_wait);
- else {
-#ifdef SERIAL_DEBUG_OPEN
- printk("scheduling hangup...");
-#endif
- tty_hangup(info->port.tty);
- }
- }
-}
-
-/*
- * This is the serial driver's interrupt routine
- */
-static irqreturn_t rs_interrupt_single(int irq, void *dev_id)
-{
- struct esp_struct *info;
- unsigned err_status;
- unsigned int scratch;
-
-#ifdef SERIAL_DEBUG_INTR
- printk("rs_interrupt_single(%d)...", irq);
-#endif
- info = (struct esp_struct *)dev_id;
- err_status = 0;
- scratch = serial_in(info, UART_ESI_SID);
-
- spin_lock(&info->lock);
-
- if (!info->port.tty) {
- spin_unlock(&info->lock);
- return IRQ_NONE;
- }
-
- if (scratch & 0x04) { /* error */
- serial_out(info, UART_ESI_CMD1, ESI_GET_ERR_STAT);
- err_status = serial_in(info, UART_ESI_STAT1);
- serial_in(info, UART_ESI_STAT2);
-
- if (err_status & 0x01)
- info->stat_flags |= ESP_STAT_RX_TIMEOUT;
-
- if (err_status & 0x20) /* UART status */
- check_modem_status(info);
-
- if (err_status & 0x80) /* Start break */
- wake_up_interruptible(&info->break_wait);
- }
-
- if ((scratch & 0x88) || /* DMA completed or timed out */
- (err_status & 0x1c) /* receive error */) {
- if (info->stat_flags & ESP_STAT_DMA_RX)
- receive_chars_dma_done(info, err_status);
- else if (info->stat_flags & ESP_STAT_DMA_TX)
- transmit_chars_dma_done(info);
- }
-
- if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) &&
- ((scratch & 0x01) || (info->stat_flags & ESP_STAT_RX_TIMEOUT)) &&
- (info->IER & UART_IER_RDI)) {
- int num_bytes;
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
- num_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- num_bytes |= serial_in(info, UART_ESI_STAT2);
-
- num_bytes = tty_buffer_request_room(info->port.tty, num_bytes);
-
- if (num_bytes) {
- if (dma_bytes ||
- (info->stat_flags & ESP_STAT_USE_PIO) ||
- (num_bytes <= info->config.pio_threshold))
- receive_chars_pio(info, num_bytes);
- else
- receive_chars_dma(info, num_bytes);
- }
- }
-
- if (!(info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) &&
- (scratch & 0x02) && (info->IER & UART_IER_THRI)) {
- if ((info->xmit_cnt <= 0) || info->port.tty->stopped) {
- info->IER &= ~UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- } else {
- int num_bytes;
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- num_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- num_bytes |= serial_in(info, UART_ESI_STAT2);
-
- if (num_bytes > info->xmit_cnt)
- num_bytes = info->xmit_cnt;
-
- if (num_bytes) {
- if (dma_bytes ||
- (info->stat_flags & ESP_STAT_USE_PIO) ||
- (num_bytes <= info->config.pio_threshold))
- transmit_chars_pio(info, num_bytes);
- else
- transmit_chars_dma(info, num_bytes);
- }
- }
- }
-
- info->last_active = jiffies;
-
-#ifdef SERIAL_DEBUG_INTR
- printk("end.\n");
-#endif
- spin_unlock(&info->lock);
- return IRQ_HANDLED;
-}
-
-/*
- * -------------------------------------------------------------------
- * Here ends the serial interrupt routines.
- * -------------------------------------------------------------------
- */
-
-/*
- * ---------------------------------------------------------------
- * Low level utility subroutines for the serial driver: routines to
- * figure out the appropriate timeout for an interrupt chain, routines
- * to initialize and startup a serial port, and routines to shutdown a
- * serial port. Useful stuff like that.
- *
- * Caller should hold lock
- * ---------------------------------------------------------------
- */
-
-static void esp_basic_init(struct esp_struct *info)
-{
- /* put ESPC in enhanced mode */
- serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0x01);
- else
- serial_out(info, UART_ESI_CMD2, 0x31);
-
- /* disable interrupts for now */
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- /* set interrupt and DMA channel */
- serial_out(info, UART_ESI_CMD1, ESI_SET_IRQ);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0x01);
- else
- serial_out(info, UART_ESI_CMD2, (dma << 4) | 0x01);
-
- serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
-
- if (info->line % 8) /* secondary port */
- serial_out(info, UART_ESI_CMD2, 0x0d); /* shared */
- else if (info->irq == 9)
- serial_out(info, UART_ESI_CMD2, 0x02);
- else
- serial_out(info, UART_ESI_CMD2, info->irq);
-
- /* set error status mask (check this) */
- serial_out(info, UART_ESI_CMD1, ESI_SET_ERR_MASK);
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- serial_out(info, UART_ESI_CMD2, 0xa1);
- else
- serial_out(info, UART_ESI_CMD2, 0xbd);
-
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- /* set DMA timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_DMA_TMOUT);
- serial_out(info, UART_ESI_CMD2, 0xff);
-
- /* set FIFO trigger levels */
- serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2, info->config.rx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.rx_trigger);
- serial_out(info, UART_ESI_CMD2, info->config.tx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.tx_trigger);
-
- /* Set clock scaling and wait states */
- serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR);
- serial_out(info, UART_ESI_CMD2, 0x04 | ESPC_SCALE);
-
- /* set reinterrupt pacing */
- serial_out(info, UART_ESI_CMD1, ESI_SET_REINTR);
- serial_out(info, UART_ESI_CMD2, 0xff);
-}
-
-static int startup(struct esp_struct *info)
-{
- unsigned long flags;
- int retval = 0;
- unsigned int num_chars;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->port.flags & ASYNC_INITIALIZED)
- goto out;
-
- if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_ATOMIC);
- retval = -ENOMEM;
- if (!info->xmit_buf)
- goto out;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "starting up ttys%d (irq %d)...",
- info->line, info->irq);
-#endif
-
- /* Flush the RX buffer. Using the ESI flush command may cause */
- /* wild interrupts, so read all the data instead. */
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
- num_chars = serial_in(info, UART_ESI_STAT1) << 8;
- num_chars |= serial_in(info, UART_ESI_STAT2);
-
- while (num_chars > 1) {
- inw(info->io_port + UART_ESI_RX);
- num_chars -= 2;
- }
-
- if (num_chars)
- serial_in(info, UART_ESI_RX);
-
- /* set receive character timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, info->config.rx_timeout);
-
- /* clear all flags except the "never DMA" flag */
- info->stat_flags &= ESP_STAT_NEVER_DMA;
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- info->stat_flags |= ESP_STAT_USE_PIO;
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Allocate the IRQ
- */
-
- retval = request_irq(info->irq, rs_interrupt_single, IRQF_SHARED,
- "esp serial", info);
-
- if (retval) {
- if (capable(CAP_SYS_ADMIN)) {
- if (info->port.tty)
- set_bit(TTY_IO_ERROR,
- &info->port.tty->flags);
- retval = 0;
- }
- goto out_unlocked;
- }
-
- if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) {
- dma_buffer = (char *)__get_dma_pages(
- GFP_KERNEL, get_order(DMA_BUFFER_SZ));
-
- /* use PIO mode if DMA buf/chan cannot be allocated */
- if (!dma_buffer)
- info->stat_flags |= ESP_STAT_USE_PIO;
- else if (request_dma(dma, "esp serial")) {
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
- dma_buffer = NULL;
- info->stat_flags |= ESP_STAT_USE_PIO;
- }
-
- }
-
- info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- /*
- * Finally, enable interrupts
- */
- /* info->IER = UART_IER_MSI | UART_IER_RLSI | UART_IER_RDI; */
- info->IER = UART_IER_RLSI | UART_IER_RDI | UART_IER_DMA_TMOUT |
- UART_IER_DMA_TC;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
-
- if (info->port.tty)
- clear_bit(TTY_IO_ERROR, &info->port.tty->flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Set up the tty->alt_speed kludge
- */
- if (info->port.tty) {
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- info->port.tty->alt_speed = 57600;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- info->port.tty->alt_speed = 115200;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- info->port.tty->alt_speed = 230400;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- info->port.tty->alt_speed = 460800;
- }
-
- /*
- * set the speed of the serial port
- */
- change_speed(info);
- info->port.flags |= ASYNC_INITIALIZED;
- return 0;
-
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-out_unlocked:
- return retval;
-}
-
-/*
- * This routine will shutdown a serial port; interrupts are disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
- */
-static void shutdown(struct esp_struct *info)
-{
- unsigned long flags, f;
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
- return;
-
-#ifdef SERIAL_DEBUG_OPEN
- printk("Shutting down serial port %d (irq %d)....", info->line,
- info->irq);
-#endif
-
- spin_lock_irqsave(&info->lock, flags);
- /*
- * clear delta_msr_wait queue to avoid mem leaks: we may free the irq
- * here so the queue might never be waken up
- */
- wake_up_interruptible(&info->port.delta_msr_wait);
- wake_up_interruptible(&info->break_wait);
-
- /* stop a DMA transfer on the port being closed */
- /* DMA lock is higher priority always */
- if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) {
- f = claim_dma_lock();
- disable_dma(dma);
- clear_dma_ff(dma);
- release_dma_lock(f);
-
- dma_bytes = 0;
- }
-
- /*
- * Free the IRQ
- */
- free_irq(info->irq, info);
-
- if (dma_buffer) {
- struct esp_struct *current_port = ports;
-
- while (current_port) {
- if ((current_port != info) &&
- (current_port->port.flags & ASYNC_INITIALIZED))
- break;
-
- current_port = current_port->next_port;
- }
-
- if (!current_port) {
- free_dma(dma);
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
- dma_buffer = NULL;
- }
- }
-
- if (info->xmit_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = NULL;
- }
-
- info->IER = 0;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- if (!info->port.tty || (info->port.tty->termios->c_cflag & HUPCL))
- info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
-
- info->MCR &= ~UART_MCR_OUT2;
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- if (info->port.tty)
- set_bit(TTY_IO_ERROR, &info->port.tty->flags);
-
- info->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * This routine is called to set the UART divisor registers to match
- * the specified baud rate for a serial port.
- */
-static void change_speed(struct esp_struct *info)
-{
- unsigned short port;
- int quot = 0;
- unsigned cflag, cval;
- int baud, bits;
- unsigned char flow1 = 0, flow2 = 0;
- unsigned long flags;
-
- if (!info->port.tty || !info->port.tty->termios)
- return;
- cflag = info->port.tty->termios->c_cflag;
- port = info->io_port;
-
- /* byte size and parity */
- switch (cflag & CSIZE) {
- case CS5: cval = 0x00; bits = 7; break;
- case CS6: cval = 0x01; bits = 8; break;
- case CS7: cval = 0x02; bits = 9; break;
- case CS8: cval = 0x03; bits = 10; break;
- default: cval = 0x00; bits = 7; break;
- }
- if (cflag & CSTOPB) {
- cval |= 0x04;
- bits++;
- }
- if (cflag & PARENB) {
- cval |= UART_LCR_PARITY;
- bits++;
- }
- if (!(cflag & PARODD))
- cval |= UART_LCR_EPAR;
-#ifdef CMSPAR
- if (cflag & CMSPAR)
- cval |= UART_LCR_SPAR;
-#endif
- baud = tty_get_baud_rate(info->port.tty);
- if (baud == 38400 &&
- ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST))
- quot = info->custom_divisor;
- else {
- if (baud == 134) /* Special case since 134 is really 134.5 */
- quot = (2*BASE_BAUD / 269);
- else if (baud)
- quot = BASE_BAUD / baud;
- }
- /* If the quotient is ever zero, default to 9600 bps */
- if (!quot)
- quot = BASE_BAUD / 9600;
-
- if (baud) {
- /* Actual rate */
- baud = BASE_BAUD/quot;
- tty_encode_baud_rate(info->port.tty, baud, baud);
- }
- info->timeout = ((1024 * HZ * bits * quot) / BASE_BAUD) + (HZ / 50);
-
- /* CTS flow control flag and modem status interrupts */
- /* info->IER &= ~UART_IER_MSI; */
- if (cflag & CRTSCTS) {
- info->port.flags |= ASYNC_CTS_FLOW;
- /* info->IER |= UART_IER_MSI; */
- flow1 = 0x04;
- flow2 = 0x10;
- } else
- info->port.flags &= ~ASYNC_CTS_FLOW;
- if (cflag & CLOCAL)
- info->port.flags &= ~ASYNC_CHECK_CD;
- else
- info->port.flags |= ASYNC_CHECK_CD;
-
- /*
- * Set up parity check flag
- */
- info->read_status_mask = UART_LSR_OE | UART_LSR_THRE | UART_LSR_DR;
- if (I_INPCK(info->port.tty))
- info->read_status_mask |= UART_LSR_FE | UART_LSR_PE;
- if (I_BRKINT(info->port.tty) || I_PARMRK(info->port.tty))
- info->read_status_mask |= UART_LSR_BI;
-
- info->ignore_status_mask = 0;
-#if 0
- /* This should be safe, but for some broken bits of hardware... */
- if (I_IGNPAR(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_PE | UART_LSR_FE;
- }
-#endif
- if (I_IGNBRK(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_BI;
- info->read_status_mask |= UART_LSR_BI;
- /*
- * If we're ignore parity and break indicators, ignore
- * overruns too. (For real raw support).
- */
- if (I_IGNPAR(info->port.tty)) {
- info->ignore_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- info->read_status_mask |= UART_LSR_OE | \
- UART_LSR_PE | UART_LSR_FE;
- }
- }
-
- if (I_IXOFF(info->port.tty))
- flow1 |= 0x81;
-
- spin_lock_irqsave(&info->lock, flags);
- /* set baud */
- serial_out(info, UART_ESI_CMD1, ESI_SET_BAUD);
- serial_out(info, UART_ESI_CMD2, quot >> 8);
- serial_out(info, UART_ESI_CMD2, quot & 0xff);
-
- /* set data bits, parity, etc. */
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_LCR);
- serial_out(info, UART_ESI_CMD2, cval);
-
- /* Enable flow control */
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CNTL);
- serial_out(info, UART_ESI_CMD2, flow1);
- serial_out(info, UART_ESI_CMD2, flow2);
-
- /* set flow control characters (XON/XOFF only) */
- if (I_IXOFF(info->port.tty)) {
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_CHARS);
- serial_out(info, UART_ESI_CMD2, START_CHAR(info->port.tty));
- serial_out(info, UART_ESI_CMD2, STOP_CHAR(info->port.tty));
- serial_out(info, UART_ESI_CMD2, 0x10);
- serial_out(info, UART_ESI_CMD2, 0x21);
- switch (cflag & CSIZE) {
- case CS5:
- serial_out(info, UART_ESI_CMD2, 0x1f);
- break;
- case CS6:
- serial_out(info, UART_ESI_CMD2, 0x3f);
- break;
- case CS7:
- case CS8:
- serial_out(info, UART_ESI_CMD2, 0x7f);
- break;
- default:
- serial_out(info, UART_ESI_CMD2, 0xff);
- break;
- }
- }
-
- /* Set high/low water */
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
- serial_out(info, UART_ESI_CMD2, info->config.flow_off >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.flow_off);
- serial_out(info, UART_ESI_CMD2, info->config.flow_on >> 8);
- serial_out(info, UART_ESI_CMD2, info->config.flow_on);
-
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static int rs_put_char(struct tty_struct *tty, unsigned char ch)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
- int ret = 0;
-
- if (serial_paranoia_check(info, tty->name, "rs_put_char"))
- return 0;
-
- if (!info->xmit_buf)
- return 0;
-
- spin_lock_irqsave(&info->lock, flags);
- if (info->xmit_cnt < ESP_XMIT_SIZE - 1) {
- info->xmit_buf[info->xmit_head++] = ch;
- info->xmit_head &= ESP_XMIT_SIZE-1;
- info->xmit_cnt++;
- ret = 1;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static void rs_flush_chars(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_flush_chars"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->xmit_cnt <= 0 || tty->stopped || !info->xmit_buf)
- goto out;
-
- if (!(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static int rs_write(struct tty_struct *tty,
- const unsigned char *buf, int count)
-{
- int c, t, ret = 0;
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_write"))
- return 0;
-
- if (!info->xmit_buf)
- return 0;
-
- while (1) {
- /* Thanks to R. Wolff for suggesting how to do this with */
- /* interrupts enabled */
-
- c = count;
- t = ESP_XMIT_SIZE - info->xmit_cnt - 1;
-
- if (t < c)
- c = t;
-
- t = ESP_XMIT_SIZE - info->xmit_head;
-
- if (t < c)
- c = t;
-
- if (c <= 0)
- break;
-
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
-
- info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1);
- info->xmit_cnt += c;
- buf += c;
- count -= c;
- ret += c;
- }
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->xmit_cnt && !tty->stopped && !(info->IER & UART_IER_THRI)) {
- info->IER |= UART_IER_THRI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static int rs_write_room(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- int ret;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_write_room"))
- return 0;
-
- spin_lock_irqsave(&info->lock, flags);
-
- ret = ESP_XMIT_SIZE - info->xmit_cnt - 1;
- if (ret < 0)
- ret = 0;
- spin_unlock_irqrestore(&info->lock, flags);
- return ret;
-}
-
-static int rs_chars_in_buffer(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer"))
- return 0;
- return info->xmit_cnt;
-}
-
-static void rs_flush_buffer(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
- return;
- spin_lock_irqsave(&info->lock, flags);
- info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- spin_unlock_irqrestore(&info->lock, flags);
- tty_wakeup(tty);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_throttle()
- *
- * This routine is called by the upper-layer tty layer to signal that
- * incoming characters should be throttled.
- * ------------------------------------------------------------
- */
-static void rs_throttle(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk("throttle %s: %d....\n", tty_name(tty, buf),
- tty_chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "rs_throttle"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- info->IER &= ~UART_IER_RDI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x00);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_unthrottle(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-#ifdef SERIAL_DEBUG_THROTTLE
- char buf[64];
-
- printk(KERN_DEBUG "unthrottle %s: %d....\n", tty_name(tty, buf),
- tty_chars_in_buffer(tty));
-#endif
-
- if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- info->IER |= UART_IER_RDI;
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, info->config.rx_timeout);
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-/*
- * ------------------------------------------------------------
- * rs_ioctl() and friends
- * ------------------------------------------------------------
- */
-
-static int get_serial_info(struct esp_struct *info,
- struct serial_struct __user *retinfo)
-{
- struct serial_struct tmp;
-
- lock_kernel();
- memset(&tmp, 0, sizeof(tmp));
- tmp.type = PORT_16550A;
- tmp.line = info->line;
- tmp.port = info->io_port;
- tmp.irq = info->irq;
- tmp.flags = info->port.flags;
- tmp.xmit_fifo_size = 1024;
- tmp.baud_base = BASE_BAUD;
- tmp.close_delay = info->close_delay;
- tmp.closing_wait = info->closing_wait;
- tmp.custom_divisor = info->custom_divisor;
- tmp.hub6 = 0;
- unlock_kernel();
- if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
- return -EFAULT;
- return 0;
-}
-
-static int get_esp_config(struct esp_struct *info,
- struct hayes_esp_config __user *retinfo)
-{
- struct hayes_esp_config tmp;
-
- if (!retinfo)
- return -EFAULT;
-
- memset(&tmp, 0, sizeof(tmp));
- lock_kernel();
- tmp.rx_timeout = info->config.rx_timeout;
- tmp.rx_trigger = info->config.rx_trigger;
- tmp.tx_trigger = info->config.tx_trigger;
- tmp.flow_off = info->config.flow_off;
- tmp.flow_on = info->config.flow_on;
- tmp.pio_threshold = info->config.pio_threshold;
- tmp.dma_channel = (info->stat_flags & ESP_STAT_NEVER_DMA ? 0 : dma);
- unlock_kernel();
-
- return copy_to_user(retinfo, &tmp, sizeof(*retinfo)) ? -EFAULT : 0;
-}
-
-static int set_serial_info(struct esp_struct *info,
- struct serial_struct __user *new_info)
-{
- struct serial_struct new_serial;
- struct esp_struct old_info;
- unsigned int change_irq;
- int retval = 0;
- struct esp_struct *current_async;
-
- if (copy_from_user(&new_serial, new_info, sizeof(new_serial)))
- return -EFAULT;
- old_info = *info;
-
- if ((new_serial.type != PORT_16550A) ||
- (new_serial.hub6) ||
- (info->io_port != new_serial.port) ||
- (new_serial.baud_base != BASE_BAUD) ||
- (new_serial.irq > 15) ||
- (new_serial.irq < 2) ||
- (new_serial.irq == 6) ||
- (new_serial.irq == 8) ||
- (new_serial.irq == 13))
- return -EINVAL;
-
- change_irq = new_serial.irq != info->irq;
-
- if (change_irq && (info->line % 8))
- return -EINVAL;
-
- if (!capable(CAP_SYS_ADMIN)) {
- if (change_irq ||
- (new_serial.close_delay != info->close_delay) ||
- ((new_serial.flags & ~ASYNC_USR_MASK) !=
- (info->port.flags & ~ASYNC_USR_MASK)))
- return -EPERM;
- info->port.flags = ((info->port.flags & ~ASYNC_USR_MASK) |
- (new_serial.flags & ASYNC_USR_MASK));
- info->custom_divisor = new_serial.custom_divisor;
- } else {
- if (new_serial.irq == 2)
- new_serial.irq = 9;
-
- if (change_irq) {
- current_async = ports;
-
- while (current_async) {
- if ((current_async->line >= info->line) &&
- (current_async->line < (info->line + 8))) {
- if (current_async == info) {
- if (current_async->port.count > 1)
- return -EBUSY;
- } else if (current_async->port.count)
- return -EBUSY;
- }
-
- current_async = current_async->next_port;
- }
- }
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
- (new_serial.flags & ASYNC_FLAGS));
- info->custom_divisor = new_serial.custom_divisor;
- info->close_delay = new_serial.close_delay * HZ/100;
- info->closing_wait = new_serial.closing_wait * HZ/100;
-
- if (change_irq) {
- /*
- * We need to shutdown the serial port at the old
- * port/irq combination.
- */
- shutdown(info);
-
- current_async = ports;
-
- while (current_async) {
- if ((current_async->line >= info->line) &&
- (current_async->line < (info->line + 8)))
- current_async->irq = new_serial.irq;
-
- current_async = current_async->next_port;
- }
-
- serial_out(info, UART_ESI_CMD1, ESI_SET_ENH_IRQ);
- if (info->irq == 9)
- serial_out(info, UART_ESI_CMD2, 0x02);
- else
- serial_out(info, UART_ESI_CMD2, info->irq);
- }
- }
-
- if (info->port.flags & ASYNC_INITIALIZED) {
- if (((old_info.port.flags & ASYNC_SPD_MASK) !=
- (info->port.flags & ASYNC_SPD_MASK)) ||
- (old_info.custom_divisor != info->custom_divisor)) {
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
- info->port.tty->alt_speed = 57600;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
- info->port.tty->alt_speed = 115200;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
- info->port.tty->alt_speed = 230400;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
- info->port.tty->alt_speed = 460800;
- change_speed(info);
- }
- } else
- retval = startup(info);
-
- return retval;
-}
-
-static int set_esp_config(struct esp_struct *info,
- struct hayes_esp_config __user *new_info)
-{
- struct hayes_esp_config new_config;
- unsigned int change_dma;
- int retval = 0;
- struct esp_struct *current_async;
- unsigned long flags;
-
- /* Perhaps a non-sysadmin user should be able to do some of these */
- /* operations. I haven't decided yet. */
-
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
-
- if (copy_from_user(&new_config, new_info, sizeof(new_config)))
- return -EFAULT;
-
- if ((new_config.flow_on >= new_config.flow_off) ||
- (new_config.rx_trigger < 1) ||
- (new_config.tx_trigger < 1) ||
- (new_config.flow_off < 1) ||
- (new_config.flow_on < 1) ||
- (new_config.rx_trigger > 1023) ||
- (new_config.tx_trigger > 1023) ||
- (new_config.flow_off > 1023) ||
- (new_config.flow_on > 1023) ||
- (new_config.pio_threshold < 0) ||
- (new_config.pio_threshold > 1024))
- return -EINVAL;
-
- if ((new_config.dma_channel != 1) && (new_config.dma_channel != 3))
- new_config.dma_channel = 0;
-
- if (info->stat_flags & ESP_STAT_NEVER_DMA)
- change_dma = new_config.dma_channel;
- else
- change_dma = (new_config.dma_channel != dma);
-
- if (change_dma) {
- if (new_config.dma_channel) {
- /* PIO mode to DMA mode transition OR */
- /* change current DMA channel */
- current_async = ports;
-
- while (current_async) {
- if (current_async == info) {
- if (current_async->port.count > 1)
- return -EBUSY;
- } else if (current_async->port.count)
- return -EBUSY;
-
- current_async = current_async->next_port;
- }
-
- shutdown(info);
- dma = new_config.dma_channel;
- info->stat_flags &= ~ESP_STAT_NEVER_DMA;
-
- /* all ports must use the same DMA channel */
-
- spin_lock_irqsave(&info->lock, flags);
- current_async = ports;
-
- while (current_async) {
- esp_basic_init(current_async);
- current_async = current_async->next_port;
- }
- spin_unlock_irqrestore(&info->lock, flags);
- } else {
- /* DMA mode to PIO mode only */
- if (info->port.count > 1)
- return -EBUSY;
-
- shutdown(info);
- spin_lock_irqsave(&info->lock, flags);
- info->stat_flags |= ESP_STAT_NEVER_DMA;
- esp_basic_init(info);
- spin_unlock_irqrestore(&info->lock, flags);
- }
- }
-
- info->config.pio_threshold = new_config.pio_threshold;
-
- if ((new_config.flow_off != info->config.flow_off) ||
- (new_config.flow_on != info->config.flow_on)) {
- info->config.flow_off = new_config.flow_off;
- info->config.flow_on = new_config.flow_on;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_FLOW_LVL);
- serial_out(info, UART_ESI_CMD2, new_config.flow_off >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.flow_off);
- serial_out(info, UART_ESI_CMD2, new_config.flow_on >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.flow_on);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if ((new_config.rx_trigger != info->config.rx_trigger) ||
- (new_config.tx_trigger != info->config.tx_trigger)) {
- info->config.rx_trigger = new_config.rx_trigger;
- info->config.tx_trigger = new_config.tx_trigger;
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2,
- new_config.rx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.rx_trigger);
- serial_out(info, UART_ESI_CMD2,
- new_config.tx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, new_config.tx_trigger);
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if (new_config.rx_timeout != info->config.rx_timeout) {
- info->config.rx_timeout = new_config.rx_timeout;
- spin_lock_irqsave(&info->lock, flags);
-
- if (info->IER & UART_IER_RDI) {
- serial_out(info, UART_ESI_CMD1,
- ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2,
- new_config.rx_timeout);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
- }
-
- if (!(info->port.flags & ASYNC_INITIALIZED))
- retval = startup(info);
-
- return retval;
-}
-
-/*
- * get_lsr_info - get line status register info
- *
- * Purpose: Let user call ioctl() to get info when the UART physically
- * is emptied. On bus types like RS485, the transmitter must
- * release the bus after transmitting. This must be done when
- * the transmit shift register is empty, not be done when the
- * transmit holding register is empty. This functionality
- * allows an RS485 driver to be written in user space.
- */
-static int get_lsr_info(struct esp_struct *info, unsigned int __user *value)
-{
- unsigned char status;
- unsigned int result;
- unsigned long flags;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT1);
- spin_unlock_irqrestore(&info->lock, flags);
- result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
- return put_user(result, value);
-}
-
-
-static int esp_tiocmget(struct tty_struct *tty, struct file *file)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned char control, status;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, __func__))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- control = info->MCR;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- status = serial_in(info, UART_ESI_STAT2);
- spin_unlock_irqrestore(&info->lock, flags);
-
- return ((control & UART_MCR_RTS) ? TIOCM_RTS : 0)
- | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)
- | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0)
- | ((status & UART_MSR_RI) ? TIOCM_RNG : 0)
- | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0)
- | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0);
-}
-
-static int esp_tiocmset(struct tty_struct *tty, struct file *file,
- unsigned int set, unsigned int clear)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, __func__))
- return -ENODEV;
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (set & TIOCM_RTS)
- info->MCR |= UART_MCR_RTS;
- if (set & TIOCM_DTR)
- info->MCR |= UART_MCR_DTR;
-
- if (clear & TIOCM_RTS)
- info->MCR &= ~UART_MCR_RTS;
- if (clear & TIOCM_DTR)
- info->MCR &= ~UART_MCR_DTR;
-
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
-
- spin_unlock_irqrestore(&info->lock, flags);
- return 0;
-}
-
-/*
- * rs_break() --- routine which turns the break handling on or off
- */
-static int esp_break(struct tty_struct *tty, int break_state)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "esp_break"))
- return -EINVAL;
-
- if (break_state == -1) {
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x01);
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* FIXME - new style wait needed here */
- interruptible_sleep_on(&info->break_wait);
- } else {
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_ISSUE_BREAK);
- serial_out(info, UART_ESI_CMD2, 0x00);
- spin_unlock_irqrestore(&info->lock, flags);
- }
- return 0;
-}
-
-static int rs_ioctl(struct tty_struct *tty, struct file *file,
- unsigned int cmd, unsigned long arg)
-{
- struct esp_struct *info = tty->driver_data;
- struct async_icount cprev, cnow; /* kernel counter temps */
- struct serial_icounter_struct __user *p_cuser; /* user space */
- void __user *argp = (void __user *)arg;
- unsigned long flags;
- int ret;
-
- if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
- return -ENODEV;
-
- if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
- (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
- (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT) &&
- (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT) &&
- (cmd != TIOCGHAYESESP) && (cmd != TIOCSHAYESESP)) {
- if (tty->flags & (1 << TTY_IO_ERROR))
- return -EIO;
- }
-
- switch (cmd) {
- case TIOCGSERIAL:
- return get_serial_info(info, argp);
- case TIOCSSERIAL:
- lock_kernel();
- ret = set_serial_info(info, argp);
- unlock_kernel();
- return ret;
- case TIOCSERGWILD:
- return put_user(0L, (unsigned long __user *)argp);
- case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, argp);
- case TIOCSERSWILD:
- if (!capable(CAP_SYS_ADMIN))
- return -EPERM;
- return 0;
- /*
- * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change
- * - mask passed in arg for lines of interest
- * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking)
- * Caller should use TIOCGICOUNT to see which one it was
- */
- case TIOCMIWAIT:
- spin_lock_irqsave(&info->lock, flags);
- cprev = info->icount; /* note the counters on entry */
- spin_unlock_irqrestore(&info->lock, flags);
- while (1) {
- /* FIXME: convert to new style wakeup */
- interruptible_sleep_on(&info->port.delta_msr_wait);
- /* see if a signal did it */
- if (signal_pending(current))
- return -ERESTARTSYS;
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount; /* atomic copy */
- spin_unlock_irqrestore(&info->lock, flags);
- if (cnow.rng == cprev.rng &&
- cnow.dsr == cprev.dsr &&
- cnow.dcd == cprev.dcd &&
- cnow.cts == cprev.cts)
- return -EIO; /* no change => error */
- if (((arg & TIOCM_RNG) &&
- (cnow.rng != cprev.rng)) ||
- ((arg & TIOCM_DSR) &&
- (cnow.dsr != cprev.dsr)) ||
- ((arg & TIOCM_CD) &&
- (cnow.dcd != cprev.dcd)) ||
- ((arg & TIOCM_CTS) &&
- (cnow.cts != cprev.cts))) {
- return 0;
- }
- cprev = cnow;
- }
- /* NOTREACHED */
- /*
- * Get counter of input serial line interrupts (DCD,RI,DSR,CTS)
- * Return: write counters to the user passed counter struct
- * NB: both 1->0 and 0->1 transitions are counted except for
- * RI where only 0->1 is counted.
- */
- case TIOCGICOUNT:
- spin_lock_irqsave(&info->lock, flags);
- cnow = info->icount;
- spin_unlock_irqrestore(&info->lock, flags);
- p_cuser = argp;
- if (put_user(cnow.cts, &p_cuser->cts) ||
- put_user(cnow.dsr, &p_cuser->dsr) ||
- put_user(cnow.rng, &p_cuser->rng) ||
- put_user(cnow.dcd, &p_cuser->dcd))
- return -EFAULT;
- return 0;
- case TIOCGHAYESESP:
- return get_esp_config(info, argp);
- case TIOCSHAYESESP:
- lock_kernel();
- ret = set_esp_config(info, argp);
- unlock_kernel();
- return ret;
- default:
- return -ENOIOCTLCMD;
- }
- return 0;
-}
-
-static void rs_set_termios(struct tty_struct *tty, struct ktermios *old_termios)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- change_speed(info);
-
- spin_lock_irqsave(&info->lock, flags);
-
- /* Handle transition to B0 status */
- if ((old_termios->c_cflag & CBAUD) &&
- !(tty->termios->c_cflag & CBAUD)) {
- info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
- }
-
- /* Handle transition away from B0 status */
- if (!(old_termios->c_cflag & CBAUD) &&
- (tty->termios->c_cflag & CBAUD)) {
- info->MCR |= (UART_MCR_DTR | UART_MCR_RTS);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, info->MCR);
- }
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /* Handle turning of CRTSCTS */
- if ((old_termios->c_cflag & CRTSCTS) &&
- !(tty->termios->c_cflag & CRTSCTS)) {
- rs_start(tty);
- }
-}
-
-/*
- * ------------------------------------------------------------
- * rs_close()
- *
- * This routine is called when the serial port gets closed. First, we
- * wait for the last remaining data to be sent. Then, we unlink its
- * async structure from the interrupt chain if necessary, and we free
- * that IRQ if nothing is left in the chain.
- * ------------------------------------------------------------
- */
-static void rs_close(struct tty_struct *tty, struct file *filp)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long flags;
-
- if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
-
- if (tty_hung_up_p(filp)) {
- DBG_CNT("before DEC-hung");
- goto out;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "rs_close ttys%d, count = %d\n",
- info->line, info->port.count);
-#endif
- if (tty->count == 1 && info->port.count != 1) {
- /*
- * Uh, oh. tty->count is 1, which means that the tty
- * structure will be freed. Info->count should always
- * be one in these conditions. If it's greater than
- * one, we've got real problems, since it means the
- * serial port won't be shutdown.
- */
- printk(KERN_DEBUG "rs_close: bad serial port count; tty->count is 1, info->port.count is %d\n", info->port.count);
- info->port.count = 1;
- }
- if (--info->port.count < 0) {
- printk(KERN_ERR "rs_close: bad serial port count for ttys%d: %d\n",
- info->line, info->port.count);
- info->port.count = 0;
- }
- if (info->port.count) {
- DBG_CNT("before DEC-2");
- goto out;
- }
- info->port.flags |= ASYNC_CLOSING;
-
- spin_unlock_irqrestore(&info->lock, flags);
- /*
- * Now we wait for the transmit buffer to clear; and we notify
- * the line discipline to only process XON/XOFF characters.
- */
- tty->closing = 1;
- if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, info->closing_wait);
- /*
- * At this point we stop accepting input. To do this, we
- * disable the receive line status interrupts, and tell the
- * interrupt driver to stop checking the data ready bit in the
- * line status register.
- */
- /* info->IER &= ~UART_IER_RLSI; */
- info->IER &= ~UART_IER_RDI;
- info->read_status_mask &= ~UART_LSR_DR;
- if (info->port.flags & ASYNC_INITIALIZED) {
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
- serial_out(info, UART_ESI_CMD2, info->IER);
-
- /* disable receive timeout */
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, 0x00);
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- rs_wait_until_sent(tty, info->timeout);
- }
- shutdown(info);
- rs_flush_buffer(tty);
- tty_ldisc_flush(tty);
- tty->closing = 0;
- info->port.tty = NULL;
-
- if (info->port.blocked_open) {
- if (info->close_delay)
- msleep_interruptible(jiffies_to_msecs(info->close_delay));
- wake_up_interruptible(&info->port.open_wait);
- }
- info->port.flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
- wake_up_interruptible(&info->port.close_wait);
- return;
-
-out:
- spin_unlock_irqrestore(&info->lock, flags);
-}
-
-static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
-{
- struct esp_struct *info = tty->driver_data;
- unsigned long orig_jiffies, char_time;
- unsigned long flags;
-
- if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent"))
- return;
-
- orig_jiffies = jiffies;
- char_time = ((info->timeout - HZ / 50) / 1024) / 5;
-
- if (!char_time)
- char_time = 1;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
-
- while ((serial_in(info, UART_ESI_STAT1) != 0x03) ||
- (serial_in(info, UART_ESI_STAT2) != 0xff)) {
-
- spin_unlock_irqrestore(&info->lock, flags);
- msleep_interruptible(jiffies_to_msecs(char_time));
-
- if (signal_pending(current))
- return;
-
- if (timeout && time_after(jiffies, orig_jiffies + timeout))
- return;
-
- spin_lock_irqsave(&info->lock, flags);
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- }
- spin_unlock_irqrestore(&info->lock, flags);
- set_current_state(TASK_RUNNING);
-}
-
-/*
- * esp_hangup() --- called by tty_hangup() when a hangup is signaled.
- */
-static void esp_hangup(struct tty_struct *tty)
-{
- struct esp_struct *info = tty->driver_data;
-
- if (serial_paranoia_check(info, tty->name, "esp_hangup"))
- return;
-
- rs_flush_buffer(tty);
- shutdown(info);
- info->port.count = 0;
- info->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- info->port.tty = NULL;
- wake_up_interruptible(&info->port.open_wait);
-}
-
-static int esp_carrier_raised(struct tty_port *port)
-{
- struct esp_struct *info = container_of(port, struct esp_struct, port);
- serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
- if (serial_in(info, UART_ESI_STAT2) & UART_MSR_DCD)
- return 1;
- return 0;
-}
-
-/*
- * ------------------------------------------------------------
- * esp_open() and friends
- * ------------------------------------------------------------
- */
-static int block_til_ready(struct tty_struct *tty, struct file *filp,
- struct esp_struct *info)
-{
- DECLARE_WAITQUEUE(wait, current);
- int retval;
- int do_clocal = 0;
- unsigned long flags;
- int cd;
- struct tty_port *port = &info->port;
-
- /*
- * If the device is in the middle of being closed, then block
- * until it's done, and then try again.
- */
- if (tty_hung_up_p(filp) ||
- (port->flags & ASYNC_CLOSING)) {
- if (port->flags & ASYNC_CLOSING)
- interruptible_sleep_on(&port->close_wait);
-#ifdef SERIAL_DO_RESTART
- if (port->flags & ASYNC_HUP_NOTIFY)
- return -EAGAIN;
- else
- return -ERESTARTSYS;
-#else
- return -EAGAIN;
-#endif
- }
-
- /*
- * If non-blocking mode is set, or the port is not enabled,
- * then make the check up front and then exit.
- */
- if ((filp->f_flags & O_NONBLOCK) ||
- (tty->flags & (1 << TTY_IO_ERROR))) {
- port->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
- }
-
- if (tty->termios->c_cflag & CLOCAL)
- do_clocal = 1;
-
- /*
- * Block waiting for the carrier detect and the line to become
- * free (i.e., not in use by the callout). While we are in
- * this loop, port->count is dropped by one, so that
- * rs_close() knows when to free things. We restore it upon
- * exit, either normal or abnormal.
- */
- retval = 0;
- add_wait_queue(&port->open_wait, &wait);
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready before block: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- spin_lock_irqsave(&info->lock, flags);
- if (!tty_hung_up_p(filp))
- port->count--;
- port->blocked_open++;
- while (1) {
- if ((tty->termios->c_cflag & CBAUD)) {
- unsigned int scratch;
-
- serial_out(info, UART_ESI_CMD1, ESI_READ_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- scratch = serial_in(info, UART_ESI_STAT1);
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2,
- scratch | UART_MCR_DTR | UART_MCR_RTS);
- }
- set_current_state(TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp) ||
- !(port->flags & ASYNC_INITIALIZED)) {
-#ifdef SERIAL_DO_RESTART
- if (port->flags & ASYNC_HUP_NOTIFY)
- retval = -EAGAIN;
- else
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
-
- cd = tty_port_carrier_raised(port);
-
- if (!(port->flags & ASYNC_CLOSING) &&
- (do_clocal))
- break;
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready blocking: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- spin_unlock_irqrestore(&info->lock, flags);
- schedule();
- spin_lock_irqsave(&info->lock, flags);
- }
- set_current_state(TASK_RUNNING);
- remove_wait_queue(&port->open_wait, &wait);
- if (!tty_hung_up_p(filp))
- port->count++;
- port->blocked_open--;
- spin_unlock_irqrestore(&info->lock, flags);
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "block_til_ready after blocking: ttys%d, count = %d\n",
- info->line, port->count);
-#endif
- if (retval)
- return retval;
- port->flags |= ASYNC_NORMAL_ACTIVE;
- return 0;
-}
-
-/*
- * This routine is called whenever a serial port is opened. It
- * enables interrupts for a serial port, linking in its async structure into
- * the IRQ chain. It also performs the serial-specific
- * initialization for the tty structure.
- */
-static int esp_open(struct tty_struct *tty, struct file *filp)
-{
- struct esp_struct *info;
- int retval, line;
- unsigned long flags;
-
- line = tty->index;
- if ((line < 0) || (line >= NR_PORTS))
- return -ENODEV;
-
- /* find the port in the chain */
-
- info = ports;
-
- while (info && (info->line != line))
- info = info->next_port;
-
- if (!info) {
- serial_paranoia_check(info, tty->name, "esp_open");
- return -ENODEV;
- }
-
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open %s, count = %d\n", tty->name, info->port.count);
-#endif
- spin_lock_irqsave(&info->lock, flags);
- info->port.count++;
- tty->driver_data = info;
- info->port.tty = tty;
-
- spin_unlock_irqrestore(&info->lock, flags);
-
- /*
- * Start up serial port
- */
- retval = startup(info);
- if (retval)
- return retval;
-
- retval = block_til_ready(tty, filp, info);
- if (retval) {
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open returning after block_til_ready with %d\n",
- retval);
-#endif
- return retval;
- }
-#ifdef SERIAL_DEBUG_OPEN
- printk(KERN_DEBUG "esp_open %s successful...", tty->name);
-#endif
- return 0;
-}
-
-/*
- * ---------------------------------------------------------------------
- * espserial_init() and friends
- *
- * espserial_init() is called at boot-time to initialize the serial driver.
- * ---------------------------------------------------------------------
- */
-
-/*
- * This routine prints out the appropriate serial driver version
- * number, and identifies which options were configured into this
- * driver.
- */
-
-static void __init show_serial_version(void)
-{
- printk(KERN_INFO "%s version %s (DMA %u)\n",
- serial_name, serial_version, dma);
-}
-
-/*
- * This routine is called by espserial_init() to initialize a specific serial
- * port.
- */
-static int autoconfig(struct esp_struct *info)
-{
- int port_detected = 0;
- unsigned long flags;
-
- if (!request_region(info->io_port, REGION_SIZE, "esp serial"))
- return -EIO;
-
- spin_lock_irqsave(&info->lock, flags);
- /*
- * Check for ESP card
- */
-
- if (serial_in(info, UART_ESI_BASE) == 0xf3) {
- serial_out(info, UART_ESI_CMD1, 0x00);
- serial_out(info, UART_ESI_CMD1, 0x01);
-
- if ((serial_in(info, UART_ESI_STAT2) & 0x70) == 0x20) {
- port_detected = 1;
-
- if (!(info->irq)) {
- serial_out(info, UART_ESI_CMD1, 0x02);
-
- if (serial_in(info, UART_ESI_STAT1) & 0x01)
- info->irq = 3;
- else
- info->irq = 4;
- }
-
-
- /* put card in enhanced mode */
- /* this prevents access through */
- /* the "old" IO ports */
- esp_basic_init(info);
-
- /* clear out MCR */
- serial_out(info, UART_ESI_CMD1, ESI_WRITE_UART);
- serial_out(info, UART_ESI_CMD2, UART_MCR);
- serial_out(info, UART_ESI_CMD2, 0x00);
- }
- }
- if (!port_detected)
- release_region(info->io_port, REGION_SIZE);
-
- spin_unlock_irqrestore(&info->lock, flags);
- return (port_detected);
-}
-
-static const struct tty_operations esp_ops = {
- .open = esp_open,
- .close = rs_close,
- .write = rs_write,
- .put_char = rs_put_char,
- .flush_chars = rs_flush_chars,
- .write_room = rs_write_room,
- .chars_in_buffer = rs_chars_in_buffer,
- .flush_buffer = rs_flush_buffer,
- .ioctl = rs_ioctl,
- .throttle = rs_throttle,
- .unthrottle = rs_unthrottle,
- .set_termios = rs_set_termios,
- .stop = rs_stop,
- .start = rs_start,
- .hangup = esp_hangup,
- .break_ctl = esp_break,
- .wait_until_sent = rs_wait_until_sent,
- .tiocmget = esp_tiocmget,
- .tiocmset = esp_tiocmset,
-};
-
-static const struct tty_port_operations esp_port_ops = {
- .esp_carrier_raised,
-};
-
-/*
- * The serial driver boot-time initialization code!
- */
-static int __init espserial_init(void)
-{
- int i, offset;
- struct esp_struct *info;
- struct esp_struct *last_primary = NULL;
- int esp[] = { 0x100, 0x140, 0x180, 0x200, 0x240, 0x280, 0x300, 0x380 };
-
- esp_driver = alloc_tty_driver(NR_PORTS);
- if (!esp_driver)
- return -ENOMEM;
-
- for (i = 0; i < NR_PRIMARY; i++) {
- if (irq[i] != 0) {
- if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) ||
- (irq[i] == 8) || (irq[i] == 13))
- irq[i] = 0;
- else if (irq[i] == 2)
- irq[i] = 9;
- }
- }
-
- if ((dma != 1) && (dma != 3))
- dma = 0;
-
- if ((rx_trigger < 1) || (rx_trigger > 1023))
- rx_trigger = 768;
-
- if ((tx_trigger < 1) || (tx_trigger > 1023))
- tx_trigger = 768;
-
- if ((flow_off < 1) || (flow_off > 1023))
- flow_off = 1016;
-
- if ((flow_on < 1) || (flow_on > 1023))
- flow_on = 944;
-
- if ((rx_timeout < 0) || (rx_timeout > 255))
- rx_timeout = 128;
-
- if (flow_on >= flow_off)
- flow_on = flow_off - 1;
-
- show_serial_version();
-
- /* Initialize the tty_driver structure */
-
- esp_driver->owner = THIS_MODULE;
- esp_driver->name = "ttyP";
- esp_driver->major = ESP_IN_MAJOR;
- esp_driver->minor_start = 0;
- esp_driver->type = TTY_DRIVER_TYPE_SERIAL;
- esp_driver->subtype = SERIAL_TYPE_NORMAL;
- esp_driver->init_termios = tty_std_termios;
- esp_driver->init_termios.c_cflag =
- B9600 | CS8 | CREAD | HUPCL | CLOCAL;
- esp_driver->init_termios.c_ispeed = 9600;
- esp_driver->init_termios.c_ospeed = 9600;
- esp_driver->flags = TTY_DRIVER_REAL_RAW;
- tty_set_operations(esp_driver, &esp_ops);
- if (tty_register_driver(esp_driver)) {
- printk(KERN_ERR "Couldn't register esp serial driver");
- put_tty_driver(esp_driver);
- return 1;
- }
-
- info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL);
-
- if (!info) {
- printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n");
- tty_unregister_driver(esp_driver);
- put_tty_driver(esp_driver);
- return 1;
- }
-
- spin_lock_init(&info->lock);
- /* rx_trigger, tx_trigger are needed by autoconfig */
- info->config.rx_trigger = rx_trigger;
- info->config.tx_trigger = tx_trigger;
-
- i = 0;
- offset = 0;
-
- do {
- tty_port_init(&info->port);
- info->port.ops = &esp_port_ops;
- info->io_port = esp[i] + offset;
- info->irq = irq[i];
- info->line = (i * 8) + (offset / 8);
-
- if (!autoconfig(info)) {
- i++;
- offset = 0;
- continue;
- }
-
- info->custom_divisor = (divisor[i] >> (offset / 2)) & 0xf;
- info->port.flags = STD_COM_FLAGS;
- if (info->custom_divisor)
- info->port.flags |= ASYNC_SPD_CUST;
- info->magic = ESP_MAGIC;
- info->close_delay = 5*HZ/10;
- info->closing_wait = 30*HZ;
- info->config.rx_timeout = rx_timeout;
- info->config.flow_on = flow_on;
- info->config.flow_off = flow_off;
- info->config.pio_threshold = pio_threshold;
- info->next_port = ports;
- init_waitqueue_head(&info->break_wait);
- ports = info;
- printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ",
- info->line, info->io_port, info->irq);
-
- if (info->line % 8) {
- printk("secondary port\n");
- /* 8 port cards can't do DMA */
- info->stat_flags |= ESP_STAT_NEVER_DMA;
-
- if (last_primary)
- last_primary->stat_flags |= ESP_STAT_NEVER_DMA;
- } else {
- printk("primary port\n");
- last_primary = info;
- irq[i] = info->irq;
- }
-
- if (!dma)
- info->stat_flags |= ESP_STAT_NEVER_DMA;
-
- info = kzalloc(sizeof(struct esp_struct), GFP_KERNEL);
- if (!info) {
- printk(KERN_ERR "Couldn't allocate memory for esp serial device information\n");
- /* allow use of the already detected ports */
- return 0;
- }
-
- spin_lock_init(&info->lock);
- /* rx_trigger, tx_trigger are needed by autoconfig */
- info->config.rx_trigger = rx_trigger;
- info->config.tx_trigger = tx_trigger;
-
- if (offset == 56) {
- i++;
- offset = 0;
- } else {
- offset += 8;
- }
- } while (i < NR_PRIMARY);
-
- /* free the last port memory allocation */
- kfree(info);
-
- return 0;
-}
-
-static void __exit espserial_exit(void)
-{
- int e1;
- struct esp_struct *temp_async;
- struct esp_pio_buffer *pio_buf;
-
- e1 = tty_unregister_driver(esp_driver);
- if (e1)
- printk(KERN_ERR "esp: failed to unregister driver (%d)\n", e1);
- put_tty_driver(esp_driver);
-
- while (ports) {
- if (ports->io_port)
- release_region(ports->io_port, REGION_SIZE);
- temp_async = ports->next_port;
- kfree(ports);
- ports = temp_async;
- }
-
- if (dma_buffer)
- free_pages((unsigned long)dma_buffer,
- get_order(DMA_BUFFER_SZ));
-
- while (free_pio_buf) {
- pio_buf = free_pio_buf->next;
- kfree(free_pio_buf);
- free_pio_buf = pio_buf;
- }
-}
-
-module_init(espserial_init);
-module_exit(espserial_exit);
diff --git a/drivers/char/isicom.c b/drivers/char/isicom.c
index 426bfdd7f3e..300d5bd6cd0 100644
--- a/drivers/char/isicom.c
+++ b/drivers/char/isicom.c
@@ -793,35 +793,30 @@ static inline void isicom_setup_board(struct isi_board *bp)
{
int channel;
struct isi_port *port;
- unsigned long flags;
- spin_lock_irqsave(&bp->card_lock, flags);
- if (bp->status & BOARD_ACTIVE) {
- spin_unlock_irqrestore(&bp->card_lock, flags);
- return;
+ bp->count++;
+ if (!(bp->status & BOARD_INIT)) {
+ port = bp->ports;
+ for (channel = 0; channel < bp->port_count; channel++, port++)
+ drop_dtr_rts(port);
}
- port = bp->ports;
- bp->status |= BOARD_ACTIVE;
- for (channel = 0; channel < bp->port_count; channel++, port++)
- drop_dtr_rts(port);
- spin_unlock_irqrestore(&bp->card_lock, flags);
+ bp->status |= BOARD_ACTIVE | BOARD_INIT;
}
-static int isicom_setup_port(struct tty_struct *tty)
+/* Activate and thus setup board are protected from races against shutdown
+ by the tty_port mutex */
+
+static int isicom_activate(struct tty_port *tport, struct tty_struct *tty)
{
- struct isi_port *port = tty->driver_data;
+ struct isi_port *port = container_of(tport, struct isi_port, port);
struct isi_board *card = port->card;
unsigned long flags;
- if (port->port.flags & ASYNC_INITIALIZED)
- return 0;
- if (tty_port_alloc_xmit_buf(&port->port) < 0)
+ if (tty_port_alloc_xmit_buf(tport) < 0)
return -ENOMEM;
spin_lock_irqsave(&card->card_lock, flags);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->port.count == 1)
- card->count++;
+ isicom_setup_board(card);
port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
@@ -832,9 +827,7 @@ static int isicom_setup_port(struct tty_struct *tty)
outw(((ISICOM_KILLTX | ISICOM_KILLRX) << 8) | 0x06, card->base);
InterruptTheCard(card->base);
}
-
isicom_config_port(tty);
- port->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&card->card_lock, flags);
return 0;
@@ -871,85 +864,37 @@ static struct tty_port *isicom_find_port(struct tty_struct *tty)
return &port->port;
}
-
+
static int isicom_open(struct tty_struct *tty, struct file *filp)
{
struct isi_port *port;
struct isi_board *card;
struct tty_port *tport;
- int error = 0;
tport = isicom_find_port(tty);
if (tport == NULL)
return -ENODEV;
port = container_of(tport, struct isi_port, port);
card = &isi_card[BOARD(tty->index)];
- isicom_setup_board(card);
- /* FIXME: locking on port.count etc */
- port->port.count++;
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
- /* FIXME: Locking on Initialized flag */
- if (!test_bit(ASYNCB_INITIALIZED, &tport->flags))
- error = isicom_setup_port(tty);
- if (error == 0)
- error = tty_port_block_til_ready(&port->port, tty, filp);
- return error;
+ return tty_port_open(tport, tty, filp);
}
/* close et all */
-static inline void isicom_shutdown_board(struct isi_board *bp)
-{
- if (bp->status & BOARD_ACTIVE)
- bp->status &= ~BOARD_ACTIVE;
-}
-
/* card->lock HAS to be held */
static void isicom_shutdown_port(struct isi_port *port)
{
struct isi_board *card = port->card;
- struct tty_struct *tty;
-
- tty = tty_port_tty_get(&port->port);
-
- if (!(port->port.flags & ASYNC_INITIALIZED)) {
- tty_kref_put(tty);
- return;
- }
-
- tty_port_free_xmit_buf(&port->port);
- port->port.flags &= ~ASYNC_INITIALIZED;
- /* 3rd October 2000 : Vinayak P Risbud */
- tty_port_tty_set(&port->port, NULL);
-
- /*Fix done by Anil .S on 30-04-2001
- remote login through isi port has dtr toggle problem
- due to which the carrier drops before the password prompt
- appears on the remote end. Now we drop the dtr only if the
- HUPCL(Hangup on close) flag is set for the tty*/
-
- if (C_HUPCL(tty))
- /* drop dtr on this port */
- drop_dtr(port);
-
- /* any other port uninits */
- if (tty)
- set_bit(TTY_IO_ERROR, &tty->flags);
if (--card->count < 0) {
pr_dbg("isicom_shutdown_port: bad board(0x%lx) count %d.\n",
card->base, card->count);
card->count = 0;
}
-
- /* last port was closed, shutdown that boad too */
- if (C_HUPCL(tty)) {
- if (!card->count)
- isicom_shutdown_board(card);
- }
- tty_kref_put(tty);
+ /* last port was closed, shutdown that board too */
+ if (!card->count)
+ card->status &= BOARD_ACTIVE;
}
static void isicom_flush_buffer(struct tty_struct *tty)
@@ -968,7 +913,7 @@ static void isicom_flush_buffer(struct tty_struct *tty)
tty_wakeup(tty);
}
-static void isicom_close_port(struct tty_port *port)
+static void isicom_shutdown(struct tty_port *port)
{
struct isi_port *ip = container_of(port, struct isi_port, port);
struct isi_board *card = ip->card;
@@ -977,12 +922,11 @@ static void isicom_close_port(struct tty_port *port)
/* indicate to the card that no more data can be received
on this port */
spin_lock_irqsave(&card->card_lock, flags);
- if (port->flags & ASYNC_INITIALIZED) {
- card->port_status &= ~(1 << ip->channel);
- outw(card->port_status, card->base + 0x02);
- }
+ card->port_status &= ~(1 << ip->channel);
+ outw(card->port_status, card->base + 0x02);
isicom_shutdown_port(ip);
spin_unlock_irqrestore(&card->card_lock, flags);
+ tty_port_free_xmit_buf(port);
}
static void isicom_close(struct tty_struct *tty, struct file *filp)
@@ -991,12 +935,7 @@ static void isicom_close(struct tty_struct *tty, struct file *filp)
struct tty_port *port = &ip->port;
if (isicom_paranoia_check(ip, tty->name, "isicom_close"))
return;
-
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
- isicom_close_port(port);
- isicom_flush_buffer(tty);
- tty_port_close_end(port, tty);
+ tty_port_close(port, tty, filp);
}
/* write et all */
@@ -1326,15 +1265,9 @@ static void isicom_start(struct tty_struct *tty)
static void isicom_hangup(struct tty_struct *tty)
{
struct isi_port *port = tty->driver_data;
- unsigned long flags;
if (isicom_paranoia_check(port, tty->name, "isicom_hangup"))
return;
-
- spin_lock_irqsave(&port->card->card_lock, flags);
- isicom_shutdown_port(port);
- spin_unlock_irqrestore(&port->card->card_lock, flags);
-
tty_port_hangup(&port->port);
}
@@ -1367,6 +1300,8 @@ static const struct tty_operations isicom_ops = {
static const struct tty_port_operations isicom_port_ops = {
.carrier_raised = isicom_carrier_raised,
.dtr_rts = isicom_dtr_rts,
+ .activate = isicom_activate,
+ .shutdown = isicom_shutdown,
};
static int __devinit reset_card(struct pci_dev *pdev,
diff --git a/drivers/char/istallion.c b/drivers/char/istallion.c
index 402838f4083..4cd6c527ee4 100644
--- a/drivers/char/istallion.c
+++ b/drivers/char/istallion.c
@@ -213,7 +213,6 @@ static int stli_shared;
* with the slave. Most of them need to be updated atomically, so always
* use the bit setting operations (unless protected by cli/sti).
*/
-#define ST_INITIALIZING 1
#define ST_OPENING 2
#define ST_CLOSING 3
#define ST_CMDING 4
@@ -621,7 +620,7 @@ static int stli_brdinit(struct stlibrd *brdp);
static int stli_startbrd(struct stlibrd *brdp);
static ssize_t stli_memread(struct file *fp, char __user *buf, size_t count, loff_t *offp);
static ssize_t stli_memwrite(struct file *fp, const char __user *buf, size_t count, loff_t *offp);
-static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static void stli_brdpoll(struct stlibrd *brdp, cdkhdr_t __iomem *hdrp);
static void stli_poll(unsigned long arg);
static int stli_hostcmd(struct stlibrd *brdp, struct stliport *portp);
@@ -704,7 +703,7 @@ static const struct file_operations stli_fsiomem = {
.owner = THIS_MODULE,
.read = stli_memread,
.write = stli_memwrite,
- .ioctl = stli_memioctl,
+ .unlocked_ioctl = stli_memioctl,
};
/*****************************************************************************/
@@ -783,13 +782,32 @@ static int stli_parsebrd(struct stlconf *confp, char **argp)
/*****************************************************************************/
+/*
+ * On the first open of the device setup the port hardware, and
+ * initialize the per port data structure. Since initializing the port
+ * requires several commands to the board we will need to wait for any
+ * other open that is already initializing the port.
+ *
+ * Locking: protected by the port mutex.
+ */
+
+static int stli_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct stliport *portp = container_of(port, struct stliport, port);
+ struct stlibrd *brdp = stli_brds[portp->brdnr];
+ int rc;
+
+ if ((rc = stli_initopen(tty, brdp, portp)) >= 0)
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ wake_up_interruptible(&portp->raw_wait);
+ return rc;
+}
+
static int stli_open(struct tty_struct *tty, struct file *filp)
{
struct stlibrd *brdp;
struct stliport *portp;
- struct tty_port *port;
unsigned int minordev, brdnr, portnr;
- int rc;
minordev = tty->index;
brdnr = MINOR2BRD(minordev);
@@ -809,95 +827,56 @@ static int stli_open(struct tty_struct *tty, struct file *filp)
return -ENODEV;
if (portp->devnr < 1)
return -ENODEV;
- port = &portp->port;
-
-/*
- * On the first open of the device setup the port hardware, and
- * initialize the per port data structure. Since initializing the port
- * requires several commands to the board we will need to wait for any
- * other open that is already initializing the port.
- *
- * Review - locking
- */
- tty_port_tty_set(port, tty);
- tty->driver_data = portp;
- port->count++;
-
- wait_event_interruptible(portp->raw_wait,
- !test_bit(ST_INITIALIZING, &portp->state));
- if (signal_pending(current))
- return -ERESTARTSYS;
-
- if ((portp->port.flags & ASYNC_INITIALIZED) == 0) {
- set_bit(ST_INITIALIZING, &portp->state);
- if ((rc = stli_initopen(tty, brdp, portp)) >= 0) {
- /* Locking */
- port->flags |= ASYNC_INITIALIZED;
- clear_bit(TTY_IO_ERROR, &tty->flags);
- }
- clear_bit(ST_INITIALIZING, &portp->state);
- wake_up_interruptible(&portp->raw_wait);
- if (rc < 0)
- return rc;
- }
- return tty_port_block_til_ready(&portp->port, tty, filp);
+ return tty_port_open(&portp->port, tty, filp);
}
+
/*****************************************************************************/
-static void stli_close(struct tty_struct *tty, struct file *filp)
+static void stli_shutdown(struct tty_port *port)
{
struct stlibrd *brdp;
- struct stliport *portp;
- struct tty_port *port;
+ unsigned long ftype;
unsigned long flags;
+ struct stliport *portp = container_of(port, struct stliport, port);
- portp = tty->driver_data;
- if (portp == NULL)
+ if (portp->brdnr >= stli_nrbrds)
return;
- port = &portp->port;
-
- if (tty_port_close_start(port, tty, filp) == 0)
+ brdp = stli_brds[portp->brdnr];
+ if (brdp == NULL)
return;
-/*
- * May want to wait for data to drain before closing. The BUSY flag
- * keeps track of whether we are still transmitting or not. It is
- * updated by messages from the slave - indicating when all chars
- * really have drained.
- */
- spin_lock_irqsave(&stli_lock, flags);
- if (tty == stli_txcooktty)
- stli_flushchars(tty);
- spin_unlock_irqrestore(&stli_lock, flags);
-
- /* We end up doing this twice for the moment. This needs looking at
- eventually. Note we still use portp->closing_wait as a result */
- if (portp->closing_wait != ASYNC_CLOSING_WAIT_NONE)
- tty_wait_until_sent(tty, portp->closing_wait);
+ /*
+ * May want to wait for data to drain before closing. The BUSY
+ * flag keeps track of whether we are still transmitting or not.
+ * It is updated by messages from the slave - indicating when all
+ * chars really have drained.
+ */
- /* FIXME: port locking here needs attending to */
- port->flags &= ~ASYNC_INITIALIZED;
+ if (!test_bit(ST_CLOSING, &portp->state))
+ stli_rawclose(brdp, portp, 0, 0);
- brdp = stli_brds[portp->brdnr];
- stli_rawclose(brdp, portp, 0, 0);
- if (tty->termios->c_cflag & HUPCL) {
- stli_mkasysigs(&portp->asig, 0, 0);
- if (test_bit(ST_CMDING, &portp->state))
- set_bit(ST_DOSIGS, &portp->state);
- else
- stli_sendcmd(brdp, portp, A_SETSIGNALS, &portp->asig,
- sizeof(asysigs_t), 0);
- }
+ spin_lock_irqsave(&stli_lock, flags);
clear_bit(ST_TXBUSY, &portp->state);
clear_bit(ST_RXSTOP, &portp->state);
- set_bit(TTY_IO_ERROR, &tty->flags);
- tty_ldisc_flush(tty);
- set_bit(ST_DOFLUSHRX, &portp->state);
- stli_flushbuffer(tty);
+ spin_unlock_irqrestore(&stli_lock, flags);
- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
+ ftype = FLUSHTX | FLUSHRX;
+ stli_cmdwait(brdp, portp, A_FLUSH, &ftype, sizeof(u32), 0);
+}
+
+static void stli_close(struct tty_struct *tty, struct file *filp)
+{
+ struct stliport *portp = tty->driver_data;
+ unsigned long flags;
+ if (portp == NULL)
+ return;
+ spin_lock_irqsave(&stli_lock, flags);
+ /* Flush any internal buffering out first */
+ if (tty == stli_txcooktty)
+ stli_flushchars(tty);
+ spin_unlock_irqrestore(&stli_lock, flags);
+ tty_port_close(&portp->port, tty, filp);
}
/*****************************************************************************/
@@ -1724,6 +1703,7 @@ static void stli_start(struct tty_struct *tty)
/*****************************************************************************/
+
/*
* Hangup this port. This is pretty much like closing the port, only
* a little more brutal. No waiting for data to drain. Shutdown the
@@ -1733,47 +1713,8 @@ static void stli_start(struct tty_struct *tty)
static void stli_hangup(struct tty_struct *tty)
{
- struct stliport *portp;
- struct stlibrd *brdp;
- struct tty_port *port;
- unsigned long flags;
-
- portp = tty->driver_data;
- if (portp == NULL)
- return;
- if (portp->brdnr >= stli_nrbrds)
- return;
- brdp = stli_brds[portp->brdnr];
- if (brdp == NULL)
- return;
- port = &portp->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
- if (!test_bit(ST_CLOSING, &portp->state))
- stli_rawclose(brdp, portp, 0, 0);
-
- spin_lock_irqsave(&stli_lock, flags);
- if (tty->termios->c_cflag & HUPCL) {
- stli_mkasysigs(&portp->asig, 0, 0);
- if (test_bit(ST_CMDING, &portp->state)) {
- set_bit(ST_DOSIGS, &portp->state);
- set_bit(ST_DOFLUSHTX, &portp->state);
- set_bit(ST_DOFLUSHRX, &portp->state);
- } else {
- stli_sendcmd(brdp, portp, A_SETSIGNALSF,
- &portp->asig, sizeof(asysigs_t), 0);
- }
- }
-
- clear_bit(ST_TXBUSY, &portp->state);
- clear_bit(ST_RXSTOP, &portp->state);
- set_bit(TTY_IO_ERROR, &tty->flags);
- spin_unlock_irqrestore(&stli_lock, flags);
-
- tty_port_hangup(port);
+ struct stliport *portp = tty->driver_data;
+ tty_port_hangup(&portp->port);
}
/*****************************************************************************/
@@ -4311,7 +4252,7 @@ static int stli_getbrdstruct(struct stlibrd __user *arg)
* reset it, and start/stop it.
*/
-static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static long stli_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
struct stlibrd *brdp;
int brdnr, rc, done;
@@ -4356,7 +4297,7 @@ static int stli_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, un
* Now handle the board specific ioctls. These all depend on the
* minor number of the device they were called from.
*/
- brdnr = iminor(ip);
+ brdnr = iminor(fp->f_dentry->d_inode);
if (brdnr >= STL_MAXBRDS)
return -ENODEV;
brdp = stli_brds[brdnr];
@@ -4420,6 +4361,8 @@ static const struct tty_operations stli_ops = {
static const struct tty_port_operations stli_port_ops = {
.carrier_raised = stli_carrier_raised,
.dtr_rts = stli_dtr_rts,
+ .activate = stli_activate,
+ .shutdown = stli_shutdown,
};
/*****************************************************************************/
diff --git a/drivers/char/moxa.c b/drivers/char/moxa.c
index dd0083bbb64..63ee3bbc1ce 100644
--- a/drivers/char/moxa.c
+++ b/drivers/char/moxa.c
@@ -34,7 +34,6 @@
#include <linux/tty.h>
#include <linux/tty_flip.h>
#include <linux/major.h>
-#include <linux/smp_lock.h>
#include <linux/string.h>
#include <linux/fcntl.h>
#include <linux/ptrace.h>
@@ -139,7 +138,7 @@ struct moxa_port {
int cflag;
unsigned long statusflags;
- u8 DCDState;
+ u8 DCDState; /* Protected by the port lock */
u8 lineCtrl;
u8 lowChkFlag;
};
@@ -151,10 +150,9 @@ struct mon_str {
};
/* statusflags */
-#define TXSTOPPED 0x1
-#define LOWWAIT 0x2
-#define EMPTYWAIT 0x4
-#define THROTTLE 0x8
+#define TXSTOPPED 1
+#define LOWWAIT 2
+#define EMPTYWAIT 3
#define SERIAL_DO_RESTART
@@ -165,6 +163,7 @@ static struct mon_str moxaLog;
static unsigned int moxaFuncTout = HZ / 2;
static unsigned int moxaLowWaterChk;
static DEFINE_MUTEX(moxa_openlock);
+static DEFINE_SPINLOCK(moxa_lock);
/* Variables for insmod */
#ifdef MODULE
static unsigned long baseaddr[MAX_BOARDS];
@@ -194,8 +193,6 @@ static int moxa_write(struct tty_struct *, const unsigned char *, int);
static int moxa_write_room(struct tty_struct *);
static void moxa_flush_buffer(struct tty_struct *);
static int moxa_chars_in_buffer(struct tty_struct *);
-static void moxa_throttle(struct tty_struct *);
-static void moxa_unthrottle(struct tty_struct *);
static void moxa_set_termios(struct tty_struct *, struct ktermios *);
static void moxa_stop(struct tty_struct *);
static void moxa_start(struct tty_struct *);
@@ -205,9 +202,9 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
unsigned int set, unsigned int clear);
static void moxa_poll(unsigned long);
static void moxa_set_tty_param(struct tty_struct *, struct ktermios *);
-static void moxa_setup_empty_event(struct tty_struct *);
-static void moxa_shut_down(struct tty_struct *);
+static void moxa_shutdown(struct tty_port *);
static int moxa_carrier_raised(struct tty_port *);
+static void moxa_dtr_rts(struct tty_port *, int);
/*
* moxa board interface functions:
*/
@@ -234,6 +231,8 @@ static void MoxaSetFifo(struct moxa_port *port, int enable);
* I/O functions
*/
+static DEFINE_SPINLOCK(moxafunc_lock);
+
static void moxa_wait_finish(void __iomem *ofsAddr)
{
unsigned long end = jiffies + moxaFuncTout;
@@ -247,9 +246,25 @@ static void moxa_wait_finish(void __iomem *ofsAddr)
static void moxafunc(void __iomem *ofsAddr, u16 cmd, u16 arg)
{
+ unsigned long flags;
+ spin_lock_irqsave(&moxafunc_lock, flags);
writew(arg, ofsAddr + FuncArg);
writew(cmd, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
+ spin_unlock_irqrestore(&moxafunc_lock, flags);
+}
+
+static int moxafuncret(void __iomem *ofsAddr, u16 cmd, u16 arg)
+{
+ unsigned long flags;
+ u16 ret;
+ spin_lock_irqsave(&moxafunc_lock, flags);
+ writew(arg, ofsAddr + FuncArg);
+ writew(cmd, ofsAddr + FuncCode);
+ moxa_wait_finish(ofsAddr);
+ ret = readw(ofsAddr + FuncArg);
+ spin_unlock_irqrestore(&moxafunc_lock, flags);
+ return ret;
}
static void moxa_low_water_check(void __iomem *ofsAddr)
@@ -299,22 +314,20 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
struct moxa_port *p;
unsigned int i, j;
- mutex_lock(&moxa_openlock);
for (i = 0; i < MAX_BOARDS; i++) {
p = moxa_boards[i].ports;
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
memset(&tmp, 0, sizeof(tmp));
+ spin_lock_bh(&moxa_lock);
if (moxa_boards[i].ready) {
tmp.inq = MoxaPortRxQueue(p);
tmp.outq = MoxaPortTxQueue(p);
}
- if (copy_to_user(argm, &tmp, sizeof(tmp))) {
- mutex_unlock(&moxa_openlock);
+ spin_unlock_bh(&moxa_lock);
+ if (copy_to_user(argm, &tmp, sizeof(tmp)))
return -EFAULT;
- }
}
}
- mutex_unlock(&moxa_openlock);
break;
} case MOXA_GET_OQUEUE:
status = MoxaPortTxQueue(ch);
@@ -330,16 +343,20 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
struct moxa_port *p;
unsigned int i, j;
- mutex_lock(&moxa_openlock);
for (i = 0; i < MAX_BOARDS; i++) {
p = moxa_boards[i].ports;
for (j = 0; j < MAX_PORTS_PER_BOARD; j++, p++, argm++) {
struct tty_struct *ttyp;
memset(&tmp, 0, sizeof(tmp));
- if (!moxa_boards[i].ready)
+ spin_lock_bh(&moxa_lock);
+ if (!moxa_boards[i].ready) {
+ spin_unlock_bh(&moxa_lock);
goto copy;
+ }
status = MoxaPortLineStatus(p);
+ spin_unlock_bh(&moxa_lock);
+
if (status & 1)
tmp.cts = 1;
if (status & 2)
@@ -354,24 +371,21 @@ static int moxa_ioctl(struct tty_struct *tty, struct file *file,
tmp.cflag = ttyp->termios->c_cflag;
tty_kref_put(tty);
copy:
- if (copy_to_user(argm, &tmp, sizeof(tmp))) {
- mutex_unlock(&moxa_openlock);
+ if (copy_to_user(argm, &tmp, sizeof(tmp)))
return -EFAULT;
- }
}
}
- mutex_unlock(&moxa_openlock);
break;
}
case TIOCGSERIAL:
- mutex_lock(&moxa_openlock);
+ mutex_lock(&ch->port.mutex);
ret = moxa_get_serial_info(ch, argp);
- mutex_unlock(&moxa_openlock);
+ mutex_unlock(&ch->port.mutex);
break;
case TIOCSSERIAL:
- mutex_lock(&moxa_openlock);
+ mutex_lock(&ch->port.mutex);
ret = moxa_set_serial_info(ch, argp);
- mutex_unlock(&moxa_openlock);
+ mutex_unlock(&ch->port.mutex);
break;
default:
ret = -ENOIOCTLCMD;
@@ -396,8 +410,6 @@ static const struct tty_operations moxa_ops = {
.flush_buffer = moxa_flush_buffer,
.chars_in_buffer = moxa_chars_in_buffer,
.ioctl = moxa_ioctl,
- .throttle = moxa_throttle,
- .unthrottle = moxa_unthrottle,
.set_termios = moxa_set_termios,
.stop = moxa_stop,
.start = moxa_start,
@@ -409,11 +421,12 @@ static const struct tty_operations moxa_ops = {
static const struct tty_port_operations moxa_port_ops = {
.carrier_raised = moxa_carrier_raised,
+ .dtr_rts = moxa_dtr_rts,
+ .shutdown = moxa_shutdown,
};
static struct tty_driver *moxaDriver;
static DEFINE_TIMER(moxaTimer, moxa_poll, 0, 0);
-static DEFINE_SPINLOCK(moxa_lock);
/*
* HW init
@@ -1112,14 +1125,12 @@ static void __exit moxa_exit(void)
module_init(moxa_init);
module_exit(moxa_exit);
-static void moxa_close_port(struct tty_struct *tty)
+static void moxa_shutdown(struct tty_port *port)
{
- struct moxa_port *ch = tty->driver_data;
- moxa_shut_down(tty);
+ struct moxa_port *ch = container_of(port, struct moxa_port, port);
+ MoxaPortDisable(ch);
MoxaPortFlushData(ch, 2);
- ch->port.flags &= ~ASYNC_NORMAL_ACTIVE;
- tty->driver_data = NULL;
- tty_port_tty_set(&ch->port, NULL);
+ clear_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);
}
static int moxa_carrier_raised(struct tty_port *port)
@@ -1127,45 +1138,19 @@ static int moxa_carrier_raised(struct tty_port *port)
struct moxa_port *ch = container_of(port, struct moxa_port, port);
int dcd;
- spin_lock_bh(&moxa_lock);
+ spin_lock_irq(&port->lock);
dcd = ch->DCDState;
- spin_unlock_bh(&moxa_lock);
+ spin_unlock_irq(&port->lock);
return dcd;
}
-static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp,
- struct moxa_port *ch)
+static void moxa_dtr_rts(struct tty_port *port, int onoff)
{
- struct tty_port *port = &ch->port;
- DEFINE_WAIT(wait);
- int retval = 0;
- u8 dcd;
-
- while (1) {
- prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
- if (tty_hung_up_p(filp)) {
-#ifdef SERIAL_DO_RESTART
- retval = -ERESTARTSYS;
-#else
- retval = -EAGAIN;
-#endif
- break;
- }
- dcd = tty_port_carrier_raised(port);
- if (dcd)
- break;
-
- if (signal_pending(current)) {
- retval = -ERESTARTSYS;
- break;
- }
- schedule();
- }
- finish_wait(&port->open_wait, &wait);
-
- return retval;
+ struct moxa_port *ch = container_of(port, struct moxa_port, port);
+ MoxaPortLineCtrl(ch, onoff, onoff);
}
+
static int moxa_open(struct tty_struct *tty, struct file *filp)
{
struct moxa_board_conf *brd;
@@ -1194,6 +1179,7 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
ch->port.count++;
tty->driver_data = ch;
tty_port_tty_set(&ch->port, tty);
+ mutex_lock(&ch->port.mutex);
if (!(ch->port.flags & ASYNC_INITIALIZED)) {
ch->statusflags = 0;
moxa_set_tty_param(tty, tty->termios);
@@ -1202,58 +1188,20 @@ static int moxa_open(struct tty_struct *tty, struct file *filp)
MoxaSetFifo(ch, ch->type == PORT_16550A);
ch->port.flags |= ASYNC_INITIALIZED;
}
+ mutex_unlock(&ch->port.mutex);
mutex_unlock(&moxa_openlock);
- retval = 0;
- if (!(filp->f_flags & O_NONBLOCK) && !C_CLOCAL(tty))
- retval = moxa_block_till_ready(tty, filp, ch);
- mutex_lock(&moxa_openlock);
- if (retval) {
- if (ch->port.count) /* 0 means already hung up... */
- if (--ch->port.count == 0)
- moxa_close_port(tty);
- } else
- ch->port.flags |= ASYNC_NORMAL_ACTIVE;
- mutex_unlock(&moxa_openlock);
-
+ retval = tty_port_block_til_ready(&ch->port, tty, filp);
+ if (retval == 0)
+ set_bit(ASYNCB_NORMAL_ACTIVE, &ch->port.flags);
return retval;
}
static void moxa_close(struct tty_struct *tty, struct file *filp)
{
- struct moxa_port *ch;
- int port;
-
- port = tty->index;
- if (port == MAX_PORTS || tty_hung_up_p(filp))
- return;
-
- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
- if (ch == NULL)
- goto unlock;
- if (tty->count == 1 && ch->port.count != 1) {
- printk(KERN_WARNING "moxa_close: bad serial port count; "
- "tty->count is 1, ch->port.count is %d\n", ch->port.count);
- ch->port.count = 1;
- }
- if (--ch->port.count < 0) {
- printk(KERN_WARNING "moxa_close: bad serial port count, "
- "device=%s\n", tty->name);
- ch->port.count = 0;
- }
- if (ch->port.count)
- goto unlock;
-
+ struct moxa_port *ch = tty->driver_data;
ch->cflag = tty->termios->c_cflag;
- if (ch->port.flags & ASYNC_INITIALIZED) {
- moxa_setup_empty_event(tty);
- tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */
- }
-
- moxa_close_port(tty);
-unlock:
- mutex_unlock(&moxa_openlock);
+ tty_port_close(&ch->port, tty, filp);
}
static int moxa_write(struct tty_struct *tty,
@@ -1269,7 +1217,7 @@ static int moxa_write(struct tty_struct *tty,
len = MoxaPortWriteData(tty, buf, count);
spin_unlock_bh(&moxa_lock);
- ch->statusflags |= LOWWAIT;
+ set_bit(LOWWAIT, &ch->statusflags);
return len;
}
@@ -1300,40 +1248,21 @@ static int moxa_chars_in_buffer(struct tty_struct *tty)
struct moxa_port *ch = tty->driver_data;
int chars;
- /*
- * Sigh...I have to check if driver_data is NULL here, because
- * if an open() fails, the TTY subsystem eventually calls
- * tty_wait_until_sent(), which calls the driver's chars_in_buffer()
- * routine. And since the open() failed, we return 0 here. TDJ
- */
- if (ch == NULL)
- return 0;
- lock_kernel();
chars = MoxaPortTxQueue(ch);
- if (chars) {
+ if (chars)
/*
* Make it possible to wakeup anything waiting for output
* in tty_ioctl.c, etc.
*/
- if (!(ch->statusflags & EMPTYWAIT))
- moxa_setup_empty_event(tty);
- }
- unlock_kernel();
+ set_bit(EMPTYWAIT, &ch->statusflags);
return chars;
}
static int moxa_tiocmget(struct tty_struct *tty, struct file *file)
{
- struct moxa_port *ch;
+ struct moxa_port *ch = tty->driver_data;
int flag = 0, dtr, rts;
- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
- if (!ch) {
- mutex_unlock(&moxa_openlock);
- return -EINVAL;
- }
-
MoxaPortGetLineOut(ch, &dtr, &rts);
if (dtr)
flag |= TIOCM_DTR;
@@ -1346,7 +1275,6 @@ static int moxa_tiocmget(struct tty_struct *tty, struct file *file)
flag |= TIOCM_DSR;
if (dtr & 4)
flag |= TIOCM_CD;
- mutex_unlock(&moxa_openlock);
return flag;
}
@@ -1379,20 +1307,6 @@ static int moxa_tiocmset(struct tty_struct *tty, struct file *file,
return 0;
}
-static void moxa_throttle(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- ch->statusflags |= THROTTLE;
-}
-
-static void moxa_unthrottle(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- ch->statusflags &= ~THROTTLE;
-}
-
static void moxa_set_termios(struct tty_struct *tty,
struct ktermios *old_termios)
{
@@ -1412,7 +1326,7 @@ static void moxa_stop(struct tty_struct *tty)
if (ch == NULL)
return;
MoxaPortTxDisable(ch);
- ch->statusflags |= TXSTOPPED;
+ set_bit(TXSTOPPED, &ch->statusflags);
}
@@ -1427,38 +1341,32 @@ static void moxa_start(struct tty_struct *tty)
return;
MoxaPortTxEnable(ch);
- ch->statusflags &= ~TXSTOPPED;
+ clear_bit(TXSTOPPED, &ch->statusflags);
}
static void moxa_hangup(struct tty_struct *tty)
{
- struct moxa_port *ch;
-
- mutex_lock(&moxa_openlock);
- ch = tty->driver_data;
- if (ch == NULL) {
- mutex_unlock(&moxa_openlock);
- return;
- }
- ch->port.count = 0;
- moxa_close_port(tty);
- mutex_unlock(&moxa_openlock);
-
- wake_up_interruptible(&ch->port.open_wait);
+ struct moxa_port *ch = tty->driver_data;
+ tty_port_hangup(&ch->port);
}
static void moxa_new_dcdstate(struct moxa_port *p, u8 dcd)
{
struct tty_struct *tty;
+ unsigned long flags;
dcd = !!dcd;
+ spin_lock_irqsave(&p->port.lock, flags);
if (dcd != p->DCDState) {
+ p->DCDState = dcd;
+ spin_unlock_irqrestore(&p->port.lock, flags);
tty = tty_port_tty_get(&p->port);
if (tty && C_CLOCAL(tty) && !dcd)
tty_hangup(tty);
tty_kref_put(tty);
}
- p->DCDState = dcd;
+ else
+ spin_unlock_irqrestore(&p->port.lock, flags);
}
static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
@@ -1470,24 +1378,24 @@ static int moxa_poll_port(struct moxa_port *p, unsigned int handle,
u16 intr;
if (tty) {
- if ((p->statusflags & EMPTYWAIT) &&
+ if (test_bit(EMPTYWAIT, &p->statusflags) &&
MoxaPortTxQueue(p) == 0) {
- p->statusflags &= ~EMPTYWAIT;
+ clear_bit(EMPTYWAIT, &p->statusflags);
tty_wakeup(tty);
}
- if ((p->statusflags & LOWWAIT) && !tty->stopped &&
+ if (test_bit(LOWWAIT, &p->statusflags) && !tty->stopped &&
MoxaPortTxQueue(p) <= WAKEUP_CHARS) {
- p->statusflags &= ~LOWWAIT;
+ clear_bit(LOWWAIT, &p->statusflags);
tty_wakeup(tty);
}
- if (inited && !(p->statusflags & THROTTLE) &&
+ if (inited && !test_bit(TTY_THROTTLED, &tty->flags) &&
MoxaPortRxQueue(p) > 0) { /* RX */
MoxaPortReadData(p);
tty_schedule_flip(tty);
}
} else {
- p->statusflags &= ~EMPTYWAIT;
+ clear_bit(EMPTYWAIT, &p->statusflags);
MoxaPortFlushData(p, 0); /* flush RX */
}
@@ -1588,35 +1496,6 @@ static void moxa_set_tty_param(struct tty_struct *tty, struct ktermios *old_term
tty_encode_baud_rate(tty, baud, baud);
}
-static void moxa_setup_empty_event(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- spin_lock_bh(&moxa_lock);
- ch->statusflags |= EMPTYWAIT;
- spin_unlock_bh(&moxa_lock);
-}
-
-static void moxa_shut_down(struct tty_struct *tty)
-{
- struct moxa_port *ch = tty->driver_data;
-
- if (!(ch->port.flags & ASYNC_INITIALIZED))
- return;
-
- MoxaPortDisable(ch);
-
- /*
- * If we're a modem control device and HUPCL is on, drop RTS & DTR.
- */
- if (C_HUPCL(tty))
- MoxaPortLineCtrl(ch, 0, 0);
-
- spin_lock_bh(&moxa_lock);
- ch->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_bh(&moxa_lock);
-}
-
/*****************************************************************************
* Driver level functions: *
*****************************************************************************/
@@ -1918,10 +1797,12 @@ static int MoxaPortSetTermio(struct moxa_port *port, struct ktermios *termio,
baud = MoxaPortSetBaud(port, baud);
if (termio->c_iflag & (IXON | IXOFF | IXANY)) {
+ spin_lock_irq(&moxafunc_lock);
writeb(termio->c_cc[VSTART], ofsAddr + FuncArg);
writeb(termio->c_cc[VSTOP], ofsAddr + FuncArg1);
writeb(FC_SetXonXoff, ofsAddr + FuncCode);
moxa_wait_finish(ofsAddr);
+ spin_unlock_irq(&moxafunc_lock);
}
return baud;
@@ -1974,18 +1855,14 @@ static int MoxaPortLineStatus(struct moxa_port *port)
int val;
ofsAddr = port->tableAddr;
- if (MOXA_IS_320(port->board)) {
- moxafunc(ofsAddr, FC_LineStatus, 0);
- val = readw(ofsAddr + FuncArg);
- } else {
+ if (MOXA_IS_320(port->board))
+ val = moxafuncret(ofsAddr, FC_LineStatus, 0);
+ else
val = readw(ofsAddr + FlagStat) >> 4;
- }
val &= 0x0B;
if (val & 8)
val |= 4;
- spin_lock_bh(&moxa_lock);
moxa_new_dcdstate(port, val & 8);
- spin_unlock_bh(&moxa_lock);
val &= 7;
return val;
}
diff --git a/drivers/char/mxser.c b/drivers/char/mxser.c
index 5e28d39b9e8..3d923065d9a 100644
--- a/drivers/char/mxser.c
+++ b/drivers/char/mxser.c
@@ -23,7 +23,6 @@
#include <linux/errno.h>
#include <linux/signal.h>
#include <linux/sched.h>
-#include <linux/smp_lock.h>
#include <linux/timer.h>
#include <linux/interrupt.h>
#include <linux/tty.h>
@@ -856,9 +855,9 @@ static void mxser_check_modem_status(struct tty_struct *tty,
}
}
-static int mxser_startup(struct tty_struct *tty)
+static int mxser_activate(struct tty_port *port, struct tty_struct *tty)
{
- struct mxser_port *info = tty->driver_data;
+ struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long page;
unsigned long flags;
@@ -868,22 +867,13 @@ static int mxser_startup(struct tty_struct *tty)
spin_lock_irqsave(&info->slock, flags);
- if (info->port.flags & ASYNC_INITIALIZED) {
- free_page(page);
- spin_unlock_irqrestore(&info->slock, flags);
- return 0;
- }
-
if (!info->ioaddr || !info->type) {
set_bit(TTY_IO_ERROR, &tty->flags);
free_page(page);
spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
- if (info->port.xmit_buf)
- free_page(page);
- else
- info->port.xmit_buf = (unsigned char *) page;
+ info->port.xmit_buf = (unsigned char *) page;
/*
* Clear the FIFO buffers and disable them
@@ -951,24 +941,19 @@ static int mxser_startup(struct tty_struct *tty)
* and set the speed of the serial port
*/
mxser_change_speed(tty, NULL);
- info->port.flags |= ASYNC_INITIALIZED;
spin_unlock_irqrestore(&info->slock, flags);
return 0;
}
/*
- * This routine will shutdown a serial port; interrupts maybe disabled, and
- * DTR is dropped if the hangup on close termio flag is on.
+ * This routine will shutdown a serial port
*/
-static void mxser_shutdown(struct tty_struct *tty)
+static void mxser_shutdown_port(struct tty_port *port)
{
- struct mxser_port *info = tty->driver_data;
+ struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long flags;
- if (!(info->port.flags & ASYNC_INITIALIZED))
- return;
-
spin_lock_irqsave(&info->slock, flags);
/*
@@ -978,7 +963,7 @@ static void mxser_shutdown(struct tty_struct *tty)
wake_up_interruptible(&info->port.delta_msr_wait);
/*
- * Free the IRQ, if necessary
+ * Free the xmit buffer, if necessary
*/
if (info->port.xmit_buf) {
free_page((unsigned long) info->port.xmit_buf);
@@ -988,10 +973,6 @@ static void mxser_shutdown(struct tty_struct *tty)
info->IER = 0;
outb(0x00, info->ioaddr + UART_IER);
- if (tty->termios->c_cflag & HUPCL)
- info->MCR &= ~(UART_MCR_DTR | UART_MCR_RTS);
- outb(info->MCR, info->ioaddr + UART_MCR);
-
/* clear Rx/Tx FIFO's */
if (info->board->chip_flag)
outb(UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
@@ -1004,9 +985,6 @@ static void mxser_shutdown(struct tty_struct *tty)
/* read data port to reset things */
(void) inb(info->ioaddr + UART_RX);
- set_bit(TTY_IO_ERROR, &tty->flags);
-
- info->port.flags &= ~ASYNC_INITIALIZED;
if (info->board->chip_flag)
SET_MOXA_MUST_NO_SOFTWARE_FLOW_CONTROL(info->ioaddr);
@@ -1023,8 +1001,7 @@ static void mxser_shutdown(struct tty_struct *tty)
static int mxser_open(struct tty_struct *tty, struct file *filp)
{
struct mxser_port *info;
- unsigned long flags;
- int retval, line;
+ int line;
line = tty->index;
if (line == MXSER_PORTS)
@@ -1035,23 +1012,7 @@ static int mxser_open(struct tty_struct *tty, struct file *filp)
if (!info->ioaddr)
return -ENODEV;
- tty->driver_data = info;
- tty_port_tty_set(&info->port, tty);
- /*
- * Start up serial port
- */
- spin_lock_irqsave(&info->port.lock, flags);
- info->port.count++;
- spin_unlock_irqrestore(&info->port.lock, flags);
- retval = mxser_startup(tty);
- if (retval)
- return retval;
-
- retval = tty_port_block_til_ready(&info->port, tty, filp);
- if (retval)
- return retval;
-
- return 0;
+ return tty_port_open(&info->port, tty, filp);
}
static void mxser_flush_buffer(struct tty_struct *tty)
@@ -1075,19 +1036,11 @@ static void mxser_flush_buffer(struct tty_struct *tty)
}
-static void mxser_close_port(struct tty_struct *tty, struct tty_port *port)
+static void mxser_close_port(struct tty_port *port)
{
struct mxser_port *info = container_of(port, struct mxser_port, port);
unsigned long timeout;
/*
- * Save the termios structure, since this port may have
- * separate termios for callout and dialin.
- *
- * FIXME: Can this go ?
- */
- if (port->flags & ASYNC_NORMAL_ACTIVE)
- info->normal_termios = *tty->termios;
- /*
* At this point we stop accepting input. To do this, we
* disable the receive line status interrupts, and tell the
* interrupt driver to stop checking the data ready bit in the
@@ -1097,22 +1050,18 @@ static void mxser_close_port(struct tty_struct *tty, struct tty_port *port)
if (info->board->chip_flag)
info->IER &= ~MOXA_MUST_RECV_ISR;
- if (port->flags & ASYNC_INITIALIZED) {
- outb(info->IER, info->ioaddr + UART_IER);
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- timeout = jiffies + HZ;
- while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
- schedule_timeout_interruptible(5);
- if (time_after(jiffies, timeout))
- break;
- }
+ outb(info->IER, info->ioaddr + UART_IER);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (!(inb(info->ioaddr + UART_LSR) & UART_LSR_TEMT)) {
+ schedule_timeout_interruptible(5);
+ if (time_after(jiffies, timeout))
+ break;
}
- mxser_shutdown(tty);
-
}
/*
@@ -1130,8 +1079,12 @@ static void mxser_close(struct tty_struct *tty, struct file *filp)
return;
if (tty_port_close_start(port, tty, filp) == 0)
return;
- mxser_close_port(tty, port);
+ mutex_lock(&port->mutex);
+ mxser_close_port(port);
mxser_flush_buffer(tty);
+ mxser_shutdown_port(port);
+ clear_bit(ASYNCB_INITIALIZED, &port->flags);
+ mutex_unlock(&port->mutex);
/* Right now the tty_port set is done outside of the close_end helper
as we don't yet have everyone using refcounts */
tty_port_close_end(port, tty);
@@ -1275,6 +1228,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
struct serial_struct __user *new_info)
{
struct mxser_port *info = tty->driver_data;
+ struct tty_port *port = &info->port;
struct serial_struct new_serial;
speed_t baud;
unsigned long sl_flags;
@@ -1290,7 +1244,7 @@ static int mxser_set_serial_info(struct tty_struct *tty,
new_serial.port != info->ioaddr)
return -EINVAL;
- flags = info->port.flags & ASYNC_SPD_MASK;
+ flags = port->flags & ASYNC_SPD_MASK;
if (!capable(CAP_SYS_ADMIN)) {
if ((new_serial.baud_base != info->baud_base) ||
@@ -1304,16 +1258,17 @@ static int mxser_set_serial_info(struct tty_struct *tty,
* OK, past this point, all the error checking has been done.
* At this point, we start making changes.....
*/
- info->port.flags = ((info->port.flags & ~ASYNC_FLAGS) |
+ port->flags = ((port->flags & ~ASYNC_FLAGS) |
(new_serial.flags & ASYNC_FLAGS));
- info->port.close_delay = new_serial.close_delay * HZ / 100;
- info->port.closing_wait = new_serial.closing_wait * HZ / 100;
- tty->low_latency = (info->port.flags & ASYNC_LOW_LATENCY)
- ? 1 : 0;
- if ((info->port.flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
+ port->close_delay = new_serial.close_delay * HZ / 100;
+ port->closing_wait = new_serial.closing_wait * HZ / 100;
+ tty->low_latency = (port->flags & ASYNC_LOW_LATENCY) ? 1 : 0;
+ if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST &&
(new_serial.baud_base != info->baud_base ||
new_serial.custom_divisor !=
info->custom_divisor)) {
+ if (new_serial.custom_divisor == 0)
+ return -EINVAL;
baud = new_serial.baud_base / new_serial.custom_divisor;
tty_encode_baud_rate(tty, baud, baud);
}
@@ -1323,15 +1278,17 @@ static int mxser_set_serial_info(struct tty_struct *tty,
process_txrx_fifo(info);
- if (info->port.flags & ASYNC_INITIALIZED) {
- if (flags != (info->port.flags & ASYNC_SPD_MASK)) {
+ if (test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ if (flags != (port->flags & ASYNC_SPD_MASK)) {
spin_lock_irqsave(&info->slock, sl_flags);
mxser_change_speed(tty, NULL);
spin_unlock_irqrestore(&info->slock, sl_flags);
}
- } else
- retval = mxser_startup(tty);
-
+ } else {
+ retval = mxser_activate(port, tty);
+ if (retval == 0)
+ set_bit(ASYNCB_INITIALIZED, &port->flags);
+ }
return retval;
}
@@ -1520,7 +1477,8 @@ static int __init mxser_read_register(int port, unsigned short *regs)
static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
{
- struct mxser_port *port;
+ struct mxser_port *ip;
+ struct tty_port *port;
struct tty_struct *tty;
int result, status;
unsigned int i, j;
@@ -1536,38 +1494,39 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
case MOXA_CHKPORTENABLE:
result = 0;
- lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++)
if (mxser_boards[i].ports[j].ioaddr)
result |= (1 << i);
- unlock_kernel();
return put_user(result, (unsigned long __user *)argp);
case MOXA_GETDATACOUNT:
- lock_kernel();
+ /* The receive side is locked by port->slock but it isn't
+ clear that an exact snapshot is worth copying here */
if (copy_to_user(argp, &mxvar_log, sizeof(mxvar_log)))
ret = -EFAULT;
- unlock_kernel();
return ret;
case MOXA_GETMSTATUS: {
struct mxser_mstatus ms, __user *msu = argp;
- lock_kernel();
for (i = 0; i < MXSER_BOARDS; i++)
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++) {
- port = &mxser_boards[i].ports[j];
+ ip = &mxser_boards[i].ports[j];
+ port = &ip->port;
memset(&ms, 0, sizeof(ms));
- if (!port->ioaddr)
+ mutex_lock(&port->mutex);
+ if (!ip->ioaddr)
goto copy;
- tty = tty_port_tty_get(&port->port);
+ tty = tty_port_tty_get(port);
if (!tty || !tty->termios)
- ms.cflag = port->normal_termios.c_cflag;
+ ms.cflag = ip->normal_termios.c_cflag;
else
ms.cflag = tty->termios->c_cflag;
tty_kref_put(tty);
- status = inb(port->ioaddr + UART_MSR);
+ spin_lock_irq(&ip->slock);
+ status = inb(ip->ioaddr + UART_MSR);
+ spin_unlock_irq(&ip->slock);
if (status & UART_MSR_DCD)
ms.dcd = 1;
if (status & UART_MSR_DSR)
@@ -1575,13 +1534,11 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (status & UART_MSR_CTS)
ms.cts = 1;
copy:
- if (copy_to_user(msu, &ms, sizeof(ms))) {
- unlock_kernel();
+ mutex_unlock(&port->mutex);
+ if (copy_to_user(msu, &ms, sizeof(ms)))
return -EFAULT;
- }
msu++;
}
- unlock_kernel();
return 0;
}
case MOXA_ASPP_MON_EXT: {
@@ -1593,41 +1550,48 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (!me)
return -ENOMEM;
- lock_kernel();
for (i = 0, p = 0; i < MXSER_BOARDS; i++) {
for (j = 0; j < MXSER_PORTS_PER_BOARD; j++, p++) {
if (p >= ARRAY_SIZE(me->rx_cnt)) {
i = MXSER_BOARDS;
break;
}
- port = &mxser_boards[i].ports[j];
- if (!port->ioaddr)
+ ip = &mxser_boards[i].ports[j];
+ port = &ip->port;
+
+ mutex_lock(&port->mutex);
+ if (!ip->ioaddr) {
+ mutex_unlock(&port->mutex);
continue;
+ }
- status = mxser_get_msr(port->ioaddr, 0, p);
+ spin_lock_irq(&ip->slock);
+ status = mxser_get_msr(ip->ioaddr, 0, p);
if (status & UART_MSR_TERI)
- port->icount.rng++;
+ ip->icount.rng++;
if (status & UART_MSR_DDSR)
- port->icount.dsr++;
+ ip->icount.dsr++;
if (status & UART_MSR_DDCD)
- port->icount.dcd++;
+ ip->icount.dcd++;
if (status & UART_MSR_DCTS)
- port->icount.cts++;
+ ip->icount.cts++;
- port->mon_data.modem_status = status;
- me->rx_cnt[p] = port->mon_data.rxcnt;
- me->tx_cnt[p] = port->mon_data.txcnt;
- me->up_rxcnt[p] = port->mon_data.up_rxcnt;
- me->up_txcnt[p] = port->mon_data.up_txcnt;
+ ip->mon_data.modem_status = status;
+ me->rx_cnt[p] = ip->mon_data.rxcnt;
+ me->tx_cnt[p] = ip->mon_data.txcnt;
+ me->up_rxcnt[p] = ip->mon_data.up_rxcnt;
+ me->up_txcnt[p] = ip->mon_data.up_txcnt;
me->modem_status[p] =
- port->mon_data.modem_status;
- tty = tty_port_tty_get(&port->port);
+ ip->mon_data.modem_status;
+ spin_unlock_irq(&ip->slock);
+
+ tty = tty_port_tty_get(&ip->port);
if (!tty || !tty->termios) {
- cflag = port->normal_termios.c_cflag;
- iflag = port->normal_termios.c_iflag;
- me->baudrate[p] = tty_termios_baud_rate(&port->normal_termios);
+ cflag = ip->normal_termios.c_cflag;
+ iflag = ip->normal_termios.c_iflag;
+ me->baudrate[p] = tty_termios_baud_rate(&ip->normal_termios);
} else {
cflag = tty->termios->c_cflag;
iflag = tty->termios->c_iflag;
@@ -1646,16 +1610,15 @@ static int mxser_ioctl_special(unsigned int cmd, void __user *argp)
if (iflag & (IXON | IXOFF))
me->flowctrl[p] |= 0x0C;
- if (port->type == PORT_16550A)
+ if (ip->type == PORT_16550A)
me->fifo[p] = 1;
- opmode = inb(port->opmode_ioaddr) >>
- ((p % 4) * 2);
+ opmode = inb(ip->opmode_ioaddr)>>((p % 4) * 2);
opmode &= OP_MODE_MASK;
me->iftype[p] = opmode;
+ mutex_unlock(&port->mutex);
}
}
- unlock_kernel();
if (copy_to_user(argp, me, sizeof(*me)))
ret = -EFAULT;
kfree(me);
@@ -1692,6 +1655,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct mxser_port *info = tty->driver_data;
+ struct tty_port *port = &info->port;
struct async_icount cnow;
unsigned long flags;
void __user *argp = (void __user *)arg;
@@ -1716,20 +1680,20 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
opmode != RS422_MODE &&
opmode != RS485_4WIRE_MODE)
return -EFAULT;
- lock_kernel();
mask = ModeMask[p];
shiftbit = p * 2;
+ spin_lock_irq(&info->slock);
val = inb(info->opmode_ioaddr);
val &= mask;
val |= (opmode << shiftbit);
outb(val, info->opmode_ioaddr);
- unlock_kernel();
+ spin_unlock_irq(&info->slock);
} else {
- lock_kernel();
shiftbit = p * 2;
+ spin_lock_irq(&info->slock);
opmode = inb(info->opmode_ioaddr) >> shiftbit;
+ spin_unlock_irq(&info->slock);
opmode &= OP_MODE_MASK;
- unlock_kernel();
if (put_user(opmode, (int __user *)argp))
return -EFAULT;
}
@@ -1742,14 +1706,14 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
switch (cmd) {
case TIOCGSERIAL:
- lock_kernel();
+ mutex_lock(&port->mutex);
retval = mxser_get_serial_info(tty, argp);
- unlock_kernel();
+ mutex_unlock(&port->mutex);
return retval;
case TIOCSSERIAL:
- lock_kernel();
+ mutex_lock(&port->mutex);
retval = mxser_set_serial_info(tty, argp);
- unlock_kernel();
+ mutex_unlock(&port->mutex);
return retval;
case TIOCSERGETLSR: /* Get line status register */
return mxser_get_lsr_info(info, argp);
@@ -1795,31 +1759,33 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
case MOXA_HighSpeedOn:
return put_user(info->baud_base != 115200 ? 1 : 0, (int __user *)argp);
case MOXA_SDS_RSTICOUNTER:
- lock_kernel();
+ spin_lock_irq(&info->slock);
info->mon_data.rxcnt = 0;
info->mon_data.txcnt = 0;
- unlock_kernel();
+ spin_unlock_irq(&info->slock);
return 0;
case MOXA_ASPP_OQUEUE:{
int len, lsr;
- lock_kernel();
len = mxser_chars_in_buffer(tty);
+ spin_lock(&info->slock);
lsr = inb(info->ioaddr + UART_LSR) & UART_LSR_THRE;
+ spin_unlock_irq(&info->slock);
len += (lsr ? 0 : 1);
- unlock_kernel();
return put_user(len, (int __user *)argp);
}
case MOXA_ASPP_MON: {
int mcr, status;
- lock_kernel();
+ spin_lock(&info->slock);
status = mxser_get_msr(info->ioaddr, 1, tty->index);
mxser_check_modem_status(tty, info, status);
mcr = inb(info->ioaddr + UART_MCR);
+ spin_unlock(&info->slock);
+
if (mcr & MOXA_MUST_MCR_XON_FLAG)
info->mon_data.hold_reason &= ~NPPI_NOTIFY_XOFFHOLD;
else
@@ -1834,7 +1800,7 @@ static int mxser_ioctl(struct tty_struct *tty, struct file *file,
info->mon_data.hold_reason |= NPPI_NOTIFY_CTSHOLD;
else
info->mon_data.hold_reason &= ~NPPI_NOTIFY_CTSHOLD;
- unlock_kernel();
+
if (copy_to_user(argp, &info->mon_data,
sizeof(struct mxser_mon)))
return -EFAULT;
@@ -1993,6 +1959,7 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
{
struct mxser_port *info = tty->driver_data;
unsigned long orig_jiffies, char_time;
+ unsigned long flags;
int lsr;
if (info->type == PORT_UNKNOWN)
@@ -2032,19 +1999,21 @@ static void mxser_wait_until_sent(struct tty_struct *tty, int timeout)
timeout, char_time);
printk("jiff=%lu...", jiffies);
#endif
- lock_kernel();
+ spin_lock_irqsave(&info->slock, flags);
while (!((lsr = inb(info->ioaddr + UART_LSR)) & UART_LSR_TEMT)) {
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...", lsr, jiffies);
#endif
+ spin_unlock_irqrestore(&info->slock, flags);
schedule_timeout_interruptible(char_time);
+ spin_lock_irqsave(&info->slock, flags);
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
}
+ spin_unlock_irqrestore(&info->slock, flags);
set_current_state(TASK_RUNNING);
- unlock_kernel();
#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);
@@ -2059,7 +2028,6 @@ static void mxser_hangup(struct tty_struct *tty)
struct mxser_port *info = tty->driver_data;
mxser_flush_buffer(tty);
- mxser_shutdown(tty);
tty_port_hangup(&info->port);
}
@@ -2363,6 +2331,8 @@ static const struct tty_operations mxser_ops = {
struct tty_port_operations mxser_port_ops = {
.carrier_raised = mxser_carrier_raised,
.dtr_rts = mxser_dtr_rts,
+ .activate = mxser_activate,
+ .shutdown = mxser_shutdown_port,
};
/*
diff --git a/drivers/char/pcmcia/ipwireless/tty.c b/drivers/char/pcmcia/ipwireless/tty.c
index 674b3ab3587..2bb7874a689 100644
--- a/drivers/char/pcmcia/ipwireless/tty.c
+++ b/drivers/char/pcmcia/ipwireless/tty.c
@@ -603,7 +603,7 @@ void ipwireless_tty_free(struct ipw_tty *tty)
}
}
-static struct tty_operations tty_ops = {
+static const struct tty_operations tty_ops = {
.open = ipw_open,
.close = ipw_close,
.hangup = ipw_hangup,
diff --git a/drivers/char/pty.c b/drivers/char/pty.c
index d86c0bc05c1..385c44b3034 100644
--- a/drivers/char/pty.c
+++ b/drivers/char/pty.c
@@ -659,7 +659,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
if (!retval)
return 0;
out1:
- tty_release_dev(filp);
+ tty_release(inode, filp);
return retval;
out:
devpts_kill_index(inode, index);
diff --git a/drivers/char/riscom8.c b/drivers/char/riscom8.c
index 3cfa22d469e..0a8d1e56c99 100644
--- a/drivers/char/riscom8.c
+++ b/drivers/char/riscom8.c
@@ -793,26 +793,21 @@ static void rc_change_speed(struct tty_struct *tty, struct riscom_board *bp,
}
/* Must be called with interrupts enabled */
-static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp,
- struct riscom_port *port)
+static int rc_activate_port(struct tty_port *port, struct tty_struct *tty)
{
+ struct riscom_port *rp = container_of(port, struct riscom_port, port);
+ struct riscom_board *bp = port_Board(rp);
unsigned long flags;
- if (port->port.flags & ASYNC_INITIALIZED)
- return 0;
-
- if (tty_port_alloc_xmit_buf(&port->port) < 0)
+ if (tty_port_alloc_xmit_buf(port) < 0)
return -ENOMEM;
spin_lock_irqsave(&riscom_lock, flags);
clear_bit(TTY_IO_ERROR, &tty->flags);
- if (port->port.count == 1)
- bp->count++;
- port->xmit_cnt = port->xmit_head = port->xmit_tail = 0;
- rc_change_speed(tty, bp, port);
- port->port.flags |= ASYNC_INITIALIZED;
-
+ bp->count++;
+ rp->xmit_cnt = rp->xmit_head = rp->xmit_tail = 0;
+ rc_change_speed(tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
return 0;
}
@@ -821,9 +816,6 @@ static int rc_setup_port(struct tty_struct *tty, struct riscom_board *bp,
static void rc_shutdown_port(struct tty_struct *tty,
struct riscom_board *bp, struct riscom_port *port)
{
- if (!(port->port.flags & ASYNC_INITIALIZED))
- return;
-
#ifdef RC_REPORT_OVERRUN
printk(KERN_INFO "rc%d: port %d: Total %ld overruns were detected.\n",
board_No(bp), port_No(port), port->overrun);
@@ -840,11 +832,6 @@ static void rc_shutdown_port(struct tty_struct *tty,
}
#endif
tty_port_free_xmit_buf(&port->port);
- if (C_HUPCL(tty)) {
- /* Drop DTR */
- bp->DTR |= (1u << port_No(port));
- rc_out(bp, RC_DTR, bp->DTR);
- }
/* Select port */
rc_out(bp, CD180_CAR, port_No(port));
@@ -856,7 +843,6 @@ static void rc_shutdown_port(struct tty_struct *tty,
rc_out(bp, CD180_IER, port->IER);
set_bit(TTY_IO_ERROR, &tty->flags);
- port->port.flags &= ~ASYNC_INITIALIZED;
if (--bp->count < 0) {
printk(KERN_INFO "rc%d: rc_shutdown_port: "
@@ -889,6 +875,20 @@ static int carrier_raised(struct tty_port *port)
return CD;
}
+static void dtr_rts(struct tty_port *port, int onoff)
+{
+ struct riscom_port *p = container_of(port, struct riscom_port, port);
+ struct riscom_board *bp = port_Board(p);
+ unsigned long flags;
+
+ spin_lock_irqsave(&riscom_lock, flags);
+ bp->DTR &= ~(1u << port_No(p));
+ if (onoff == 0)
+ bp->DTR |= (1u << port_No(p));
+ rc_out(bp, RC_DTR, bp->DTR);
+ spin_unlock_irqrestore(&riscom_lock, flags);
+}
+
static int rc_open(struct tty_struct *tty, struct file *filp)
{
int board;
@@ -909,14 +909,7 @@ static int rc_open(struct tty_struct *tty, struct file *filp)
if (error)
return error;
- port->port.count++;
- tty->driver_data = port;
- tty_port_tty_set(&port->port, tty);
-
- error = rc_setup_port(tty, bp, port);
- if (error == 0)
- error = tty_port_block_til_ready(&port->port, tty, filp);
- return error;
+ return tty_port_open(&port->port, tty, filp);
}
static void rc_flush_buffer(struct tty_struct *tty)
@@ -950,24 +943,23 @@ static void rc_close_port(struct tty_port *port)
spin_lock_irqsave(&riscom_lock, flags);
rp->IER &= ~IER_RXD;
- if (port->flags & ASYNC_INITIALIZED) {
- rp->IER &= ~IER_TXRDY;
- rp->IER |= IER_TXEMPTY;
- rc_out(bp, CD180_CAR, port_No(rp));
- rc_out(bp, CD180_IER, rp->IER);
- /*
- * Before we drop DTR, make sure the UART transmitter
- * has completely drained; this is especially
- * important if there is a transmit FIFO!
- */
- timeout = jiffies + HZ;
- while (rp->IER & IER_TXEMPTY) {
- spin_unlock_irqrestore(&riscom_lock, flags);
- msleep_interruptible(jiffies_to_msecs(rp->timeout));
- spin_lock_irqsave(&riscom_lock, flags);
- if (time_after(jiffies, timeout))
- break;
- }
+
+ rp->IER &= ~IER_TXRDY;
+ rp->IER |= IER_TXEMPTY;
+ rc_out(bp, CD180_CAR, port_No(rp));
+ rc_out(bp, CD180_IER, rp->IER);
+ /*
+ * Before we drop DTR, make sure the UART transmitter
+ * has completely drained; this is especially
+ * important if there is a transmit FIFO!
+ */
+ timeout = jiffies + HZ;
+ while (rp->IER & IER_TXEMPTY) {
+ spin_unlock_irqrestore(&riscom_lock, flags);
+ msleep_interruptible(jiffies_to_msecs(rp->timeout));
+ spin_lock_irqsave(&riscom_lock, flags);
+ if (time_after(jiffies, timeout))
+ break;
}
rc_shutdown_port(port->tty, bp, rp);
spin_unlock_irqrestore(&riscom_lock, flags);
@@ -1354,7 +1346,6 @@ static void rc_hangup(struct tty_struct *tty)
if (rc_paranoia_check(port, tty->name, "rc_hangup"))
return;
- rc_shutdown_port(tty, port_Board(port), port);
tty_port_hangup(&port->port);
}
@@ -1401,7 +1392,9 @@ static const struct tty_operations riscom_ops = {
static const struct tty_port_operations riscom_port_ops = {
.carrier_raised = carrier_raised,
+ .dtr_rts = dtr_rts,
.shutdown = rc_close_port,
+ .activate = rc_activate_port,
};
diff --git a/drivers/char/stallion.c b/drivers/char/stallion.c
index db6dcfa35ba..0e511d61f54 100644
--- a/drivers/char/stallion.c
+++ b/drivers/char/stallion.c
@@ -407,7 +407,7 @@ static unsigned int stl_baudrates[] = {
* Declare all those functions in this driver!
*/
-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg);
+static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg);
static int stl_brdinit(struct stlbrd *brdp);
static int stl_getportstats(struct tty_struct *tty, struct stlport *portp, comstats_t __user *cp);
static int stl_clrportstats(struct stlport *portp, comstats_t __user *cp);
@@ -607,7 +607,7 @@ static unsigned int sc26198_baudtable[] = {
*/
static const struct file_operations stl_fsiomem = {
.owner = THIS_MODULE,
- .ioctl = stl_memioctl,
+ .unlocked_ioctl = stl_memioctl,
};
static struct class *stallion_class;
@@ -702,6 +702,24 @@ static struct stlbrd *stl_allocbrd(void)
/*****************************************************************************/
+static int stl_activate(struct tty_port *port, struct tty_struct *tty)
+{
+ struct stlport *portp = container_of(port, struct stlport, port);
+ if (!portp->tx.buf) {
+ portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
+ if (!portp->tx.buf)
+ return -ENOMEM;
+ portp->tx.head = portp->tx.buf;
+ portp->tx.tail = portp->tx.buf;
+ }
+ stl_setport(portp, tty->termios);
+ portp->sigs = stl_getsignals(portp);
+ stl_setsignals(portp, 1, 1);
+ stl_enablerxtx(portp, 1, 1);
+ stl_startrxtx(portp, 1, 0);
+ return 0;
+}
+
static int stl_open(struct tty_struct *tty, struct file *filp)
{
struct stlport *portp;
@@ -737,32 +755,8 @@ static int stl_open(struct tty_struct *tty, struct file *filp)
if (portp == NULL)
return -ENODEV;
port = &portp->port;
+ return tty_port_open(&portp->port, tty, filp);
-/*
- * On the first open of the device setup the port hardware, and
- * initialize the per port data structure.
- */
- tty_port_tty_set(port, tty);
- tty->driver_data = portp;
- port->count++;
-
- if ((port->flags & ASYNC_INITIALIZED) == 0) {
- if (!portp->tx.buf) {
- portp->tx.buf = kmalloc(STL_TXBUFSIZE, GFP_KERNEL);
- if (!portp->tx.buf)
- return -ENOMEM;
- portp->tx.head = portp->tx.buf;
- portp->tx.tail = portp->tx.buf;
- }
- stl_setport(portp, tty->termios);
- portp->sigs = stl_getsignals(portp);
- stl_setsignals(portp, 1, 1);
- stl_enablerxtx(portp, 1, 1);
- stl_startrxtx(portp, 1, 0);
- clear_bit(TTY_IO_ERROR, &tty->flags);
- port->flags |= ASYNC_INITIALIZED;
- }
- return tty_port_block_til_ready(port, tty, filp);
}
/*****************************************************************************/
@@ -826,38 +820,12 @@ static void stl_waituntilsent(struct tty_struct *tty, int timeout)
/*****************************************************************************/
-static void stl_close(struct tty_struct *tty, struct file *filp)
+static void stl_shutdown(struct tty_port *port)
{
- struct stlport *portp;
- struct tty_port *port;
- unsigned long flags;
-
- pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
-
- portp = tty->driver_data;
- BUG_ON(portp == NULL);
-
- port = &portp->port;
-
- if (tty_port_close_start(port, tty, filp) == 0)
- return;
-/*
- * May want to wait for any data to drain before closing. The BUSY
- * flag keeps track of whether we are still sending or not - it is
- * very accurate for the cd1400, not quite so for the sc26198.
- * (The sc26198 has no "end-of-data" interrupt only empty FIFO)
- */
- stl_waituntilsent(tty, (HZ / 2));
-
- spin_lock_irqsave(&port->lock, flags);
- portp->port.flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
+ struct stlport *portp = container_of(port, struct stlport, port);
stl_disableintrs(portp);
- if (tty->termios->c_cflag & HUPCL)
- stl_setsignals(portp, 0, 0);
stl_enablerxtx(portp, 0, 0);
- stl_flushbuffer(tty);
+ stl_flush(portp);
portp->istate = 0;
if (portp->tx.buf != NULL) {
kfree(portp->tx.buf);
@@ -865,9 +833,16 @@ static void stl_close(struct tty_struct *tty, struct file *filp)
portp->tx.head = NULL;
portp->tx.tail = NULL;
}
+}
+
+static void stl_close(struct tty_struct *tty, struct file *filp)
+{
+ struct stlport*portp;
+ pr_debug("stl_close(tty=%p,filp=%p)\n", tty, filp);
- tty_port_close_end(port, tty);
- tty_port_tty_set(port, NULL);
+ portp = tty->driver_data;
+ BUG_ON(portp == NULL);
+ tty_port_close(&portp->port, tty, filp);
}
/*****************************************************************************/
@@ -1314,35 +1289,12 @@ static void stl_stop(struct tty_struct *tty)
static void stl_hangup(struct tty_struct *tty)
{
- struct stlport *portp;
- struct tty_port *port;
- unsigned long flags;
-
+ struct stlport *portp = tty->driver_data;
pr_debug("stl_hangup(tty=%p)\n", tty);
- portp = tty->driver_data;
if (portp == NULL)
return;
- port = &portp->port;
-
- spin_lock_irqsave(&port->lock, flags);
- port->flags &= ~ASYNC_INITIALIZED;
- spin_unlock_irqrestore(&port->lock, flags);
-
- stl_disableintrs(portp);
- if (tty->termios->c_cflag & HUPCL)
- stl_setsignals(portp, 0, 0);
- stl_enablerxtx(portp, 0, 0);
- stl_flushbuffer(tty);
- portp->istate = 0;
- set_bit(TTY_IO_ERROR, &tty->flags);
- if (portp->tx.buf != NULL) {
- kfree(portp->tx.buf);
- portp->tx.buf = NULL;
- portp->tx.head = NULL;
- portp->tx.tail = NULL;
- }
- tty_port_hangup(port);
+ tty_port_hangup(&portp->port);
}
/*****************************************************************************/
@@ -2486,18 +2438,19 @@ static int stl_getbrdstruct(struct stlbrd __user *arg)
* collection.
*/
-static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, unsigned long arg)
+static long stl_memioctl(struct file *fp, unsigned int cmd, unsigned long arg)
{
int brdnr, rc;
void __user *argp = (void __user *)arg;
- pr_debug("stl_memioctl(ip=%p,fp=%p,cmd=%x,arg=%lx)\n", ip, fp, cmd,arg);
+ pr_debug("stl_memioctl(fp=%p,cmd=%x,arg=%lx)\n", fp, cmd,arg);
- brdnr = iminor(ip);
+ brdnr = iminor(fp->f_dentry->d_inode);
if (brdnr >= STL_MAXBRDS)
return -ENODEV;
rc = 0;
+ lock_kernel();
switch (cmd) {
case COM_GETPORTSTATS:
rc = stl_getportstats(NULL, NULL, argp);
@@ -2518,7 +2471,7 @@ static int stl_memioctl(struct inode *ip, struct file *fp, unsigned int cmd, uns
rc = -ENOIOCTLCMD;
break;
}
-
+ unlock_kernel();
return rc;
}
@@ -2549,6 +2502,8 @@ static const struct tty_operations stl_ops = {
static const struct tty_port_operations stl_port_ops = {
.carrier_raised = stl_carrier_raised,
.dtr_rts = stl_dtr_rts,
+ .activate = stl_activate,
+ .shutdown = stl_shutdown,
};
/*****************************************************************************/
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c
index 59499ee0fe6..684f0e0b175 100644
--- a/drivers/char/tty_io.c
+++ b/drivers/char/tty_io.c
@@ -142,7 +142,6 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
size_t, loff_t *);
static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *);
-static int tty_release(struct inode *, struct file *);
long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd,
@@ -506,8 +505,6 @@ static void do_tty_hangup(struct work_struct *work)
if (!tty)
return;
- /* inuse_filps is protected by the single kernel lock */
- lock_kernel();
spin_lock(&redirect_lock);
if (redirect && redirect->private_data == tty) {
@@ -516,7 +513,11 @@ static void do_tty_hangup(struct work_struct *work)
}
spin_unlock(&redirect_lock);
+ /* inuse_filps is protected by the single kernel lock */
+ lock_kernel();
check_tty_count(tty, "do_tty_hangup");
+ unlock_kernel();
+
file_list_lock();
/* This breaks for file handles being sent over AF_UNIX sockets ? */
list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
@@ -530,6 +531,7 @@ static void do_tty_hangup(struct work_struct *work)
}
file_list_unlock();
+ lock_kernel();
tty_ldisc_hangup(tty);
read_lock(&tasklist_lock);
@@ -708,6 +710,8 @@ void disassociate_ctty(int on_exit)
struct tty_struct *tty;
struct pid *tty_pgrp = NULL;
+ if (!current->signal->leader)
+ return;
tty = get_current_tty();
if (tty) {
@@ -773,8 +777,7 @@ void no_tty(void)
{
struct task_struct *tsk = current;
lock_kernel();
- if (tsk->signal->leader)
- disassociate_ctty(0);
+ disassociate_ctty(0);
unlock_kernel();
proc_clear_tty(tsk);
}
@@ -1017,14 +1020,16 @@ out:
void tty_write_message(struct tty_struct *tty, char *msg)
{
- lock_kernel();
if (tty) {
mutex_lock(&tty->atomic_write_lock);
- if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+ lock_kernel();
+ if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+ unlock_kernel();
tty->ops->write(tty, msg, strlen(msg));
+ } else
+ unlock_kernel();
tty_write_unlock(tty);
}
- unlock_kernel();
return;
}
@@ -1202,14 +1207,21 @@ static int tty_driver_install_tty(struct tty_driver *driver,
struct tty_struct *tty)
{
int idx = tty->index;
+ int ret;
- if (driver->ops->install)
- return driver->ops->install(driver, tty);
+ if (driver->ops->install) {
+ lock_kernel();
+ ret = driver->ops->install(driver, tty);
+ unlock_kernel();
+ return ret;
+ }
if (tty_init_termios(tty) == 0) {
+ lock_kernel();
tty_driver_kref_get(driver);
tty->count++;
driver->ttys[idx] = tty;
+ unlock_kernel();
return 0;
}
return -ENOMEM;
@@ -1302,10 +1314,14 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
struct tty_struct *tty;
int retval;
+ lock_kernel();
/* Check if pty master is being opened multiple times */
if (driver->subtype == PTY_TYPE_MASTER &&
- (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
+ (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+ unlock_kernel();
return ERR_PTR(-EIO);
+ }
+ unlock_kernel();
/*
* First time open is complex, especially for PTY devices.
@@ -1335,7 +1351,6 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
* If we fail here just call release_tty to clean up. No need
* to decrement the use counts, as release_tty doesn't care.
*/
-
retval = tty_ldisc_setup(tty, tty->link);
if (retval)
goto release_mem_out;
@@ -1350,7 +1365,9 @@ release_mem_out:
if (printk_ratelimit())
printk(KERN_INFO "tty_init_dev: ldisc open failed, "
"clearing slot %d\n", idx);
+ lock_kernel();
release_tty(tty, idx);
+ unlock_kernel();
return ERR_PTR(retval);
}
@@ -1464,7 +1481,17 @@ static void release_tty(struct tty_struct *tty, int idx)
tty_kref_put(tty);
}
-/*
+/**
+ * tty_release - vfs callback for close
+ * @inode: inode of tty
+ * @filp: file pointer for handle to tty
+ *
+ * Called the last time each file handle is closed that references
+ * this tty. There may however be several such references.
+ *
+ * Locking:
+ * Takes bkl. See tty_release_dev
+ *
* Even releasing the tty structures is a tricky business.. We have
* to be very careful that the structures are all released at the
* same time, as interrupts might otherwise get the wrong pointers.
@@ -1472,20 +1499,20 @@ static void release_tty(struct tty_struct *tty, int idx)
* WSH 09/09/97: rewritten to avoid some nasty race conditions that could
* lead to double frees or releasing memory still in use.
*/
-void tty_release_dev(struct file *filp)
+
+int tty_release(struct inode *inode, struct file *filp)
{
struct tty_struct *tty, *o_tty;
int pty_master, tty_closing, o_tty_closing, do_sleep;
int devpts;
int idx;
char buf[64];
- struct inode *inode;
- inode = filp->f_path.dentry->d_inode;
tty = (struct tty_struct *)filp->private_data;
if (tty_paranoia_check(tty, inode, "tty_release_dev"))
- return;
+ return 0;
+ lock_kernel();
check_tty_count(tty, "tty_release_dev");
tty_fasync(-1, filp, 0);
@@ -1500,19 +1527,22 @@ void tty_release_dev(struct file *filp)
if (idx < 0 || idx >= tty->driver->num) {
printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
"free (%s)\n", tty->name);
- return;
+ unlock_kernel();
+ return 0;
}
if (!devpts) {
if (tty != tty->driver->ttys[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
"for (%s)\n", idx, tty->name);
- return;
+ return 0;
}
if (tty->termios != tty->driver->termios[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
"for (%s)\n",
idx, tty->name);
- return;
+ return 0;
}
}
#endif
@@ -1526,26 +1556,30 @@ void tty_release_dev(struct file *filp)
if (tty->driver->other &&
!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
if (o_tty != tty->driver->other->ttys[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
"not o_tty for (%s)\n",
idx, tty->name);
- return;
+ return 0 ;
}
if (o_tty->termios != tty->driver->other->termios[idx]) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
"not o_termios for (%s)\n",
idx, tty->name);
- return;
+ return 0;
}
if (o_tty->link != tty) {
+ unlock_kernel();
printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
- return;
+ return 0;
}
}
#endif
if (tty->ops->close)
tty->ops->close(tty, filp);
+ unlock_kernel();
/*
* Sanity check: if tty->count is going to zero, there shouldn't be
* any waiters on tty->read_wait or tty->write_wait. We test the
@@ -1568,6 +1602,7 @@ void tty_release_dev(struct file *filp)
opens on /dev/tty */
mutex_lock(&tty_mutex);
+ lock_kernel();
tty_closing = tty->count <= 1;
o_tty_closing = o_tty &&
(o_tty->count <= (pty_master ? 1 : 0));
@@ -1598,6 +1633,7 @@ void tty_release_dev(struct file *filp)
printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
"active!\n", tty_name(tty, buf));
+ unlock_kernel();
mutex_unlock(&tty_mutex);
schedule();
}
@@ -1661,8 +1697,10 @@ void tty_release_dev(struct file *filp)
mutex_unlock(&tty_mutex);
/* check whether both sides are closing ... */
- if (!tty_closing || (o_tty && !o_tty_closing))
- return;
+ if (!tty_closing || (o_tty && !o_tty_closing)) {
+ unlock_kernel();
+ return 0;
+ }
#ifdef TTY_DEBUG_HANGUP
printk(KERN_DEBUG "freeing tty structure...");
@@ -1680,10 +1718,12 @@ void tty_release_dev(struct file *filp)
/* Make this pty number available for reallocation */
if (devpts)
devpts_kill_index(inode, idx);
+ unlock_kernel();
+ return 0;
}
/**
- * __tty_open - open a tty device
+ * tty_open - open a tty device
* @inode: inode of device file
* @filp: file pointer to tty
*
@@ -1703,7 +1743,7 @@ void tty_release_dev(struct file *filp)
* ->siglock protects ->signal/->sighand
*/
-static int __tty_open(struct inode *inode, struct file *filp)
+static int tty_open(struct inode *inode, struct file *filp)
{
struct tty_struct *tty = NULL;
int noctty, retval;
@@ -1720,10 +1760,12 @@ retry_open:
retval = 0;
mutex_lock(&tty_mutex);
+ lock_kernel();
if (device == MKDEV(TTYAUX_MAJOR, 0)) {
tty = get_current_tty();
if (!tty) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENXIO;
}
@@ -1755,12 +1797,14 @@ retry_open:
goto got_driver;
}
}
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
driver = get_tty_driver(device, &index);
if (!driver) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return -ENODEV;
}
@@ -1770,6 +1814,7 @@ got_driver:
tty = tty_driver_lookup_tty(driver, inode, index);
if (IS_ERR(tty)) {
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return PTR_ERR(tty);
}
@@ -1784,8 +1829,10 @@ got_driver:
mutex_unlock(&tty_mutex);
tty_driver_kref_put(driver);
- if (IS_ERR(tty))
+ if (IS_ERR(tty)) {
+ unlock_kernel();
return PTR_ERR(tty);
+ }
filp->private_data = tty;
file_move(filp, &tty->tty_files);
@@ -1813,11 +1860,15 @@ got_driver:
printk(KERN_DEBUG "error %d in opening %s...", retval,
tty->name);
#endif
- tty_release_dev(filp);
- if (retval != -ERESTARTSYS)
+ tty_release(inode, filp);
+ if (retval != -ERESTARTSYS) {
+ unlock_kernel();
return retval;
- if (signal_pending(current))
+ }
+ if (signal_pending(current)) {
+ unlock_kernel();
return retval;
+ }
schedule();
/*
* Need to reset f_op in case a hangup happened.
@@ -1826,8 +1877,11 @@ got_driver:
filp->f_op = &tty_fops;
goto retry_open;
}
+ unlock_kernel();
+
mutex_lock(&tty_mutex);
+ lock_kernel();
spin_lock_irq(&current->sighand->siglock);
if (!noctty &&
current->signal->leader &&
@@ -1835,43 +1889,12 @@ got_driver:
tty->session == NULL)
__proc_set_tty(current, tty);
spin_unlock_irq(&current->sighand->siglock);
+ unlock_kernel();
mutex_unlock(&tty_mutex);
return 0;
}
-/* BKL pushdown: scary code avoidance wrapper */
-static int tty_open(struct inode *inode, struct file *filp)
-{
- int ret;
-
- lock_kernel();
- ret = __tty_open(inode, filp);
- unlock_kernel();
- return ret;
-}
-
-
-
-
-/**
- * tty_release - vfs callback for close
- * @inode: inode of tty
- * @filp: file pointer for handle to tty
- *
- * Called the last time each file handle is closed that references
- * this tty. There may however be several such references.
- *
- * Locking:
- * Takes bkl. See tty_release_dev
- */
-static int tty_release(struct inode *inode, struct file *filp)
-{
- lock_kernel();
- tty_release_dev(filp);
- unlock_kernel();
- return 0;
-}
/**
* tty_poll - check tty status
@@ -2317,9 +2340,7 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
if (get_user(ldisc, p))
return -EFAULT;
- lock_kernel();
ret = tty_set_ldisc(tty, ldisc);
- unlock_kernel();
return ret;
}
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index feb55075819..3f653f7d849 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -34,6 +34,8 @@
#include <linux/vt_kern.h>
#include <linux/selection.h>
+#include <linux/smp_lock.h> /* For the moment */
+
#include <linux/kmod.h>
#include <linux/nsproxy.h>
@@ -443,8 +445,14 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
{
WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
- if (ld->ops->open)
- return ld->ops->open(tty);
+ if (ld->ops->open) {
+ int ret;
+ /* BKL here locks verus a hangup event */
+ lock_kernel();
+ ret = ld->ops->open(tty);
+ unlock_kernel();
+ return ret;
+ }
return 0;
}
@@ -545,6 +553,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);
+ lock_kernel();
/*
* We need to look at the tty locking here for pty/tty pairs
* when both sides try to change in parallel.
@@ -558,10 +567,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
*/
if (tty->ldisc->ops->num == ldisc) {
+ unlock_kernel();
tty_ldisc_put(new_ldisc);
return 0;
}
+ unlock_kernel();
/*
* Problem: What do we do if this blocks ?
* We could deadlock here
@@ -582,6 +593,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
mutex_lock(&tty->ldisc_mutex);
}
+
+ lock_kernel();
+
set_bit(TTY_LDISC_CHANGING, &tty->flags);
/*
@@ -592,6 +606,8 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
tty->receive_room = 0;
o_ldisc = tty->ldisc;
+
+ unlock_kernel();
/*
* Make sure we don't change while someone holds a
* reference to the line discipline. The TTY_LDISC bit
@@ -617,12 +633,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
flush_scheduled_work();
mutex_lock(&tty->ldisc_mutex);
+ lock_kernel();
if (test_bit(TTY_HUPPED, &tty->flags)) {
/* We were raced by the hangup method. It will have stomped
the ldisc data and closed the ldisc down */
clear_bit(TTY_LDISC_CHANGING, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_ldisc_put(new_ldisc);
+ unlock_kernel();
return -EIO;
}
@@ -664,6 +682,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
if (o_work)
schedule_delayed_work(&o_tty->buf.work, 1);
mutex_unlock(&tty->ldisc_mutex);
+ unlock_kernel();
return retval;
}
diff --git a/drivers/char/tty_port.c b/drivers/char/tty_port.c
index c63f3d33914..be492dd6643 100644
--- a/drivers/char/tty_port.c
+++ b/drivers/char/tty_port.c
@@ -25,19 +25,21 @@ void tty_port_init(struct tty_port *port)
init_waitqueue_head(&port->close_wait);
init_waitqueue_head(&port->delta_msr_wait);
mutex_init(&port->mutex);
+ mutex_init(&port->buf_mutex);
spin_lock_init(&port->lock);
port->close_delay = (50 * HZ) / 100;
port->closing_wait = (3000 * HZ) / 100;
+ kref_init(&port->kref);
}
EXPORT_SYMBOL(tty_port_init);
int tty_port_alloc_xmit_buf(struct tty_port *port)
{
/* We may sleep in get_zeroed_page() */
- mutex_lock(&port->mutex);
+ mutex_lock(&port->buf_mutex);
if (port->xmit_buf == NULL)
port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
- mutex_unlock(&port->mutex);
+ mutex_unlock(&port->buf_mutex);
if (port->xmit_buf == NULL)
return -ENOMEM;
return 0;
@@ -46,15 +48,32 @@ EXPORT_SYMBOL(tty_port_alloc_xmit_buf);
void tty_port_free_xmit_buf(struct tty_port *port)
{
- mutex_lock(&port->mutex);
+ mutex_lock(&port->buf_mutex);
if (port->xmit_buf != NULL) {
free_page((unsigned long)port->xmit_buf);
port->xmit_buf = NULL;
}
- mutex_unlock(&port->mutex);
+ mutex_unlock(&port->buf_mutex);
}
EXPORT_SYMBOL(tty_port_free_xmit_buf);
+static void tty_port_destructor(struct kref *kref)
+{
+ struct tty_port *port = container_of(kref, struct tty_port, kref);
+ if (port->xmit_buf)
+ free_page((unsigned long)port->xmit_buf);
+ if (port->ops->destruct)
+ port->ops->destruct(port);
+ else
+ kfree(port);
+}
+
+void tty_port_put(struct tty_port *port)
+{
+ if (port)
+ kref_put(&port->kref, tty_port_destructor);
+}
+EXPORT_SYMBOL(tty_port_put);
/**
* tty_port_tty_get - get a tty reference
@@ -99,10 +118,11 @@ EXPORT_SYMBOL(tty_port_tty_set);
static void tty_port_shutdown(struct tty_port *port)
{
+ mutex_lock(&port->mutex);
if (port->ops->shutdown &&
test_and_clear_bit(ASYNCB_INITIALIZED, &port->flags))
port->ops->shutdown(port);
-
+ mutex_unlock(&port->mutex);
}
/**
@@ -120,8 +140,10 @@ void tty_port_hangup(struct tty_port *port)
spin_lock_irqsave(&port->lock, flags);
port->count = 0;
port->flags &= ~ASYNC_NORMAL_ACTIVE;
- if (port->tty)
+ if (port->tty) {
+ set_bit(TTY_IO_ERROR, &port->tty->flags);
tty_kref_put(port->tty);
+ }
port->tty = NULL;
spin_unlock_irqrestore(&port->lock, flags);
wake_up_interruptible(&port->open_wait);
@@ -198,7 +220,7 @@ EXPORT_SYMBOL(tty_port_lower_dtr_rts);
* management of these lines. Note that the dtr/rts raise is done each
* iteration as a hangup may have previously dropped them while we wait.
*/
-
+
int tty_port_block_til_ready(struct tty_port *port,
struct tty_struct *tty, struct file *filp)
{
@@ -253,7 +275,8 @@ int tty_port_block_til_ready(struct tty_port *port,
tty_port_raise_dtr_rts(port);
prepare_to_wait(&port->open_wait, &wait, TASK_INTERRUPTIBLE);
- /* Check for a hangup or uninitialised port. Return accordingly */
+ /* Check for a hangup or uninitialised port.
+ Return accordingly */
if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) {
if (port->flags & ASYNC_HUP_NOTIFY)
retval = -EAGAIN;
@@ -285,11 +308,11 @@ int tty_port_block_til_ready(struct tty_port *port,
port->flags |= ASYNC_NORMAL_ACTIVE;
spin_unlock_irqrestore(&port->lock, flags);
return retval;
-
}
EXPORT_SYMBOL(tty_port_block_til_ready);
-int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct file *filp)
+int tty_port_close_start(struct tty_port *port,
+ struct tty_struct *tty, struct file *filp)
{
unsigned long flags;
@@ -299,7 +322,7 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
return 0;
}
- if( tty->count == 1 && port->count != 1) {
+ if (tty->count == 1 && port->count != 1) {
printk(KERN_WARNING
"tty_port_close_start: tty->count = 1 port count = %d.\n",
port->count);
@@ -331,12 +354,20 @@ int tty_port_close_start(struct tty_port *port, struct tty_struct *tty, struct f
long timeout;
if (bps > 1200)
- timeout = max_t(long, (HZ * 10 * port->drain_delay) / bps,
- HZ / 10);
+ timeout = max_t(long,
+ (HZ * 10 * port->drain_delay) / bps, HZ / 10);
else
timeout = 2 * HZ;
schedule_timeout_interruptible(timeout);
}
+ /* Flush the ldisc buffering */
+ tty_ldisc_flush(tty);
+
+ /* Drop DTR/RTS if HUPCL is set. This causes any attached modem to
+ hang up the line */
+ if (tty->termios->c_cflag & HUPCL)
+ tty_port_lower_dtr_rts(port);
+
/* Don't call port->drop for the last reference. Callers will want
to drop the last active reference in ->shutdown() or the tty
shutdown path */
@@ -348,11 +379,6 @@ void tty_port_close_end(struct tty_port *port, struct tty_struct *tty)
{
unsigned long flags;
- tty_ldisc_flush(tty);
-
- if (tty->termios->c_cflag & HUPCL)
- tty_port_lower_dtr_rts(port);
-
spin_lock_irqsave(&port->lock, flags);
tty->closing = 0;
@@ -377,7 +403,42 @@ void tty_port_close(struct tty_port *port, struct tty_struct *tty,
if (tty_port_close_start(port, tty, filp) == 0)
return;
tty_port_shutdown(port);
+ set_bit(TTY_IO_ERROR, &tty->flags);
tty_port_close_end(port, tty);
tty_port_tty_set(port, NULL);
}
EXPORT_SYMBOL(tty_port_close);
+
+int tty_port_open(struct tty_port *port, struct tty_struct *tty,
+ struct file *filp)
+{
+ spin_lock_irq(&port->lock);
+ if (!tty_hung_up_p(filp))
+ ++port->count;
+ spin_unlock_irq(&port->lock);
+ tty_port_tty_set(port, tty);
+
+ /*
+ * Do the device-specific open only if the hardware isn't
+ * already initialized. Serialize open and shutdown using the
+ * port mutex.
+ */
+
+ mutex_lock(&port->mutex);
+
+ if (!test_bit(ASYNCB_INITIALIZED, &port->flags)) {
+ clear_bit(TTY_IO_ERROR, &tty->flags);
+ if (port->ops->activate) {
+ int retval = port->ops->activate(port, tty);
+ if (retval) {
+ mutex_unlock(&port->mutex);
+ return retval;
+ }
+ }
+ set_bit(ASYNCB_INITIALIZED, &port->flags);
+ }
+ mutex_unlock(&port->mutex);
+ return tty_port_block_til_ready(port, tty, filp);
+}
+
+EXPORT_SYMBOL(tty_port_open);