Merge remote-tracking branch 'staging/staging-next'
[deliverable/linux.git] / drivers / staging / dgnc / dgnc_cls.c
index 46c050cc7dbe79d3793f550c7c1ce4fbd589f3d6..4e1e0dc67f410011736df65192cf453f3f541dd9 100644 (file)
 #include "dgnc_cls.h"
 #include "dgnc_tty.h"
 
-static inline void cls_parse_isr(struct dgnc_board *brd, uint port);
-static inline void cls_clear_break(struct channel_t *ch, int force);
-static inline void cls_set_cts_flow_control(struct channel_t *ch);
-static inline void cls_set_rts_flow_control(struct channel_t *ch);
-static inline void cls_set_ixon_flow_control(struct channel_t *ch);
-static inline void cls_set_ixoff_flow_control(struct channel_t *ch);
-static inline void cls_set_no_output_flow_control(struct channel_t *ch);
-static inline void cls_set_no_input_flow_control(struct channel_t *ch);
-static void cls_parse_modem(struct channel_t *ch, unsigned char signals);
-static void cls_tasklet(unsigned long data);
-static void cls_vpd(struct dgnc_board *brd);
-static void cls_uart_init(struct channel_t *ch);
-static void cls_uart_off(struct channel_t *ch);
-static int cls_drain(struct tty_struct *tty, uint seconds);
-static void cls_param(struct tty_struct *tty);
-static void cls_assert_modem_signals(struct channel_t *ch);
-static void cls_flush_uart_write(struct channel_t *ch);
-static void cls_flush_uart_read(struct channel_t *ch);
-static void cls_disable_receiver(struct channel_t *ch);
-static void cls_enable_receiver(struct channel_t *ch);
-static void cls_send_break(struct channel_t *ch, int msecs);
-static void cls_send_start_character(struct channel_t *ch);
-static void cls_send_stop_character(struct channel_t *ch);
-static void cls_copy_data_from_uart_to_queue(struct channel_t *ch);
-static void cls_copy_data_from_queue_to_uart(struct channel_t *ch);
-static uint cls_get_uart_bytes_left(struct channel_t *ch);
-static void cls_send_immediate_char(struct channel_t *ch, unsigned char);
-static irqreturn_t cls_intr(int irq, void *voidbrd);
-
-struct board_ops dgnc_cls_ops = {
-       .tasklet =                      cls_tasklet,
-       .intr =                         cls_intr,
-       .uart_init =                    cls_uart_init,
-       .uart_off =                     cls_uart_off,
-       .drain =                        cls_drain,
-       .param =                        cls_param,
-       .vpd =                          cls_vpd,
-       .assert_modem_signals =         cls_assert_modem_signals,
-       .flush_uart_write =             cls_flush_uart_write,
-       .flush_uart_read =              cls_flush_uart_read,
-       .disable_receiver =             cls_disable_receiver,
-       .enable_receiver =              cls_enable_receiver,
-       .send_break =                   cls_send_break,
-       .send_start_character =         cls_send_start_character,
-       .send_stop_character =          cls_send_stop_character,
-       .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
-       .get_uart_bytes_left =          cls_get_uart_bytes_left,
-       .send_immediate_char =          cls_send_immediate_char
-};
-
 static inline void cls_set_cts_flow_control(struct channel_t *ch)
 {
        unsigned char lcrb = readb(&ch->ch_cls_uart->lcr);
@@ -357,179 +307,445 @@ static inline void cls_clear_break(struct channel_t *ch, int force)
        spin_unlock_irqrestore(&ch->ch_lock, flags);
 }
 
-/* Parse the ISR register for the specific port */
-static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
+static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
 {
-       struct channel_t *ch;
-       unsigned char isr = 0;
+       int qleft = 0;
+       unsigned char linestatus = 0;
+       unsigned char error_mask = 0;
+       ushort head;
+       ushort tail;
        unsigned long flags;
 
-       /*
-        * No need to verify board pointer, it was already
-        * verified in the interrupt routine.
-        */
-
-       if (port >= brd->nasync)
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
                return;
 
-       ch = brd->channels[port];
-       if (ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
+       spin_lock_irqsave(&ch->ch_lock, flags);
+
+       /* cache head and tail of queue */
+       head = ch->ch_r_head;
+       tail = ch->ch_r_tail;
+
+       /* Store how much space we have left in the queue */
+       qleft = tail - head - 1;
+       if (qleft < 0)
+               qleft += RQUEUEMASK + 1;
+
+       /*
+        * Create a mask to determine whether we should
+        * insert the character (if any) into our queue.
+        */
+       if (ch->ch_c_iflag & IGNBRK)
+               error_mask |= UART_LSR_BI;
 
-       /* Here we try to figure out what caused the interrupt to happen */
        while (1) {
-               isr = readb(&ch->ch_cls_uart->isr_fcr);
+               linestatus = readb(&ch->ch_cls_uart->lsr);
 
-               /* Bail if no pending interrupt on port */
-               if (isr & UART_IIR_NO_INT)
+               if (!(linestatus & (UART_LSR_DR)))
                        break;
 
-               /* Receive Interrupt pending */
-               if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
-                       /* Read data from uart -> queue */
-                       brd->intr_rx++;
-                       ch->ch_intr_rx++;
-                       cls_copy_data_from_uart_to_queue(ch);
-                       dgnc_check_queue_flow_control(ch);
+               /*
+                * Discard character if we are ignoring the error mask.
+               */
+               if (linestatus & error_mask)  {
+                       linestatus = 0;
+                       readb(&ch->ch_cls_uart->txrx);
+                       continue;
                }
 
-               /* Transmit Hold register empty pending */
-               if (isr & UART_IIR_THRI) {
-                       /* Transfer data (if any) from Write Queue -> UART. */
-                       spin_lock_irqsave(&ch->ch_lock, flags);
-                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
-                       brd->intr_tx++;
-                       ch->ch_intr_tx++;
-                       spin_unlock_irqrestore(&ch->ch_lock, flags);
-                       cls_copy_data_from_queue_to_uart(ch);
+               /*
+                * If our queue is full, we have no choice but to drop some
+                * data. The assumption is that HWFLOW or SWFLOW should have
+                * stopped things way way before we got to this point.
+                *
+                * I decided that I wanted to ditch the oldest data first,
+                * I hope thats okay with everyone? Yes? Good.
+                */
+               while (qleft < 1) {
+                       tail = (tail + 1) & RQUEUEMASK;
+                       ch->ch_r_tail = tail;
+                       ch->ch_err_overrun++;
+                       qleft++;
                }
 
-               /* CTS/RTS change of state */
-               if (isr & UART_IIR_CTSRTS) {
-                       brd->intr_modem++;
-                       ch->ch_intr_modem++;
-                       /*
-                        * Don't need to do anything, the cls_parse_modem
-                        * below will grab the updated modem signals.
-                        */
-               }
+               ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
+                                                                | UART_LSR_FE);
+               ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
 
-               /* Parse any modem signal changes */
-               cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+               qleft--;
+
+               if (ch->ch_equeue[head] & UART_LSR_PE)
+                       ch->ch_err_parity++;
+               if (ch->ch_equeue[head] & UART_LSR_BI)
+                       ch->ch_err_break++;
+               if (ch->ch_equeue[head] & UART_LSR_FE)
+                       ch->ch_err_frame++;
+
+               /* Add to, and flip head if needed */
+               head = (head + 1) & RQUEUEMASK;
+               ch->ch_rxcount++;
        }
+
+       /*
+        * Write new final heads to channel structure.
+        */
+       ch->ch_r_head = head & RQUEUEMASK;
+       ch->ch_e_head = head & EQUEUEMASK;
+
+       spin_unlock_irqrestore(&ch->ch_lock, flags);
 }
 
-/*
- * cls_param()
- * Send any/all changes to the line to the UART.
- */
-static void cls_param(struct tty_struct *tty)
+/* Make the UART raise any of the output signals we want up */
+static void cls_assert_modem_signals(struct channel_t *ch)
 {
-       unsigned char lcr = 0;
-       unsigned char uart_lcr = 0;
-       unsigned char ier = 0;
-       unsigned char uart_ier = 0;
-       uint baud = 9600;
-       int quot = 0;
-       struct dgnc_board *bd;
-       struct channel_t *ch;
-       struct un_t   *un;
+       unsigned char out;
 
-       if (!tty || tty->magic != TTY_MAGIC)
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
                return;
 
-       un = (struct un_t *)tty->driver_data;
-       if (!un || un->magic != DGNC_UNIT_MAGIC)
-               return;
+       out = ch->ch_mostat;
 
-       ch = un->un_ch;
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
+       if (ch->ch_flags & CH_LOOPBACK)
+               out |= UART_MCR_LOOP;
 
-       bd = ch->ch_bd;
-       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
-               return;
+       writeb(out, &ch->ch_cls_uart->mcr);
 
-       /*
-        * If baud rate is zero, flush queues, and set mval to drop DTR.
-        */
-       if ((ch->ch_c_cflag & (CBAUD)) == 0) {
-               ch->ch_r_head = 0;
-               ch->ch_r_tail = 0;
-               ch->ch_e_head = 0;
-               ch->ch_e_tail = 0;
-               ch->ch_w_head = 0;
-               ch->ch_w_tail = 0;
+       /* Give time for the UART to actually drop the signals */
+       udelay(10);
+}
 
-               cls_flush_uart_write(ch);
-               cls_flush_uart_read(ch);
+static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
+{
+       ushort head;
+       ushort tail;
+       int n;
+       int qlen;
+       uint len_written = 0;
+       unsigned long flags;
 
-               /* The baudrate is B0 so all modem lines are to be dropped. */
-               ch->ch_flags |= (CH_BAUD0);
-               ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
-               cls_assert_modem_signals(ch);
-               ch->ch_old_baud = 0;
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
                return;
-       } else if (ch->ch_custom_speed) {
-               baud = ch->ch_custom_speed;
-               /* Handle transition from B0 */
-               if (ch->ch_flags & CH_BAUD0) {
-                       ch->ch_flags &= ~(CH_BAUD0);
 
-                       /*
-                        * Bring back up RTS and DTR...
-                        * Also handle RTS or DTR toggle if set.
-                        */
-                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
-                               ch->ch_mostat |= (UART_MCR_RTS);
-                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
-                               ch->ch_mostat |= (UART_MCR_DTR);
-               }
+       spin_lock_irqsave(&ch->ch_lock, flags);
 
-       } else {
-               int iindex = 0;
-               int jindex = 0;
+       /* No data to write to the UART */
+       if (ch->ch_w_tail == ch->ch_w_head)
+               goto exit_unlock;
 
-               ulong bauds[4][16] = {
-                       { /* slowbaud */
-                               0,      50,     75,     110,
-                               134,    150,    200,    300,
-                               600,    1200,   1800,   2400,
-                               4800,   9600,   19200,  38400 },
-                       { /* slowbaud & CBAUDEX */
-                               0,      57600,  115200, 230400,
-                               460800, 150,    200,    921600,
-                               600,    1200,   1800,   2400,
-                               4800,   9600,   19200,  38400 },
-                       { /* fastbaud */
-                               0,      57600,   76800, 115200,
-                               131657, 153600, 230400, 460800,
-                               921600, 1200,   1800,   2400,
-                               4800,   9600,   19200,  38400 },
-                       { /* fastbaud & CBAUDEX */
-                               0,      57600,  115200, 230400,
-                               460800, 150,    200,    921600,
-                               600,    1200,   1800,   2400,
-                               4800,   9600,   19200,  38400 }
-               };
+       /* If port is "stopped", don't send any data to the UART */
+       if ((ch->ch_flags & CH_FORCED_STOP) ||
+           (ch->ch_flags & CH_BREAK_SENDING))
+               goto exit_unlock;
 
-               /*
-                * Only use the TXPrint baud rate if the terminal
-                * unit is NOT open
-                */
-               if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
-                   (un->un_type == DGNC_PRINT))
-                       baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
-               else
-                       baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
+       if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
+               goto exit_unlock;
 
-               if (ch->ch_c_cflag & CBAUDEX)
-                       iindex = 1;
+       n = 32;
 
-               if (ch->ch_digi.digi_flags & DIGI_FAST)
-                       iindex += 2;
+       /* cache head and tail of queue */
+       head = ch->ch_w_head & WQUEUEMASK;
+       tail = ch->ch_w_tail & WQUEUEMASK;
+       qlen = (head - tail) & WQUEUEMASK;
 
-               jindex = baud;
+       /* Find minimum of the FIFO space, versus queue length */
+       n = min(n, qlen);
+
+       while (n > 0) {
+               /*
+                * If RTS Toggle mode is on, turn on RTS now if not already set,
+                * and make sure we get an event when the data transfer has
+                * completed.
+                */
+               if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
+                       if (!(ch->ch_mostat & UART_MCR_RTS)) {
+                               ch->ch_mostat |= (UART_MCR_RTS);
+                               cls_assert_modem_signals(ch);
+                       }
+                       ch->ch_tun.un_flags |= (UN_EMPTY);
+               }
+
+               /*
+                * If DTR Toggle mode is on, turn on DTR now if not already set,
+                * and make sure we get an event when the data transfer has
+                * completed.
+                */
+               if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
+                       if (!(ch->ch_mostat & UART_MCR_DTR)) {
+                               ch->ch_mostat |= (UART_MCR_DTR);
+                               cls_assert_modem_signals(ch);
+                       }
+                       ch->ch_tun.un_flags |= (UN_EMPTY);
+               }
+               writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
+               ch->ch_w_tail++;
+               ch->ch_w_tail &= WQUEUEMASK;
+               ch->ch_txcount++;
+               len_written++;
+               n--;
+       }
+
+       if (len_written > 0)
+               ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+
+exit_unlock:
+       spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
+{
+       unsigned char msignals = signals;
+       unsigned long flags;
+
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+               return;
+
+       /*
+        * Do altpin switching. Altpin switches DCD and DSR.
+        * This prolly breaks DSRPACE, so we should be more clever here.
+        */
+       spin_lock_irqsave(&ch->ch_lock, flags);
+       if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
+               unsigned char mswap = signals;
+
+               if (mswap & UART_MSR_DDCD) {
+                       msignals &= ~UART_MSR_DDCD;
+                       msignals |= UART_MSR_DDSR;
+               }
+               if (mswap & UART_MSR_DDSR) {
+                       msignals &= ~UART_MSR_DDSR;
+                       msignals |= UART_MSR_DDCD;
+               }
+               if (mswap & UART_MSR_DCD) {
+                       msignals &= ~UART_MSR_DCD;
+                       msignals |= UART_MSR_DSR;
+               }
+               if (mswap & UART_MSR_DSR) {
+                       msignals &= ~UART_MSR_DSR;
+                       msignals |= UART_MSR_DCD;
+               }
+       }
+       spin_unlock_irqrestore(&ch->ch_lock, flags);
+
+       /*
+        * Scrub off lower bits. They signify delta's, which I don't
+        * care about
+        */
+       signals &= 0xf0;
+
+       spin_lock_irqsave(&ch->ch_lock, flags);
+       if (msignals & UART_MSR_DCD)
+               ch->ch_mistat |= UART_MSR_DCD;
+       else
+               ch->ch_mistat &= ~UART_MSR_DCD;
+
+       if (msignals & UART_MSR_DSR)
+               ch->ch_mistat |= UART_MSR_DSR;
+       else
+               ch->ch_mistat &= ~UART_MSR_DSR;
+
+       if (msignals & UART_MSR_RI)
+               ch->ch_mistat |= UART_MSR_RI;
+       else
+               ch->ch_mistat &= ~UART_MSR_RI;
+
+       if (msignals & UART_MSR_CTS)
+               ch->ch_mistat |= UART_MSR_CTS;
+       else
+               ch->ch_mistat &= ~UART_MSR_CTS;
+       spin_unlock_irqrestore(&ch->ch_lock, flags);
+}
+
+/* Parse the ISR register for the specific port */
+static inline void cls_parse_isr(struct dgnc_board *brd, uint port)
+{
+       struct channel_t *ch;
+       unsigned char isr = 0;
+       unsigned long flags;
+
+       /*
+        * No need to verify board pointer, it was already
+        * verified in the interrupt routine.
+        */
+
+       if (port >= brd->nasync)
+               return;
+
+       ch = brd->channels[port];
+       if (ch->magic != DGNC_CHANNEL_MAGIC)
+               return;
+
+       /* Here we try to figure out what caused the interrupt to happen */
+       while (1) {
+               isr = readb(&ch->ch_cls_uart->isr_fcr);
+
+               /* Bail if no pending interrupt on port */
+               if (isr & UART_IIR_NO_INT)
+                       break;
+
+               /* Receive Interrupt pending */
+               if (isr & (UART_IIR_RDI | UART_IIR_RDI_TIMEOUT)) {
+                       /* Read data from uart -> queue */
+                       cls_copy_data_from_uart_to_queue(ch);
+                       dgnc_check_queue_flow_control(ch);
+               }
+
+               /* Transmit Hold register empty pending */
+               if (isr & UART_IIR_THRI) {
+                       /* Transfer data (if any) from Write Queue -> UART. */
+                       spin_lock_irqsave(&ch->ch_lock, flags);
+                       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+                       spin_unlock_irqrestore(&ch->ch_lock, flags);
+                       cls_copy_data_from_queue_to_uart(ch);
+               }
+
+               /* Parse any modem signal changes */
+               cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr));
+       }
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_write(struct channel_t *ch)
+{
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+               return;
+
+       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
+              &ch->ch_cls_uart->isr_fcr);
+       usleep_range(10, 20);
+
+       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
+}
+
+/* Channel lock MUST be held before calling this function! */
+static void cls_flush_uart_read(struct channel_t *ch)
+{
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+               return;
+
+       /*
+        * For complete POSIX compatibility, we should be purging the
+        * read FIFO in the UART here.
+        *
+        * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
+        * incorrectly flushes write data as well as just basically trashing the
+        * FIFO.
+        *
+        * Presumably, this is a bug in this UART.
+        */
+
+       udelay(10);
+}
+
+/*
+ * cls_param()
+ * Send any/all changes to the line to the UART.
+ */
+static void cls_param(struct tty_struct *tty)
+{
+       unsigned char lcr = 0;
+       unsigned char uart_lcr = 0;
+       unsigned char ier = 0;
+       unsigned char uart_ier = 0;
+       uint baud = 9600;
+       int quot = 0;
+       struct dgnc_board *bd;
+       struct channel_t *ch;
+       struct un_t   *un;
+
+       if (!tty || tty->magic != TTY_MAGIC)
+               return;
+
+       un = (struct un_t *)tty->driver_data;
+       if (!un || un->magic != DGNC_UNIT_MAGIC)
+               return;
+
+       ch = un->un_ch;
+       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
+               return;
+
+       bd = ch->ch_bd;
+       if (!bd || bd->magic != DGNC_BOARD_MAGIC)
+               return;
+
+       /*
+        * If baud rate is zero, flush queues, and set mval to drop DTR.
+        */
+       if ((ch->ch_c_cflag & (CBAUD)) == 0) {
+               ch->ch_r_head = 0;
+               ch->ch_r_tail = 0;
+               ch->ch_e_head = 0;
+               ch->ch_e_tail = 0;
+               ch->ch_w_head = 0;
+               ch->ch_w_tail = 0;
+
+               cls_flush_uart_write(ch);
+               cls_flush_uart_read(ch);
+
+               /* The baudrate is B0 so all modem lines are to be dropped. */
+               ch->ch_flags |= (CH_BAUD0);
+               ch->ch_mostat &= ~(UART_MCR_RTS | UART_MCR_DTR);
+               cls_assert_modem_signals(ch);
+               ch->ch_old_baud = 0;
+               return;
+       } else if (ch->ch_custom_speed) {
+               baud = ch->ch_custom_speed;
+               /* Handle transition from B0 */
+               if (ch->ch_flags & CH_BAUD0) {
+                       ch->ch_flags &= ~(CH_BAUD0);
+
+                       /*
+                        * Bring back up RTS and DTR...
+                        * Also handle RTS or DTR toggle if set.
+                        */
+                       if (!(ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE))
+                               ch->ch_mostat |= (UART_MCR_RTS);
+                       if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE))
+                               ch->ch_mostat |= (UART_MCR_DTR);
+               }
+
+       } else {
+               int iindex = 0;
+               int jindex = 0;
+
+               ulong bauds[4][16] = {
+                       { /* slowbaud */
+                               0,      50,     75,     110,
+                               134,    150,    200,    300,
+                               600,    1200,   1800,   2400,
+                               4800,   9600,   19200,  38400 },
+                       { /* slowbaud & CBAUDEX */
+                               0,      57600,  115200, 230400,
+                               460800, 150,    200,    921600,
+                               600,    1200,   1800,   2400,
+                               4800,   9600,   19200,  38400 },
+                       { /* fastbaud */
+                               0,      57600,   76800, 115200,
+                               131657, 153600, 230400, 460800,
+                               921600, 1200,   1800,   2400,
+                               4800,   9600,   19200,  38400 },
+                       { /* fastbaud & CBAUDEX */
+                               0,      57600,  115200, 230400,
+                               460800, 150,    200,    921600,
+                               600,    1200,   1800,   2400,
+                               4800,   9600,   19200,  38400 }
+               };
+
+               /*
+                * Only use the TXPrint baud rate if the terminal
+                * unit is NOT open
+                */
+               if (!(ch->ch_tun.un_flags & UN_ISOPEN) &&
+                   (un->un_type == DGNC_PRINT))
+                       baud = C_BAUD(ch->ch_pun.un_tty) & 0xff;
+               else
+                       baud = C_BAUD(ch->ch_tun.un_tty) & 0xff;
+
+               if (ch->ch_c_cflag & CBAUDEX)
+                       iindex = 1;
+
+               if (ch->ch_digi.digi_flags & DIGI_FAST)
+                       iindex += 2;
+
+               jindex = baud;
 
                if ((iindex >= 0) && (iindex < 4) && (jindex >= 0) &&
                    (jindex < 16)) {
@@ -760,135 +976,46 @@ static irqreturn_t cls_intr(int irq, void *voidbrd)
 
        spin_lock_irqsave(&brd->bd_intr_lock, flags);
 
-       brd->intr_count++;
-
-       /*
-        * Check the board's global interrupt offset to see if we
-        * we actually do have an interrupt pending for us.
-        */
-       poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
-
-       /* If 0, no interrupts pending */
-       if (!poll_reg) {
-               spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
-               return IRQ_NONE;
-       }
-
-       /* Parse each port to find out what caused the interrupt */
-       for (i = 0; i < brd->nasync; i++)
-               cls_parse_isr(brd, i);
-
-       /*
-        * Schedule tasklet to more in-depth servicing at a better time.
-        */
-       tasklet_schedule(&brd->helper_tasklet);
-
-       spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
-
-       return IRQ_HANDLED;
-}
-
-static void cls_disable_receiver(struct channel_t *ch)
-{
-       unsigned char tmp = readb(&ch->ch_cls_uart->ier);
-
-       tmp &= ~(UART_IER_RDI);
-       writeb(tmp, &ch->ch_cls_uart->ier);
-}
-
-static void cls_enable_receiver(struct channel_t *ch)
-{
-       unsigned char tmp = readb(&ch->ch_cls_uart->ier);
-
-       tmp |= (UART_IER_RDI);
-       writeb(tmp, &ch->ch_cls_uart->ier);
-}
-
-static void cls_copy_data_from_uart_to_queue(struct channel_t *ch)
-{
-       int qleft = 0;
-       unsigned char linestatus = 0;
-       unsigned char error_mask = 0;
-       ushort head;
-       ushort tail;
-       unsigned long flags;
-
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       spin_lock_irqsave(&ch->ch_lock, flags);
-
-       /* cache head and tail of queue */
-       head = ch->ch_r_head;
-       tail = ch->ch_r_tail;
-
-       /* Store how much space we have left in the queue */
-       qleft = tail - head - 1;
-       if (qleft < 0)
-               qleft += RQUEUEMASK + 1;
-
-       /*
-        * Create a mask to determine whether we should
-        * insert the character (if any) into our queue.
-        */
-       if (ch->ch_c_iflag & IGNBRK)
-               error_mask |= UART_LSR_BI;
-
-       while (1) {
-               linestatus = readb(&ch->ch_cls_uart->lsr);
-
-               if (!(linestatus & (UART_LSR_DR)))
-                       break;
-
-               /*
-                * Discard character if we are ignoring the error mask.
-               */
-               if (linestatus & error_mask)  {
-                       linestatus = 0;
-                       readb(&ch->ch_cls_uart->txrx);
-                       continue;
-               }
-
-               /*
-                * If our queue is full, we have no choice but to drop some
-                * data. The assumption is that HWFLOW or SWFLOW should have
-                * stopped things way way before we got to this point.
-                *
-                * I decided that I wanted to ditch the oldest data first,
-                * I hope thats okay with everyone? Yes? Good.
-                */
-               while (qleft < 1) {
-                       tail = (tail + 1) & RQUEUEMASK;
-                       ch->ch_r_tail = tail;
-                       ch->ch_err_overrun++;
-                       qleft++;
-               }
-
-               ch->ch_equeue[head] = linestatus & (UART_LSR_BI | UART_LSR_PE
-                                                                | UART_LSR_FE);
-               ch->ch_rqueue[head] = readb(&ch->ch_cls_uart->txrx);
-
-               qleft--;
-
-               if (ch->ch_equeue[head] & UART_LSR_PE)
-                       ch->ch_err_parity++;
-               if (ch->ch_equeue[head] & UART_LSR_BI)
-                       ch->ch_err_break++;
-               if (ch->ch_equeue[head] & UART_LSR_FE)
-                       ch->ch_err_frame++;
-
-               /* Add to, and flip head if needed */
-               head = (head + 1) & RQUEUEMASK;
-               ch->ch_rxcount++;
+       /*
+        * Check the board's global interrupt offset to see if we
+        * we actually do have an interrupt pending for us.
+        */
+       poll_reg = readb(brd->re_map_membase + UART_CLASSIC_POLL_ADDR_OFFSET);
+
+       /* If 0, no interrupts pending */
+       if (!poll_reg) {
+               spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+               return IRQ_NONE;
        }
 
+       /* Parse each port to find out what caused the interrupt */
+       for (i = 0; i < brd->nasync; i++)
+               cls_parse_isr(brd, i);
+
        /*
-        * Write new final heads to channel structure.
+        * Schedule tasklet to more in-depth servicing at a better time.
         */
-       ch->ch_r_head = head & RQUEUEMASK;
-       ch->ch_e_head = head & EQUEUEMASK;
+       tasklet_schedule(&brd->helper_tasklet);
 
-       spin_unlock_irqrestore(&ch->ch_lock, flags);
+       spin_unlock_irqrestore(&brd->bd_intr_lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+static void cls_disable_receiver(struct channel_t *ch)
+{
+       unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+       tmp &= ~(UART_IER_RDI);
+       writeb(tmp, &ch->ch_cls_uart->ier);
+}
+
+static void cls_enable_receiver(struct channel_t *ch)
+{
+       unsigned char tmp = readb(&ch->ch_cls_uart->ier);
+
+       tmp |= (UART_IER_RDI);
+       writeb(tmp, &ch->ch_cls_uart->ier);
 }
 
 /*
@@ -926,199 +1053,6 @@ static int cls_drain(struct tty_struct *tty, uint seconds)
                                         ((un->un_flags & UN_EMPTY) == 0));
 }
 
-/* Channel lock MUST be held before calling this function! */
-static void cls_flush_uart_write(struct channel_t *ch)
-{
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       writeb((UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_XMIT),
-              &ch->ch_cls_uart->isr_fcr);
-       usleep_range(10, 20);
-
-       ch->ch_flags |= (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
-}
-
-/* Channel lock MUST be held before calling this function! */
-static void cls_flush_uart_read(struct channel_t *ch)
-{
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       /*
-        * For complete POSIX compatibility, we should be purging the
-        * read FIFO in the UART here.
-        *
-        * However, clearing the read FIFO (UART_FCR_CLEAR_RCVR) also
-        * incorrectly flushes write data as well as just basically trashing the
-        * FIFO.
-        *
-        * Presumably, this is a bug in this UART.
-        */
-
-       udelay(10);
-}
-
-static void cls_copy_data_from_queue_to_uart(struct channel_t *ch)
-{
-       ushort head;
-       ushort tail;
-       int n;
-       int qlen;
-       uint len_written = 0;
-       unsigned long flags;
-
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       spin_lock_irqsave(&ch->ch_lock, flags);
-
-       /* No data to write to the UART */
-       if (ch->ch_w_tail == ch->ch_w_head)
-               goto exit_unlock;
-
-       /* If port is "stopped", don't send any data to the UART */
-       if ((ch->ch_flags & CH_FORCED_STOP) ||
-           (ch->ch_flags & CH_BREAK_SENDING))
-               goto exit_unlock;
-
-       if (!(ch->ch_flags & (CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM)))
-               goto exit_unlock;
-
-       n = 32;
-
-       /* cache head and tail of queue */
-       head = ch->ch_w_head & WQUEUEMASK;
-       tail = ch->ch_w_tail & WQUEUEMASK;
-       qlen = (head - tail) & WQUEUEMASK;
-
-       /* Find minimum of the FIFO space, versus queue length */
-       n = min(n, qlen);
-
-       while (n > 0) {
-               /*
-                * If RTS Toggle mode is on, turn on RTS now if not already set,
-                * and make sure we get an event when the data transfer has
-                * completed.
-                */
-               if (ch->ch_digi.digi_flags & DIGI_RTS_TOGGLE) {
-                       if (!(ch->ch_mostat & UART_MCR_RTS)) {
-                               ch->ch_mostat |= (UART_MCR_RTS);
-                               cls_assert_modem_signals(ch);
-                       }
-                       ch->ch_tun.un_flags |= (UN_EMPTY);
-               }
-
-               /*
-                * If DTR Toggle mode is on, turn on DTR now if not already set,
-                * and make sure we get an event when the data transfer has
-                * completed.
-                */
-               if (ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE) {
-                       if (!(ch->ch_mostat & UART_MCR_DTR)) {
-                               ch->ch_mostat |= (UART_MCR_DTR);
-                               cls_assert_modem_signals(ch);
-                       }
-                       ch->ch_tun.un_flags |= (UN_EMPTY);
-               }
-               writeb(ch->ch_wqueue[ch->ch_w_tail], &ch->ch_cls_uart->txrx);
-               ch->ch_w_tail++;
-               ch->ch_w_tail &= WQUEUEMASK;
-               ch->ch_txcount++;
-               len_written++;
-               n--;
-       }
-
-       if (len_written > 0)
-               ch->ch_flags &= ~(CH_TX_FIFO_EMPTY | CH_TX_FIFO_LWM);
-
-exit_unlock:
-       spin_unlock_irqrestore(&ch->ch_lock, flags);
-}
-
-static void cls_parse_modem(struct channel_t *ch, unsigned char signals)
-{
-       unsigned char msignals = signals;
-       unsigned long flags;
-
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       /*
-        * Do altpin switching. Altpin switches DCD and DSR.
-        * This prolly breaks DSRPACE, so we should be more clever here.
-        */
-       spin_lock_irqsave(&ch->ch_lock, flags);
-       if (ch->ch_digi.digi_flags & DIGI_ALTPIN) {
-               unsigned char mswap = signals;
-
-               if (mswap & UART_MSR_DDCD) {
-                       msignals &= ~UART_MSR_DDCD;
-                       msignals |= UART_MSR_DDSR;
-               }
-               if (mswap & UART_MSR_DDSR) {
-                       msignals &= ~UART_MSR_DDSR;
-                       msignals |= UART_MSR_DDCD;
-               }
-               if (mswap & UART_MSR_DCD) {
-                       msignals &= ~UART_MSR_DCD;
-                       msignals |= UART_MSR_DSR;
-               }
-               if (mswap & UART_MSR_DSR) {
-                       msignals &= ~UART_MSR_DSR;
-                       msignals |= UART_MSR_DCD;
-               }
-       }
-       spin_unlock_irqrestore(&ch->ch_lock, flags);
-
-       /*
-        * Scrub off lower bits. They signify delta's, which I don't
-        * care about
-        */
-       signals &= 0xf0;
-
-       spin_lock_irqsave(&ch->ch_lock, flags);
-       if (msignals & UART_MSR_DCD)
-               ch->ch_mistat |= UART_MSR_DCD;
-       else
-               ch->ch_mistat &= ~UART_MSR_DCD;
-
-       if (msignals & UART_MSR_DSR)
-               ch->ch_mistat |= UART_MSR_DSR;
-       else
-               ch->ch_mistat &= ~UART_MSR_DSR;
-
-       if (msignals & UART_MSR_RI)
-               ch->ch_mistat |= UART_MSR_RI;
-       else
-               ch->ch_mistat &= ~UART_MSR_RI;
-
-       if (msignals & UART_MSR_CTS)
-               ch->ch_mistat |= UART_MSR_CTS;
-       else
-               ch->ch_mistat &= ~UART_MSR_CTS;
-       spin_unlock_irqrestore(&ch->ch_lock, flags);
-}
-
-/* Make the UART raise any of the output signals we want up */
-static void cls_assert_modem_signals(struct channel_t *ch)
-{
-       unsigned char out;
-
-       if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
-               return;
-
-       out = ch->ch_mostat;
-
-       if (ch->ch_flags & CH_LOOPBACK)
-               out |= UART_MCR_LOOP;
-
-       writeb(out, &ch->ch_cls_uart->mcr);
-
-       /* Give time for the UART to actually drop the signals */
-       udelay(10);
-}
-
 static void cls_send_start_character(struct channel_t *ch)
 {
        if (!ch || ch->magic != DGNC_CHANNEL_MAGIC)
@@ -1298,3 +1232,24 @@ static void cls_vpd(struct dgnc_board *brd)
 
        iounmap(re_map_vpdbase);
 }
+
+struct board_ops dgnc_cls_ops = {
+       .tasklet =                      cls_tasklet,
+       .intr =                         cls_intr,
+       .uart_init =                    cls_uart_init,
+       .uart_off =                     cls_uart_off,
+       .drain =                        cls_drain,
+       .param =                        cls_param,
+       .vpd =                          cls_vpd,
+       .assert_modem_signals =         cls_assert_modem_signals,
+       .flush_uart_write =             cls_flush_uart_write,
+       .flush_uart_read =              cls_flush_uart_read,
+       .disable_receiver =             cls_disable_receiver,
+       .enable_receiver =              cls_enable_receiver,
+       .send_break =                   cls_send_break,
+       .send_start_character =         cls_send_start_character,
+       .send_stop_character =          cls_send_stop_character,
+       .copy_data_from_queue_to_uart = cls_copy_data_from_queue_to_uart,
+       .get_uart_bytes_left =          cls_get_uart_bytes_left,
+       .send_immediate_char =          cls_send_immediate_char
+};
This page took 0.036401 seconds and 5 git commands to generate.