*
* 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.
+ * Bernd Anh�pl 05/17/96.
*
* --- End of notices from serial.c ---
*
#include <linux/ioport.h>
#include <linux/mm.h>
#include <linux/init.h>
+#include <linux/delay.h>
#include <asm/system.h>
#include <asm/io.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 */
MODULE_LICENSE("GPL");
-MODULE_PARM(irq, "1-8i");
-MODULE_PARM(divisor, "1-8i");
-MODULE_PARM(dma, "i");
-MODULE_PARM(rx_trigger, "i");
-MODULE_PARM(tx_trigger, "i");
-MODULE_PARM(flow_off, "i");
-MODULE_PARM(flow_on, "i");
-MODULE_PARM(rx_timeout, "i");
-MODULE_PARM(pio_threshold, "i");
+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 */
#undef SERIAL_DEBUG_OPEN
#undef SERIAL_DEBUG_FLOW
-#define _INLINE_ inline
-
#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
tty->name, (info->flags), serial_driver.refcount,info->count,tty->count,s)
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).
/* Standard COM flags (except for COM4, because of the 8514 problem) */
#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
-#ifndef MIN
-#define MIN(a,b) ((a) < (b) ? (a) : (b))
-#endif
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write. We need to
- * lock it in case the memcpy_fromfs blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char *tmp_buf;
-static DECLARE_MUTEX(tmp_buf_sem);
-
static inline int serial_paranoia_check(struct esp_struct *info,
char *name, const char *routine)
{
if (serial_paranoia_check(info, tty->name, "rs_stop"))
return;
-
- save_flags(flags); cli();
+
+ 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);
}
-
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_start(struct tty_struct *tty)
if (serial_paranoia_check(info, tty->name, "rs_start"))
return;
- save_flags(flags); cli();
+ 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);
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/*
* This routine is used by the interrupt handler to schedule
* processing in the software interrupt portion of the driver.
*/
-static _INLINE_ void rs_sched_event(struct esp_struct *info,
+static inline void rs_sched_event(struct esp_struct *info,
int event)
{
info->event |= 1 << event;
schedule_work(&info->tqueue);
}
-static _INLINE_ struct esp_pio_buffer *get_pio_buffer(void)
+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)
+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)
+static inline void receive_chars_pio(struct esp_struct *info, int num_bytes)
{
struct tty_struct *tty = info->tty;
int i;
return;
}
- sti();
-
status_mask = (info->read_status_mask >> 2) & 0x07;
for (i = 0; i < num_bytes - 1; i += 2) {
(serial_in(info, UART_ESI_RWS) >> 3) & status_mask;
}
- cli();
-
/* make sure everything is still ok since interrupts were enabled */
tty = info->tty;
for (i = 0; i < num_bytes; i++) {
if (!(err_buf->data[i] & status_mask)) {
- *(tty->flip.char_buf_ptr++) = pio_buf->data[i];
+ int flag = 0;
if (err_buf->data[i] & 0x04) {
- *(tty->flip.flag_buf_ptr++) = TTY_BREAK;
-
+ flag = TTY_BREAK;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
}
else if (err_buf->data[i] & 0x02)
- *(tty->flip.flag_buf_ptr++) = TTY_FRAME;
+ flag = TTY_FRAME;
else if (err_buf->data[i] & 0x01)
- *(tty->flip.flag_buf_ptr++) = TTY_PARITY;
- else
- *(tty->flip.flag_buf_ptr++) = 0;
-
- tty->flip.count++;
+ flag = TTY_PARITY;
+ tty_insert_flip_char(tty, pio_buf->data[i], flag);
}
}
- schedule_delayed_work(&tty->flip.work, 1);
+ tty_schedule_flip(tty);
info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
release_pio_buffer(pio_buf);
release_pio_buffer(err_buf);
}
-static _INLINE_ void receive_chars_dma(struct esp_struct *info, int num_bytes)
+static inline void receive_chars_dma(struct esp_struct *info, int num_bytes)
{
unsigned long flags;
info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
}
-static _INLINE_ void receive_chars_dma_done(struct esp_struct *info,
+static inline void receive_chars_dma_done(struct esp_struct *info,
int status)
{
struct tty_struct *tty = info->tty;
int num_bytes;
unsigned long flags;
-
flags=claim_dma_lock();
disable_dma(dma);
clear_dma_ff(dma);
info->icount.rx += num_bytes;
- memcpy(tty->flip.char_buf_ptr, dma_buffer, num_bytes);
- tty->flip.char_buf_ptr += num_bytes;
- tty->flip.count += num_bytes;
- memset(tty->flip.flag_buf_ptr, 0, num_bytes);
- tty->flip.flag_buf_ptr += num_bytes;
-
if (num_bytes > 0) {
- tty->flip.flag_buf_ptr--;
+ 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 & info->ignore_status_mask) {
- tty->flip.count--;
- tty->flip.char_buf_ptr--;
- tty->flip.flag_buf_ptr--;
- } else if (status & 0x10) {
- *tty->flip.flag_buf_ptr = TTY_BREAK;
- (info->icount.brk)++;
- if (info->flags & ASYNC_SAK)
- do_SAK(tty);
- } else if (status & 0x08) {
- *tty->flip.flag_buf_ptr = TTY_FRAME;
- (info->icount.frame)++;
- }
- else if (status & 0x04) {
- *tty->flip.flag_buf_ptr = TTY_PARITY;
- (info->icount.parity)++;
+ if (status & 0x10) {
+ statflag = TTY_BREAK;
+ (info->icount.brk)++;
+ if (info->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->flip.flag_buf_ptr++;
-
- schedule_delayed_work(&tty->flip.work, 1);
+ tty_schedule_flip(tty);
}
if (dma_bytes != num_bytes) {
dma_bytes = 0;
}
-static _INLINE_ void transmit_chars_pio(struct esp_struct *info,
+/* Caller must hold info->lock */
+
+static inline void transmit_chars_pio(struct esp_struct *info,
int space_avail)
{
int i;
info->xmit_tail = (info->xmit_tail + space_avail) &
(ESP_XMIT_SIZE - 1);
- sti();
-
for (i = 0; i < space_avail - 1; i += 2) {
outw(*((unsigned short *)(pio_buf->data + i)),
info->port + UART_ESI_TX);
serial_out(info, UART_ESI_TX,
pio_buf->data[space_avail - 1]);
- cli();
-
if (info->xmit_cnt) {
serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
release_pio_buffer(pio_buf);
}
-static _INLINE_ void transmit_chars_dma(struct esp_struct *info, int num_bytes)
+/* Caller must hold info->lock */
+static inline void transmit_chars_dma(struct esp_struct *info, int num_bytes)
{
unsigned long flags;
serial_out(info, UART_ESI_CMD1, ESI_START_DMA_TX);
}
-static _INLINE_ void transmit_chars_dma_done(struct esp_struct *info)
+static inline void transmit_chars_dma_done(struct esp_struct *info)
{
int num_bytes;
unsigned long flags;
}
}
-static _INLINE_ void check_modem_status(struct esp_struct *info)
+static inline void check_modem_status(struct esp_struct *info)
{
int status;
err_status = 0;
scratch = serial_in(info, UART_ESI_SID);
- cli();
+ spin_lock(&info->lock);
if (!info->tty) {
- sti();
+ spin_unlock(&info->lock);
return IRQ_NONE;
}
num_bytes = serial_in(info, UART_ESI_STAT1) << 8;
num_bytes |= serial_in(info, UART_ESI_STAT2);
- if (num_bytes > (TTY_FLIPBUF_SIZE - info->tty->flip.count))
- num_bytes = TTY_FLIPBUF_SIZE - info->tty->flip.count;
+ num_bytes = tty_buffer_request_room(info->tty, num_bytes);
if (num_bytes) {
if (dma_bytes ||
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
- sti();
+ spin_unlock(&info->lock);
return IRQ_HANDLED;
}
return;
if (test_and_clear_bit(ESP_EVENT_WRITE_WAKEUP, &info->event)) {
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
- wake_up_interruptible(&tty->write_wait);
+ tty_wakeup(tty);
}
}
* 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 _INLINE_ void esp_basic_init(struct esp_struct * info)
+static inline void esp_basic_init(struct esp_struct * info)
{
/* put ESPC in enhanced mode */
serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
int retval=0;
unsigned int num_chars;
- save_flags(flags); cli();
+ spin_lock_irqsave(&info->lock, flags);
if (info->flags & ASYNC_INITIALIZED)
goto out;
if (!info->xmit_buf) {
- info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
+ info->xmit_buf = (unsigned char *)get_zeroed_page(GFP_ATOMIC);
retval = -ENOMEM;
if (!info->xmit_buf)
goto out;
if (info->stat_flags & ESP_STAT_NEVER_DMA)
info->stat_flags |= ESP_STAT_USE_PIO;
+ spin_unlock_irqrestore(&info->lock, flags);
+
/*
* Allocate the IRQ
*/
&info->tty->flags);
retval = 0;
}
- goto out;
+ goto out_unlocked;
}
if (!(info->stat_flags & ESP_STAT_USE_PIO) && !dma_buffer) {
else if (request_dma(dma, "esp serial")) {
free_pages((unsigned long)dma_buffer,
get_order(DMA_BUFFER_SZ));
- dma_buffer = 0;
+ 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);
if (info->tty)
clear_bit(TTY_IO_ERROR, &info->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
* set the speed of the serial port
*/
change_speed(info);
-
info->flags |= ASYNC_INITIALIZED;
- retval = 0;
-out: restore_flags(flags);
+ return 0;
+
+out:
+ spin_unlock_irqrestore(&info->lock, flags);
+out_unlocked:
return retval;
}
info->irq);
#endif
- save_flags(flags); cli(); /* Disable interrupts */
-
+ 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->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);
free_dma(dma);
free_pages((unsigned long)dma_buffer,
get_order(DMA_BUFFER_SZ));
- dma_buffer = 0;
+ dma_buffer = NULL;
}
}
if (info->xmit_buf) {
free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
+ info->xmit_buf = NULL;
}
info->IER = 0;
set_bit(TTY_IO_ERROR, &info->tty->flags);
info->flags &= ~ASYNC_INITIALIZED;
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
/*
if (I_IXOFF(info->tty))
flow1 |= 0x81;
- save_flags(flags); cli();
+ 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, info->config.flow_on >> 8);
serial_out(info, UART_ESI_CMD2, info->config.flow_on);
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_put_char(struct tty_struct *tty, unsigned char ch)
if (!tty || !info->xmit_buf)
return;
- save_flags(flags); cli();
- if (info->xmit_cnt >= ESP_XMIT_SIZE - 1) {
- restore_flags(flags);
- return;
+ 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++;
}
-
- info->xmit_buf[info->xmit_head++] = ch;
- info->xmit_head &= ESP_XMIT_SIZE-1;
- info->xmit_cnt++;
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_flush_chars(struct tty_struct *tty)
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)
- return;
+ goto out;
- save_flags(flags); cli();
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);
}
- restore_flags(flags);
+out:
+ spin_unlock_irqrestore(&info->lock, flags);
}
-static int rs_write(struct tty_struct * tty, int from_user,
+static int rs_write(struct tty_struct * tty,
const unsigned char *buf, int count)
{
int c, t, ret = 0;
if (serial_paranoia_check(info, tty->name, "rs_write"))
return 0;
- if (!tty || !info->xmit_buf || !tmp_buf)
+ if (!tty || !info->xmit_buf)
return 0;
- if (from_user)
- down(&tmp_buf_sem);
-
while (1) {
/* Thanks to R. Wolff for suggesting how to do this with */
/* interrupts enabled */
if (c <= 0)
break;
- if (from_user) {
- c -= copy_from_user(tmp_buf, buf, c);
-
- if (!c) {
- if (!ret)
- ret = -EFAULT;
- break;
- }
-
- memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
- } else
- memcpy(info->xmit_buf + info->xmit_head, buf, c);
+ memcpy(info->xmit_buf + info->xmit_head, buf, c);
info->xmit_head = (info->xmit_head + c) & (ESP_XMIT_SIZE-1);
info->xmit_cnt += c;
ret += c;
}
- if (from_user)
- up(&tmp_buf_sem);
-
- save_flags(flags); cli();
+ 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_CMD2, info->IER);
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return ret;
}
{
struct esp_struct *info = (struct esp_struct *)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 void rs_flush_buffer(struct tty_struct *tty)
{
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->name, "rs_flush_buffer"))
return;
- cli();
+ spin_lock_irqsave(&info->lock, flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- sti();
- wake_up_interruptible(&tty->write_wait);
- if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
- tty->ldisc.write_wakeup)
- (tty->ldisc.write_wakeup)(tty);
+ spin_unlock_irqrestore(&info->lock, flags);
+ tty_wakeup(tty);
}
/*
static void rs_throttle(struct tty_struct * tty)
{
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
if (serial_paranoia_check(info, tty->name, "rs_throttle"))
return;
-
- cli();
+
+ 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);
- sti();
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_unthrottle(struct tty_struct * tty)
{
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
#ifdef SERIAL_DEBUG_THROTTLE
char buf[64];
if (serial_paranoia_check(info, tty->name, "rs_unthrottle"))
return;
- cli();
+ 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);
- sti();
+ spin_unlock_irqrestore(&info->lock, flags);
}
/*
*/
static int get_serial_info(struct esp_struct * info,
- struct serial_struct * retinfo)
+ struct serial_struct __user *retinfo)
{
struct serial_struct tmp;
- if (!retinfo)
- return -EFAULT;
memset(&tmp, 0, sizeof(tmp));
tmp.type = PORT_16550A;
tmp.line = info->line;
}
static int get_esp_config(struct esp_struct * info,
- struct hayes_esp_config * retinfo)
+ struct hayes_esp_config __user *retinfo)
{
struct hayes_esp_config tmp;
}
static int set_serial_info(struct esp_struct * info,
- struct serial_struct * new_info)
+ struct serial_struct __user *new_info)
{
struct serial_struct new_serial;
struct esp_struct old_info;
}
static int set_esp_config(struct esp_struct * info,
- struct hayes_esp_config * new_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. */
/* 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 */
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.flow_off = new_config.flow_off;
info->config.flow_on = new_config.flow_on;
- save_flags(flags); cli();
+
+ 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);
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
if ((new_config.rx_trigger != info->config.rx_trigger) ||
info->config.rx_trigger = new_config.rx_trigger;
info->config.tx_trigger = new_config.tx_trigger;
- save_flags(flags); cli();
+ 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.tx_trigger >> 8);
serial_out(info, UART_ESI_CMD2, new_config.tx_trigger);
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
if (new_config.rx_timeout != info->config.rx_timeout) {
unsigned long flags;
info->config.rx_timeout = new_config.rx_timeout;
- save_flags(flags); cli();
+ spin_lock_irqsave(&info->lock, flags);
if (info->IER & UART_IER_RDI) {
serial_out(info, UART_ESI_CMD1,
new_config.rx_timeout);
}
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
if (!(info->flags & ASYNC_INITIALIZED))
* 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 *value)
+static int get_lsr_info(struct esp_struct * info, unsigned int __user *value)
{
unsigned char status;
unsigned int result;
+ unsigned long flags;
- cli();
+ spin_lock_irqsave(&info->lock, flags);
serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
status = serial_in(info, UART_ESI_STAT1);
- sti();
+ spin_unlock_irqrestore(&info->lock, flags);
result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0);
return put_user(result,value);
}
{
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
unsigned char control, status;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->name, __FUNCTION__))
return -ENODEV;
return -EIO;
control = info->MCR;
- cli();
+
+ spin_lock_irqsave(&info->lock, flags);
serial_out(info, UART_ESI_CMD1, ESI_GET_UART_STAT);
status = serial_in(info, UART_ESI_STAT2);
- sti();
+ 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)
unsigned int set, unsigned int clear)
{
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
- unsigned int arg;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->name, __FUNCTION__))
return -ENODEV;
if (tty->flags & (1 << TTY_IO_ERROR))
return -EIO;
- cli();
+ spin_lock_irqsave(&info->lock, flags);
if (set & TIOCM_RTS)
info->MCR |= 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);
- sti();
+
+ spin_unlock_irqrestore(&info->lock, flags);
return 0;
}
if (serial_paranoia_check(info, tty->name, "esp_break"))
return;
- save_flags(flags); cli();
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);
}
- restore_flags(flags);
}
static int rs_ioctl(struct tty_struct *tty, struct file * file,
{
struct esp_struct * info = (struct esp_struct *)tty->driver_data;
struct async_icount cprev, cnow; /* kernel counter temps */
- struct serial_icounter_struct *p_cuser; /* user space */
+ struct serial_icounter_struct __user *p_cuser; /* user space */
+ void __user *argp = (void __user *)arg;
+ unsigned long flags;
if (serial_paranoia_check(info, tty->name, "rs_ioctl"))
return -ENODEV;
switch (cmd) {
case TIOCGSERIAL:
- return get_serial_info(info,
- (struct serial_struct *) arg);
+ return get_serial_info(info, argp);
case TIOCSSERIAL:
- return set_serial_info(info,
- (struct serial_struct *) arg);
+ return set_serial_info(info, argp);
case TIOCSERCONFIG:
/* do not reconfigure after initial configuration */
return 0;
case TIOCSERGWILD:
- return put_user(0L, (unsigned long *) arg);
+ return put_user(0L, (unsigned long __user *)argp);
case TIOCSERGETLSR: /* Get line status register */
- return get_lsr_info(info, (unsigned int *) arg);
+ return get_lsr_info(info, argp);
case TIOCSERSWILD:
if (!capable(CAP_SYS_ADMIN))
* Caller should use TIOCGICOUNT to see which one it was
*/
case TIOCMIWAIT:
- cli();
+ spin_lock_irqsave(&info->lock, flags);
cprev = info->icount; /* note the counters on entry */
- sti();
+ spin_unlock_irqrestore(&info->lock, flags);
while (1) {
+ /* FIXME: convert to new style wakeup */
interruptible_sleep_on(&info->delta_msr_wait);
/* see if a signal did it */
if (signal_pending(current))
return -ERESTARTSYS;
- cli();
+ spin_lock_irqsave(&info->lock, flags);
cnow = info->icount; /* atomic copy */
- sti();
+ spin_unlock_irqrestore(&info->lock, flags);
if (cnow.rng == cprev.rng &&
cnow.dsr == cprev.dsr &&
cnow.dcd == cprev.dcd &&
* RI where only 0->1 is counted.
*/
case TIOCGICOUNT:
- cli();
+ spin_lock_irqsave(&info->lock, flags);
cnow = info->icount;
- sti();
- p_cuser = (struct serial_icounter_struct *) arg;
+ 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) ||
return 0;
case TIOCGHAYESESP:
- return (get_esp_config(info, (struct hayes_esp_config *)arg));
+ return get_esp_config(info, argp);
case TIOCSHAYESESP:
- return (set_esp_config(info, (struct hayes_esp_config *)arg));
+ return set_esp_config(info, argp);
default:
return -ENOIOCTLCMD;
static void rs_set_termios(struct tty_struct *tty, struct termios *old_termios)
{
struct esp_struct *info = (struct esp_struct *)tty->driver_data;
+ unsigned long flags;
if ( (tty->termios->c_cflag == old_termios->c_cflag)
&& ( RELEVANT_IFLAG(tty->termios->c_iflag)
== RELEVANT_IFLAG(old_termios->c_iflag)))
- return;
+ return;
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);
- cli();
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);
- sti();
}
/* 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);
- cli();
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);
- sti();
}
+ spin_unlock_irqrestore(&info->lock, flags);
+
/* Handle turning of CRTSCTS */
if ((old_termios->c_cflag & CRTSCTS) &&
!(tty->termios->c_cflag & CRTSCTS)) {
rs_start(tty);
}
-
-#if 0
- /*
- * No need to wake up processes in open wait, since they
- * sample the CLOCAL flag once, and don't recheck it.
- * XXX It's not clear whether the current behavior is correct
- * or not. Hence, this may change.....
- */
- if (!(old_termios->c_cflag & CLOCAL) &&
- (tty->termios->c_cflag & CLOCAL))
- wake_up_interruptible(&info->open_wait);
-#endif
}
/*
if (!info || serial_paranoia_check(info, tty->name, "rs_close"))
return;
- save_flags(flags); cli();
+ spin_lock_irqsave(&info->lock, flags);
if (tty_hung_up_p(filp)) {
DBG_CNT("before DEC-hung");
goto out;
}
info->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.
info->IER &= ~UART_IER_RDI;
info->read_status_mask &= ~UART_LSR_DR;
if (info->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);
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
shutdown(info);
if (tty->driver->flush_buffer)
tty->driver->flush_buffer(tty);
- if (tty->ldisc.flush_buffer)
- tty->ldisc.flush_buffer(tty);
+ tty_ldisc_flush(tty);
tty->closing = 0;
info->event = 0;
- info->tty = 0;
+ info->tty = NULL;
if (info->blocked_open) {
if (info->close_delay) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(info->close_delay);
+ msleep_interruptible(jiffies_to_msecs(info->close_delay));
}
wake_up_interruptible(&info->open_wait);
}
info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);
wake_up_interruptible(&info->close_wait);
+ return;
+
out:
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
}
static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
if (!char_time)
char_time = 1;
- save_flags(flags); cli();
+ 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)) {
- set_current_state(TASK_INTERRUPTIBLE);
- schedule_timeout(char_time);
+
+ spin_unlock_irqrestore(&info->lock, flags);
+ msleep_interruptible(jiffies_to_msecs(char_time));
if (signal_pending(current))
break;
if (timeout && time_after(jiffies, orig_jiffies + timeout))
break;
+ 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);
}
-
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
set_current_state(TASK_RUNNING);
}
info->event = 0;
info->count = 0;
info->flags &= ~ASYNC_NORMAL_ACTIVE;
- info->tty = 0;
+ info->tty = NULL;
wake_up_interruptible(&info->open_wait);
}
printk("block_til_ready before block: ttys%d, count = %d\n",
info->line, info->count);
#endif
- save_flags(flags);
- cli();
+ spin_lock_irqsave(&info->lock, flags);
if (!tty_hung_up_p(filp))
info->count--;
- restore_flags(flags);
info->blocked_open++;
while (1) {
- save_flags(flags);
- cli();
if ((tty->termios->c_cflag & CBAUD)) {
unsigned int scratch;
serial_out(info, UART_ESI_CMD2,
scratch | UART_MCR_DTR | UART_MCR_RTS);
}
- restore_flags(flags);
set_current_state(TASK_INTERRUPTIBLE);
if (tty_hung_up_p(filp) ||
!(info->flags & ASYNC_INITIALIZED)) {
printk("block_til_ready blocking: ttys%d, count = %d\n",
info->line, info->count);
#endif
+ spin_unlock_irqrestore(&info->lock, flags);
schedule();
+ spin_lock_irqsave(&info->lock, flags);
}
set_current_state(TASK_RUNNING);
remove_wait_queue(&info->open_wait, &wait);
if (!tty_hung_up_p(filp))
info->count++;
info->blocked_open--;
+ spin_unlock_irqrestore(&info->lock, flags);
#ifdef SERIAL_DEBUG_OPEN
printk("block_til_ready after blocking: ttys%d, count = %d\n",
info->line, info->count);
{
struct esp_struct *info;
int retval, line;
+ unsigned long flags;
line = tty->index;
if ((line < 0) || (line >= NR_PORTS))
#ifdef SERIAL_DEBUG_OPEN
printk("esp_open %s, count = %d\n", tty->name, info->count);
#endif
+ spin_lock_irqsave(&info->lock, flags);
info->count++;
tty->driver_data = info;
info->tty = tty;
- if (!tmp_buf) {
- tmp_buf = (unsigned char *) get_zeroed_page(GFP_KERNEL);
- if (!tmp_buf)
- return -ENOMEM;
- }
+ spin_unlock_irqrestore(&info->lock, flags);
/*
* Start up serial port
* driver.
*/
-static _INLINE_ void show_serial_version(void)
+static inline void 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 _INLINE_ int autoconfig(struct esp_struct * info, int *region_start)
+static inline int autoconfig(struct esp_struct * info)
{
int port_detected = 0;
unsigned long flags;
- save_flags(flags); cli();
-
+ if (!request_region(info->port, REGION_SIZE, "esp serial"))
+ return -EIO;
+
+ spin_lock_irqsave(&info->lock, flags);
/*
* Check for ESP card
*/
- if (!check_region(info->port, 8) &&
- serial_in(info, UART_ESI_BASE) == 0xf3) {
+ if (serial_in(info, UART_ESI_BASE) == 0xf3) {
serial_out(info, UART_ESI_CMD1, 0x00);
serial_out(info, UART_ESI_CMD1, 0x01);
info->irq = 4;
}
- if (ports && (ports->port == (info->port - 8))) {
- release_region(*region_start,
- info->port - *region_start);
- } else
- *region_start = info->port;
-
- if (!request_region(*region_start,
- info->port - *region_start + 8,
- "esp serial"))
- {
- restore_flags(flags);
- return -EIO;
- }
/* put card in enhanced mode */
/* this prevents access through */
serial_out(info, UART_ESI_CMD2, 0x00);
}
}
+ if (!port_detected)
+ release_region(info->port, REGION_SIZE);
- restore_flags(flags);
+ spin_unlock_irqrestore(&info->lock, flags);
return (port_detected);
}
/*
* The serial driver boot-time initialization code!
*/
-int __init espserial_init(void)
+static int __init espserial_init(void)
{
int i, offset;
- int region_start;
struct esp_struct * info;
- struct esp_struct *last_primary = 0;
+ struct esp_struct *last_primary = NULL;
int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
esp_driver = alloc_tty_driver(NR_PORTS);
}
memset((void *)info, 0, sizeof(struct esp_struct));
+ 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;
info->irq = irq[i];
info->line = (i * 8) + (offset / 8);
- if (!autoconfig(info, ®ion_start)) {
+ if (!autoconfig(info)) {
i++;
offset = 0;
continue;
static void __exit espserial_exit(void)
{
- unsigned long flags;
int e1;
- unsigned int region_start, region_end;
struct esp_struct *temp_async;
struct esp_pio_buffer *pio_buf;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
- save_flags(flags);
- cli();
if ((e1 = tty_unregister_driver(esp_driver)))
printk("SERIAL: failed to unregister serial driver (%d)\n",
e1);
- restore_flags(flags);
put_tty_driver(esp_driver);
while (ports) {
if (ports->port) {
- region_start = region_end = ports->port;
- temp_async = ports;
-
- while (temp_async) {
- if ((region_start - temp_async->port) == 8) {
- region_start = temp_async->port;
- temp_async->port = 0;
- temp_async = ports;
- } else if ((temp_async->port - region_end)
- == 8) {
- region_end = temp_async->port;
- temp_async->port = 0;
- temp_async = ports;
- } else
- temp_async = temp_async->next_port;
- }
-
- release_region(region_start,
- region_end - region_start + 8);
+ release_region(ports->port, REGION_SIZE);
}
-
temp_async = ports->next_port;
kfree(ports);
ports = temp_async;
free_pages((unsigned long)dma_buffer,
get_order(DMA_BUFFER_SZ));
- if (tmp_buf)
- free_page((unsigned long)tmp_buf);
-
while (free_pio_buf) {
pio_buf = free_pio_buf->next;
kfree(free_pio_buf);