patch-2.1.87 linux/drivers/char/esp.c
Next file: linux/drivers/char/esp.h
Previous file: linux/drivers/char/bttv.c
Back to the patch index
Back to the overall index
- Lines: 1303
- Date:
Thu Feb 12 15:47:44 1998
- Orig file:
v2.1.86/linux/drivers/char/esp.c
- Orig date:
Mon Dec 1 09:45:43 1997
diff -u --recursive --new-file v2.1.86/linux/drivers/char/esp.c linux/drivers/char/esp.c
@@ -29,9 +29,8 @@
* by Chris Faylor.
*
* Most recent changes: (Andrew J. Robinson)
- * Added rx_trigger, tx_trigger, flow_off, flow_on, and rx_timeout options.
- * ESP enhanced mode configuration can be viewed/changed by a patched
- * version of setserial.
+ * Support for PIO mode. This allows the driver to work properly with
+ * multiport cards.
*
* This module exports the following rs232 io functions:
*
@@ -94,13 +93,14 @@
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 = "ESP serial driver";
-static char *serial_version = "1.6";
+static char *serial_version = "2.0";
static DECLARE_TASK_QUEUE(tq_esp);
@@ -135,7 +135,7 @@
#define DBG_CNT(s)
#endif
-static struct esp_struct *IRQ_ports[16];
+static struct esp_struct *ports;
static void change_speed(struct esp_struct *info);
static void rs_wait_until_sent(struct tty_struct *, int);
@@ -290,181 +290,249 @@
queue_task(&info->tqueue, &tq_esp);
mark_bh(ESP_BH);
}
-
-static void receive_chars_dma(struct esp_struct *info)
+static _INLINE_ struct esp_pio_buffer *get_pio_buffer(void)
{
- info->stat_flags &= ~(ESP_STAT_RX_TIMEOUT | ESP_STAT_NEED_DMA_RX);
+ struct esp_pio_buffer *buf;
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_RX_AVAIL);
- dma_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- dma_bytes |= serial_in(info, UART_ESI_STAT2);
+ if (free_pio_buf) {
+ buf = free_pio_buf;
+ free_pio_buf = buf->next;
+ } else {
+ buf = (struct esp_pio_buffer *)
+ kmalloc(sizeof(struct esp_pio_buffer), GFP_ATOMIC);
+ }
- if (!dma_bytes)
- return;
-
- info->stat_flags |= ESP_STAT_DMA_RX;
- disable_dma(dma);
- clear_dma_ff(dma);
- set_dma_mode(dma, DMA_MODE_READ);
- set_dma_addr(dma, virt_to_bus(dma_buffer));
- set_dma_count(dma, dma_bytes);
- enable_dma(dma);
- serial_out(info, UART_ESI_CMD1, ESI_START_DMA_RX);
+ return buf;
}
-static void do_ttybuf(void *private_)
+static _INLINE_ void release_pio_buffer(struct esp_pio_buffer *buf)
{
- struct esp_struct *info = (struct esp_struct *) private_;
- struct tty_struct *tty;
- int avail_bytes, x_bytes;
- unsigned long int flags;
+ buf->next = free_pio_buf;
+ free_pio_buf = buf;
+}
- save_flags(flags); cli();
+static _INLINE_ void receive_chars_pio(struct esp_struct *info, int num_bytes)
+{
+ struct tty_struct *tty = info->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;
+ }
+
+ sti();
+
+ 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->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;
+ }
+
+ cli();
+
+ /* make sure everything is still ok since interrupts were enabled */
tty = info->tty;
if (!tty) {
- restore_flags(flags);
+ release_pio_buffer(pio_buf);
+ release_pio_buffer(err_buf);
+ info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
return;
}
- avail_bytes = TTY_FLIPBUF_SIZE - tty->flip.count;
+ status_mask = (info->ignore_status_mask >> 2) & 0x07;
- if (avail_bytes) {
- if (info->tty_buf->count < avail_bytes)
- x_bytes = info->tty_buf->count;
- else
- x_bytes = avail_bytes;
+ for (i = 0; i < num_bytes; i++) {
+ if (!(err_buf->data[i] & status_mask)) {
+ *(tty->flip.char_buf_ptr++) = pio_buf->data[i];
- tty->flip.count += x_bytes;
- memcpy(tty->flip.char_buf_ptr, info->tty_buf->char_buf,
- x_bytes);
- memcpy(tty->flip.flag_buf_ptr, info->tty_buf->flag_buf,
- x_bytes);
- tty->flip.char_buf_ptr += x_bytes;
- tty->flip.flag_buf_ptr += x_bytes;
- info->tty_buf->count -= x_bytes;
- info->tty_buf->char_buf_ptr -= x_bytes;
- info->tty_buf->flag_buf_ptr -= x_bytes;
-
- if (info->tty_buf->count) {
- memmove(info->tty_buf->char_buf,
- info->tty_buf->char_buf + x_bytes,
- info->tty_buf->count);
- queue_task(&info->tty_buf->tqueue,
- &tq_timer);
- }
+ if (err_buf->data[i] & 0x04) {
+ *(tty->flip.flag_buf_ptr++) = TTY_BREAK;
- queue_task(&tty->flip.tqueue, &tq_timer);
- } else {
- queue_task(&info->tty_buf->tqueue, &tq_timer);
+ if (info->flags & ASYNC_SAK)
+ do_SAK(tty);
+ }
+ else if (err_buf->data[i] & 0x02)
+ *(tty->flip.flag_buf_ptr++) = 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++;
+ }
}
- restore_flags(flags);
+ queue_task(&tty->flip.tqueue, &tq_timer);
+
+ 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)
+{
+ info->stat_flags &= ~ESP_STAT_RX_TIMEOUT;
+ dma_bytes = num_bytes;
+ info->stat_flags |= ESP_STAT_DMA_RX;
+ disable_dma(dma);
+ clear_dma_ff(dma);
+ set_dma_mode(dma, DMA_MODE_READ);
+ set_dma_addr(dma, virt_to_bus(dma_buffer));
+ set_dma_count(dma, dma_bytes);
+ enable_dma(dma);
+ 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->tty;
- int num_bytes, bytes_left, x_bytes;
- struct tty_flip_buffer *buffer;
-
- if (!(info->stat_flags & ESP_STAT_DMA_RX))
- return;
+ int num_bytes;
disable_dma(dma);
clear_dma_ff(dma);
info->stat_flags &= ~ESP_STAT_DMA_RX;
- bytes_left = num_bytes = dma_bytes - get_dma_residue(dma);
+ num_bytes = dma_bytes - get_dma_residue(dma);
info->icount.rx += num_bytes;
- buffer = &(tty->flip);
-
- if (info->tty_buf->count && (tty->flip.count < TTY_FLIPBUF_SIZE))
- do_ttybuf(info);
-
- while (bytes_left > 0) {
- if ((buffer->count + bytes_left) > TTY_FLIPBUF_SIZE)
- x_bytes = TTY_FLIPBUF_SIZE - buffer->count;
- else
- x_bytes = bytes_left;
- memcpy(buffer->char_buf_ptr,
- dma_buffer + (num_bytes - bytes_left), x_bytes);
- buffer->char_buf_ptr += x_bytes;
- buffer->count += x_bytes;
- memset(buffer->flag_buf_ptr, 0, x_bytes);
- buffer->flag_buf_ptr += x_bytes;
- bytes_left -= x_bytes;
-
- if (bytes_left > 0) {
- if (buffer == info->tty_buf)
- break;
- else
- buffer = info->tty_buf;
- }
- }
+ 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) {
- buffer->flag_buf_ptr--;
+ tty->flip.flag_buf_ptr--;
status &= (0x1c & info->read_status_mask);
if (status & info->ignore_status_mask) {
- buffer->count--;
- buffer->char_buf_ptr--;
- buffer->flag_buf_ptr--;
+ tty->flip.count--;
+ tty->flip.char_buf_ptr--;
+ tty->flip.flag_buf_ptr--;
} else if (status & 0x10) {
- *buffer->flag_buf_ptr = TTY_BREAK;
+ *tty->flip.flag_buf_ptr = TTY_BREAK;
(info->icount.brk)++;
if (info->flags & ASYNC_SAK)
do_SAK(tty);
} else if (status & 0x08) {
- *buffer->flag_buf_ptr = TTY_FRAME;
+ *tty->flip.flag_buf_ptr = TTY_FRAME;
(info->icount.frame)++;
}
else if (status & 0x04) {
- *buffer->flag_buf_ptr = TTY_PARITY;
+ *tty->flip.flag_buf_ptr = TTY_PARITY;
(info->icount.parity)++;
}
- buffer->flag_buf_ptr++;
+ tty->flip.flag_buf_ptr++;
- if (buffer == info->tty_buf)
- queue_task(&info->tty_buf->tqueue, &tq_timer);
-
queue_task(&tty->flip.tqueue, &tq_timer);
}
if (dma_bytes != num_bytes) {
+ num_bytes = dma_bytes - num_bytes;
dma_bytes = 0;
- receive_chars_dma(info);
+ receive_chars_dma(info, num_bytes);
} else
dma_bytes = 0;
}
-static void transmit_chars_dma(struct esp_struct *info)
+static _INLINE_ void transmit_chars_pio(struct esp_struct *info,
+ int space_avail)
{
- info->stat_flags &= ~ESP_STAT_NEED_DMA_TX;
+ int i;
+ struct esp_pio_buffer *pio_buf;
- if ((info->xmit_cnt <= 0) || info->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);
+ 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);
+
+ sti();
+
+ for (i = 0; i < space_avail - 1; i += 2) {
+ outw(*((unsigned short *)(pio_buf->data + i)),
+ info->port + UART_ESI_TX);
+ }
+
+ if (space_avail & 0x0001)
+ 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);
+ 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;
+ }
}
-
- serial_out(info, UART_ESI_CMD1, ESI_NO_COMMAND);
- serial_out(info, UART_ESI_CMD1, ESI_GET_TX_AVAIL);
- dma_bytes = serial_in(info, UART_ESI_STAT1) << 8;
- dma_bytes |= serial_in(info, UART_ESI_STAT2);
- if (!dma_bytes)
- return;
+ if (info->xmit_cnt < WAKEUP_CHARS) {
+ rs_sched_event(info, ESP_EVENT_WRITE_WAKEUP);
- if (dma_bytes > info->xmit_cnt)
- dma_bytes = info->xmit_cnt;
+#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);
+}
+
+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]),
@@ -507,9 +575,6 @@
{
int num_bytes;
- if (!(info->stat_flags & ESP_STAT_DMA_TX))
- return;
-
disable_dma(dma);
clear_dma_ff(dma);
@@ -564,8 +629,7 @@
#ifdef SERIAL_DEBUG_OPEN
printk("scheduling hangup...");
#endif
- queue_task(&info->tqueue_hangup,
- &tq_scheduler);
+ queue_task(&info->tqueue_hangup, &tq_scheduler);
}
}
}
@@ -575,92 +639,104 @@
*/
static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
{
- struct esp_struct * info, *stop_port = 0;
+ struct esp_struct * info;
unsigned err_status;
unsigned int scratch;
- int pre_bytes;
-
+
#ifdef SERIAL_DEBUG_INTR
printk("rs_interrupt_single(%d)...", irq);
#endif
-
- /* This routine will currently check ALL ports when an interrupt */
- /* is received from ANY port */
+ info = (struct esp_struct *)dev_id;
+ err_status = 0;
+ scratch = serial_in(info, UART_ESI_SID);
- info = IRQ_ports[irq];
-
- if (!info)
+ cli();
+
+ if (!info->tty) {
+ sti();
return;
+ }
- do {
- if (!info->tty) {
- info = info->next_port;
- continue;
- }
-
- pre_bytes = dma_bytes;
- err_status = 0;
- scratch = serial_in(info, UART_ESI_SID);
- if (scratch & 0x04) { /* error - check for rx timeout */
- 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 (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 & 0x01)
+ info->stat_flags |= ESP_STAT_RX_TIMEOUT;
- if (err_status & 0x20) /* UART status */
- check_modem_status(info);
+ if (err_status & 0x20) /* UART status */
+ check_modem_status(info);
- if (err_status & 0x80) /* Start break */
- wake_up_interruptible(&info->break_wait);
- }
+ 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 */) {
- receive_chars_dma_done(info, err_status);
- transmit_chars_dma_done(info);
- }
+ 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 (((scratch & 0x01) ||
- (info->stat_flags & ESP_STAT_RX_TIMEOUT)) &&
- (info->IER & UART_IER_RDI))
- if (dma_bytes)
- info->stat_flags |= ESP_STAT_NEED_DMA_RX;
- else
- receive_chars_dma(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;
- if ((scratch & 0x02) && (info->IER & UART_IER_THRI))
- if (dma_bytes)
- info->stat_flags |= ESP_STAT_NEED_DMA_TX;
+ 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);
+
+ if (num_bytes > (TTY_FLIPBUF_SIZE - info->tty->flip.count))
+ num_bytes = TTY_FLIPBUF_SIZE - info->tty->flip.count;
+
+ if (num_bytes) {
+ if (dma_bytes ||
+ (info->stat_flags & ESP_STAT_USE_PIO) ||
+ (num_bytes <= ESP_PIO_THRESHOLD))
+ receive_chars_pio(info, num_bytes);
else
- transmit_chars_dma(info);
-
- info->last_active = jiffies;
-
- if (pre_bytes && !dma_bytes) /* released DMA */
- stop_port = info;
-
- info = info->next_port;
- } while ((info->irq == irq) && (info != IRQ_ports[irq]));
+ 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->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;
- if (stop_port) {
- while ((info != stop_port) && (!dma_bytes)) {
- if (info->tty) {
- if (info->stat_flags & ESP_STAT_NEED_DMA_RX)
- receive_chars_dma(info);
- if ((info->stat_flags & ESP_STAT_NEED_DMA_TX)
- && !dma_bytes)
- transmit_chars_dma(info);
+ 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 <= ESP_PIO_THRESHOLD))
+ transmit_chars_pio(info, num_bytes);
+ else
+ transmit_chars_dma(info, num_bytes);
}
-
- info = info->next_port;
}
}
+ info->last_active = jiffies;
+
#ifdef SERIAL_DEBUG_INTR
printk("end.\n");
#endif
+ sti();
}
/*
@@ -734,7 +810,11 @@
{
/* put ESPC in enhanced mode */
serial_out(info, UART_ESI_CMD1, ESI_SET_MODE);
- serial_out(info, UART_ESI_CMD2, 0x31);
+
+ 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);
@@ -742,8 +822,14 @@
/* set interrupt and DMA channel */
serial_out(info, UART_ESI_CMD1, ESI_SET_IRQ);
- serial_out(info, UART_ESI_CMD2, (dma << 4) | 0x01);
+
+ 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)
@@ -753,7 +839,12 @@
/* set error status mask (check this) */
serial_out(info, UART_ESI_CMD1, ESI_SET_ERR_MASK);
- serial_out(info, UART_ESI_CMD2, 0xbd);
+
+ 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 */
@@ -767,9 +858,9 @@
serial_out(info, UART_ESI_CMD2, tx_trigger >> 8);
serial_out(info, UART_ESI_CMD2, tx_trigger);
- /* Set clock scaling */
+ /* Set clock scaling and wait states */
serial_out(info, UART_ESI_CMD1, ESI_SET_PRESCALAR);
- serial_out(info, UART_ESI_CMD2, ESPC_SCALE);
+ serial_out(info, UART_ESI_CMD2, 0x04 | ESPC_SCALE);
/* set reinterrupt pacing */
serial_out(info, UART_ESI_CMD1, ESI_SET_REINTR);
@@ -780,43 +871,23 @@
{
unsigned long flags;
int retval=0;
- int next_irq;
- struct esp_struct *next_info = 0;
unsigned int num_chars;
save_flags(flags); cli();
- if (info->flags & ASYNC_INITIALIZED)
- goto errout;
+ if (info->flags & ASYNC_INITIALIZED) {
+ restore_flags(flags);
+ return retval;
+ }
if (!info->xmit_buf) {
info->xmit_buf = (unsigned char *)get_free_page(GFP_KERNEL);
if (!info->xmit_buf) {
- retval = -ENOMEM;
- goto errout;
+ restore_flags(flags);
+ return -ENOMEM;
}
}
- if (!info->tty_buf) {
- info->tty_buf = (struct tty_flip_buffer *)kmalloc(
- sizeof(struct tty_flip_buffer), GFP_KERNEL);
-
- if (!info->tty_buf) {
- free_page((unsigned long) info->xmit_buf);
- info->xmit_buf = 0;
- retval = -ENOMEM;
- goto errout;
- }
-
- memset(info->tty_buf, 0, sizeof(struct tty_flip_buffer));
- info->tty_buf->tqueue.routine = do_ttybuf;
- info->tty_buf->tqueue.data = info;
- }
-
- info->tty_buf->count = 0;
- info->tty_buf->char_buf_ptr = info->tty_buf->char_buf;
- info->tty_buf->flag_buf_ptr = info->tty_buf->flag_buf;
-
#ifdef SERIAL_DEBUG_OPEN
printk("starting up ttys%d (irq %d)...", info->line, info->irq);
#endif
@@ -841,44 +912,45 @@
serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
serial_out(info, UART_ESI_CMD2, rx_timeout);
- info->stat_flags = 0;
+ /* 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;
/*
- * Allocate the IRQ if necessary
+ * Allocate the IRQ
*/
- if (!IRQ_ports[info->irq]) {
- retval = request_irq(info->irq, rs_interrupt_single,
- SA_INTERRUPT, "esp serial", NULL);
-
- if (!retval) {
- int i = 1;
-
- while ((i < 16) && !IRQ_ports[i])
- i++;
-
- if (i == 16) {
- dma_buffer = (char *)__get_dma_pages(GFP_KERNEL,
- __get_order(DMA_BUFFER_SZ));
- if (!dma_buffer)
- retval = -ENOMEM;
- else
- retval = request_dma(dma, "esp serial");
+ retval = request_irq(info->irq, rs_interrupt_single, SA_SHIRQ,
+ "esp serial", info);
- if (retval)
- free_irq(info->irq, NULL);
- }
+ if (retval) {
+ if (suser()) {
+ if (info->tty)
+ set_bit(TTY_IO_ERROR,
+ &info->tty->flags);
+ retval = 0;
}
+
+ restore_flags(flags);
+ return retval;
+ }
- if (retval) {
- if (suser()) {
- if (info->tty)
- set_bit(TTY_IO_ERROR,
- &info->tty->flags);
- retval = 0;
- }
- goto errout;
+ 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 int)dma_buffer,
+ __get_order(DMA_BUFFER_SZ));
+ dma_buffer = 0;
+ info->stat_flags |= ESP_STAT_USE_PIO;
}
+
}
info->MCR = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2;
@@ -899,38 +971,6 @@
clear_bit(TTY_IO_ERROR, &info->tty->flags);
info->xmit_cnt = info->xmit_head = info->xmit_tail = 0;
- /* Remove port from "closed" chain */
- if (info->next_port)
- info->next_port->prev_port = info->prev_port;
- if (info->prev_port)
- info->prev_port->next_port = info->next_port;
- else
- IRQ_ports[0] = info->next_port;
-
- /*
- * Insert serial port into IRQ chain.
- */
- next_irq = info->irq;
-
- do {
- next_info = IRQ_ports[next_irq];
-
- if (++next_irq > 15)
- next_irq = 1;
- } while (!next_info && (next_irq != info->irq));
-
- if (!next_info) {
- info->next_port = info;
- info->prev_port = info;
- } else {
- info->next_port = next_info;
- info->prev_port = next_info->prev_port;
- next_info->prev_port->next_port = info;
- next_info->prev_port = info;
- }
-
- IRQ_ports[info->irq] = info;
-
/*
* Set up the tty->alt_speed kludge
*/
@@ -953,10 +993,6 @@
info->flags |= ASYNC_INITIALIZED;
restore_flags(flags);
return 0;
-
-errout:
- restore_flags(flags);
- return retval;
}
/*
@@ -984,67 +1020,36 @@
wake_up_interruptible(&info->delta_msr_wait);
wake_up_interruptible(&info->break_wait);
- /* stop a DMA transfer on the port being closed, and start the next
- one */
+ /* stop a DMA transfer on the port being closed */
if (info->stat_flags & (ESP_STAT_DMA_RX | ESP_STAT_DMA_TX)) {
- struct esp_struct *curr = info->next_port;
-
disable_dma(dma);
clear_dma_ff(dma);
dma_bytes = 0;
-
- while ((curr != info) && (!dma_bytes)) {
- if (curr->tty) {
- if (curr->stat_flags & ESP_STAT_NEED_DMA_RX)
- receive_chars_dma(curr);
- if ((curr->stat_flags & ESP_STAT_NEED_DMA_TX)
- && !dma_bytes)
- transmit_chars_dma(curr);
- }
-
- curr = curr->next_port;
- }
}
/*
- * First unlink the serial port from the IRQ chain...
+ * Free the IRQ
*/
- info->next_port->prev_port = info->prev_port;
- info->prev_port->next_port = info->next_port;
+ free_irq(info->irq, info);
- if (IRQ_ports[info->irq] == info) {
- if ((info->next_port == info) ||
- (info->next_port->irq != info->irq))
- IRQ_ports[info->irq] = 0;
- else
- IRQ_ports[info->irq] = info->next_port;
- }
+ if (dma_buffer) {
+ struct esp_struct *current_port = ports;
- /* Stick it on the "closed" chain */
- info->next_port = IRQ_ports[0];
- if (info->next_port)
- info->next_port->prev_port = info;
- info->prev_port = 0;
- IRQ_ports[0] = info;
-
- /*
- * Free the IRQ, if necessary
- */
- if (!IRQ_ports[info->irq]) {
- int i = 1;
+ while (current_port) {
+ if ((current_port != info) &&
+ (current_port->flags & ASYNC_INITIALIZED))
+ break;
- while ((i < 16) && !IRQ_ports[i])
- i++;
+ current_port = current_port->next_port;
+ }
- if (i == 16) {
+ if (!current_port) {
free_dma(dma);
- free_pages((unsigned int)dma_buffer,
- __get_order(DMA_BUFFER_SZ));
+ free_pages((unsigned int)dma_buffer,
+ __get_order(DMA_BUFFER_SZ));
dma_buffer = 0;
- }
-
- free_irq(info->irq, NULL);
+ }
}
if (info->xmit_buf) {
@@ -1052,25 +1057,6 @@
info->xmit_buf = 0;
}
- if (info->tty_buf) {
- /* code borrowed from tty_io.c */
- if (info->tty_buf->tqueue.sync) {
- struct tq_struct *tq, *prev;
-
- for (tq=tq_timer, prev=0; tq; prev=tq, tq=tq->next) {
- if (tq == &info->tty_buf->tqueue) {
- if (prev)
- prev->next = tq->next;
- else
- tq_timer = tq->next;
- break;
- }
- }
- }
- kfree(info->tty_buf);
- info->tty_buf = 0;
- }
-
info->IER = 0;
serial_out(info, UART_ESI_CMD1, ESI_SET_SRV_MASK);
serial_out(info, UART_ESI_CMD2, 0x00);
@@ -1477,9 +1463,9 @@
{
struct serial_struct new_serial;
struct esp_struct old_info;
- unsigned int change_irq;
+ unsigned int change_irq;
unsigned int change_flow;
- int i, retval = 0;
+ int retval = 0;
struct esp_struct *current_async;
if (copy_from_user(&new_serial,new_info,sizeof(new_serial)))
@@ -1522,102 +1508,97 @@
info->flags = ((info->flags & ~ASYNC_USR_MASK) |
(new_serial.flags & ASYNC_USR_MASK));
info->custom_divisor = new_serial.custom_divisor;
- goto check_and_exit;
- }
-
- if (new_serial.irq == 2)
- new_serial.irq = 9;
-
- if (change_irq) {
- i = 1;
-
- while ((i < 16) && !IRQ_ports[i])
- i++;
+ } else {
+ if (new_serial.irq == 2)
+ new_serial.irq = 9;
- if (i < 16) {
- current_async = IRQ_ports[i];
+ if (change_irq) {
+ current_async = ports;
- do {
+ while (current_async) {
if ((current_async->line >= info->line) &&
(current_async->line < (info->line + 8))) {
if (current_async == info) {
if (current_async->count > 1)
return -EBUSY;
- } else
+ } else if (current_async->count)
return -EBUSY;
}
current_async = current_async->next_port;
- } while (current_async != IRQ_ports[i]);
+ }
}
- }
-
- /*
- * OK, past this point, all the error checking has been done.
- * At this point, we start making changes.....
- */
-
- info->flags = ((info->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;
- flow_off = new_serial.reserved[2];
- flow_on = new_serial.reserved[3];
-
- if ((new_serial.reserved[0] != rx_trigger) ||
- (new_serial.reserved[1] != tx_trigger)) {
- unsigned long flags;
-
- rx_trigger = new_serial.reserved[0];
- tx_trigger = new_serial.reserved[1];
- save_flags(flags); cli();
- serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
- serial_out(info, UART_ESI_CMD2, rx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, rx_trigger);
- serial_out(info, UART_ESI_CMD2, tx_trigger >> 8);
- serial_out(info, UART_ESI_CMD2, tx_trigger);
- restore_flags(flags);
- }
- if (((unsigned char)(new_serial.reserved_char[1]) != rx_timeout)) {
- unsigned long flags;
+ /*
+ * OK, past this point, all the error checking has been done.
+ * At this point, we start making changes.....
+ */
- rx_timeout = (unsigned char)(new_serial.reserved_char[1]);
- save_flags(flags); cli();
+ info->flags = ((info->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;
+ flow_off = new_serial.reserved[2];
+ flow_on = new_serial.reserved[3];
+
+ if ((new_serial.reserved[0] != rx_trigger) ||
+ (new_serial.reserved[1] != tx_trigger)) {
+ unsigned long flags;
+
+ rx_trigger = new_serial.reserved[0];
+ tx_trigger = new_serial.reserved[1];
+ save_flags(flags); cli();
+ serial_out(info, UART_ESI_CMD1, ESI_SET_TRIGGER);
+ serial_out(info, UART_ESI_CMD2, rx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, rx_trigger);
+ serial_out(info, UART_ESI_CMD2, tx_trigger >> 8);
+ serial_out(info, UART_ESI_CMD2, tx_trigger);
+ restore_flags(flags);
+ }
+
+ if (((unsigned char)(new_serial.reserved_char[1]) !=
+ rx_timeout)) {
+ unsigned long flags;
+
+ rx_timeout = (unsigned char)
+ (new_serial.reserved_char[1]);
+ save_flags(flags); cli();
+
+ if (info->IER & UART_IER_RDI) {
+ serial_out(info, UART_ESI_CMD1,
+ ESI_SET_RX_TIMEOUT);
+ serial_out(info, UART_ESI_CMD2, rx_timeout);
+ }
- if (info->IER & UART_IER_RDI) {
- serial_out(info, UART_ESI_CMD1, ESI_SET_RX_TIMEOUT);
- serial_out(info, UART_ESI_CMD2, rx_timeout);
+ restore_flags(flags);
}
- restore_flags(flags);
- }
+ if (change_irq) {
+ /*
+ * We need to shutdown the serial port at the old
+ * port/irq combination.
+ */
+ shutdown(info);
- if (change_irq) {
- /*
- * We need to shutdown the serial port at the old
- * port/irq combination.
- */
- shutdown(info);
+ current_async = ports;
- current_async = IRQ_ports[0];
- while (current_async != 0) {
- if ((current_async->line >= info->line) &&
- (current_async->line < (info->line + 8)))
- current_async->irq = new_serial.irq;
+ 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;
- }
+ 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);
+ 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);
+ }
}
-
-check_and_exit:
+
if (info->flags & ASYNC_INITIALIZED) {
if (((old_info.flags & ASYNC_SPD_MASK) !=
(info->flags & ASYNC_SPD_MASK)) ||
@@ -1635,6 +1616,7 @@
}
} else
retval = startup(info);
+
return retval;
}
@@ -2237,43 +2219,24 @@
static int esp_open(struct tty_struct *tty, struct file * filp)
{
struct esp_struct *info;
- int i, retval, line;
+ int retval, line;
unsigned long page;
line = MINOR(tty->device) - tty->driver.minor_start;
if ((line < 0) || (line >= NR_PORTS))
return -ENODEV;
- /* check whether or not the port is on the closed chain */
+ /* find the port in the chain */
- info = IRQ_ports[0];
+ info = ports;
while (info && (info->line != line))
info = info->next_port;
- /* if the port is not on the closed chain, look for it on the */
- /* open chain */
-
if (!info) {
- i = 1;
-
- while ((i < 16) && !IRQ_ports[i])
- i++;
-
- if (i < 16) {
- info = IRQ_ports[i];
-
- do {
- if (info->line == line)
- break;
- info = info->next_port;
- } while (info != IRQ_ports[i]);
- }
- }
-
- if (!info || (info->line != line) ||
- serial_paranoia_check(info, tty->device, "esp_open"))
+ serial_paranoia_check(info, tty->device, "esp_open");
return -ENODEV;
+ }
#ifdef SERIAL_DEBUG_OPEN
printk("esp_open %s%d, count = %d\n", tty->driver.name, info->line,
@@ -2379,8 +2342,7 @@
info->irq = 4;
}
- if (IRQ_ports[0] &&
- (IRQ_ports[0]->port == (info->port - 8))) {
+ if (ports && (ports->port == (info->port - 8))) {
release_region(*region_start,
info->port - *region_start);
} else
@@ -2414,14 +2376,11 @@
int i, offset;
int region_start;
struct esp_struct * info;
+ struct esp_struct *last_primary = 0;
int esp[] = {0x100,0x140,0x180,0x200,0x240,0x280,0x300,0x380};
init_bh(ESP_BH, do_serial_bh);
- for (i = 0; i < 16; i++) {
- IRQ_ports[i] = 0;
- }
-
for (i = 0; i < NR_PRIMARY; i++)
if (irq[i] != 0)
if ((irq[i] < 2) || (irq[i] > 15) || (irq[i] == 6) ||
@@ -2431,7 +2390,7 @@
irq[i] = 9;
if ((dma != 1) && (dma != 3))
- dma = 1;
+ dma = 0;
if ((rx_trigger < 1) || (rx_trigger > 1023))
rx_trigger = 768;
@@ -2552,21 +2511,27 @@
info->tqueue_hangup.data = info;
info->callout_termios = esp_callout_driver.init_termios;
info->normal_termios = esp_driver.init_termios;
-
- if (IRQ_ports[0])
- IRQ_ports[0]->prev_port = info;
- info->next_port = IRQ_ports[0];
- info->prev_port = 0;
- IRQ_ports[0] = info;
+ info->next_port = ports;
+ ports = info;
printk(KERN_INFO "ttyP%d at 0x%04x (irq = %d) is an ESP ",
info->line, info->port, info->irq);
- if (info->line % 8)
+
+ if (info->line % 8) {
printk("secondary port\n");
- else {
+ /* 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 = (struct esp_struct *)kmalloc(sizeof(struct esp_struct),
GFP_KERNEL);
if (!info)
@@ -2605,7 +2570,8 @@
unsigned long flags;
int e1, e2;
unsigned int region_start, region_end;
- struct esp_struct *current_async, *temp_async;
+ struct esp_struct *temp_async;
+ struct esp_pio_buffer *pio_buf;
/* printk("Unloading %s: version %s\n", serial_name, serial_version); */
save_flags(flags);
@@ -2619,22 +2585,21 @@
e2);
restore_flags(flags);
- current_async = IRQ_ports[0];
- while (current_async != 0) {
- if (current_async->port != 0) {
- region_start = region_end = current_async->port;
- temp_async = current_async;
+ while (ports) {
+ if (ports->port) {
+ region_start = region_end = ports->port;
+ temp_async = ports;
- while (temp_async != 0) {
+ while (temp_async) {
if ((region_start - temp_async->port) == 8) {
region_start = temp_async->port;
temp_async->port = 0;
- temp_async = current_async;
+ temp_async = ports;
} else if ((temp_async->port - region_end)
== 8) {
region_end = temp_async->port;
temp_async->port = 0;
- temp_async = current_async;
+ temp_async = ports;
} else
temp_async = temp_async->next_port;
}
@@ -2643,9 +2608,9 @@
region_end - region_start + 8);
}
- temp_async = current_async->next_port;
- kfree(current_async);
- current_async = temp_async;
+ temp_async = ports->next_port;
+ kfree(ports);
+ ports = temp_async;
}
if (dma_buffer)
@@ -2654,5 +2619,11 @@
if (tmp_buf)
free_page((unsigned long)tmp_buf);
+
+ while (free_pio_buf) {
+ pio_buf = free_pio_buf->next;
+ kfree(free_pio_buf);
+ free_pio_buf = pio_buf;
+ }
}
#endif /* MODULE */
FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov