Loading drivers/staging/dgnc/dgnc_cls.c +460 −489 Original line number Diff line number Diff line Loading @@ -26,56 +26,6 @@ #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); Loading Loading @@ -357,138 +307,418 @@ 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); } /* 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); /* * Discard character if we are ignoring the error mask. */ if (linestatus & error_mask) { linestatus = 0; readb(&ch->ch_cls_uart->txrx); continue; } /* 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. * 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++; } /* Parse any modem signal changes */ cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); } 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++; } /* * cls_param() * Send any/all changes to the line to the UART. * Write new final heads to channel structure. */ static void cls_param(struct tty_struct *tty) ch->ch_r_head = head & RQUEUEMASK; ch->ch_e_head = head & EQUEUEMASK; 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 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; 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_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; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; spin_lock_irqsave(&ch->ch_lock, flags); /* * 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; /* No data to write to the UART */ if (ch->ch_w_tail == ch->ch_w_head) goto exit_unlock; cls_flush_uart_write(ch); cls_flush_uart_read(ch); /* 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; /* 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); 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) { /* * Bring back up RTS and DTR... * Also handle RTS or DTR toggle if set. * 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_digi.digi_flags & DIGI_RTS_TOGGLE) { if (!(ch->ch_mostat & UART_MCR_RTS)) { ch->ch_mostat |= (UART_MCR_RTS); if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) 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--; } } else { int iindex = 0; int jindex = 0; 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 */ brd->intr_rx++; ch->ch_intr_rx++; 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); brd->intr_tx++; ch->ch_intr_tx++; spin_unlock_irqrestore(&ch->ch_lock, flags); cls_copy_data_from_queue_to_uart(ch); } /* 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. */ } /* 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 */ Loading Loading @@ -804,109 +1034,22 @@ static void cls_enable_receiver(struct channel_t *ch) writeb(tmp, &ch->ch_cls_uart->ier); } static void cls_copy_data_from_uart_to_queue(struct channel_t *ch) /* * This function basically goes to sleep for secs, or until * it gets signalled that the port has fully drained. */ static int cls_drain(struct tty_struct *tty, uint seconds) { int qleft = 0; unsigned char linestatus = 0; unsigned char error_mask = 0; ushort head; ushort tail; unsigned long flags; struct channel_t *ch; struct un_t *un; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; if (!tty || tty->magic != TTY_MAGIC) return -ENXIO; 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++; } /* * 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); } /* * This function basically goes to sleep for secs, or until * it gets signalled that the port has fully drained. */ static int cls_drain(struct tty_struct *tty, uint seconds) { unsigned long flags; struct channel_t *ch; struct un_t *un; if (!tty || tty->magic != TTY_MAGIC) return -ENXIO; un = (struct un_t *)tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return -ENXIO; un = (struct un_t *)tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return -ENXIO; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) Loading @@ -926,199 +1069,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) Loading Loading @@ -1298,3 +1248,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 }; Loading
drivers/staging/dgnc/dgnc_cls.c +460 −489 Original line number Diff line number Diff line Loading @@ -26,56 +26,6 @@ #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); Loading Loading @@ -357,138 +307,418 @@ 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); } /* 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); /* * Discard character if we are ignoring the error mask. */ if (linestatus & error_mask) { linestatus = 0; readb(&ch->ch_cls_uart->txrx); continue; } /* 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. * 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++; } /* Parse any modem signal changes */ cls_parse_modem(ch, readb(&ch->ch_cls_uart->msr)); } 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++; } /* * cls_param() * Send any/all changes to the line to the UART. * Write new final heads to channel structure. */ static void cls_param(struct tty_struct *tty) ch->ch_r_head = head & RQUEUEMASK; ch->ch_e_head = head & EQUEUEMASK; 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 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; 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_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; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; bd = ch->ch_bd; if (!bd || bd->magic != DGNC_BOARD_MAGIC) return; spin_lock_irqsave(&ch->ch_lock, flags); /* * 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; /* No data to write to the UART */ if (ch->ch_w_tail == ch->ch_w_head) goto exit_unlock; cls_flush_uart_write(ch); cls_flush_uart_read(ch); /* 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; /* 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); 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) { /* * Bring back up RTS and DTR... * Also handle RTS or DTR toggle if set. * 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_digi.digi_flags & DIGI_RTS_TOGGLE) { if (!(ch->ch_mostat & UART_MCR_RTS)) { ch->ch_mostat |= (UART_MCR_RTS); if (!(ch->ch_digi.digi_flags & DIGI_DTR_TOGGLE)) 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--; } } else { int iindex = 0; int jindex = 0; 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 */ brd->intr_rx++; ch->ch_intr_rx++; 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); brd->intr_tx++; ch->ch_intr_tx++; spin_unlock_irqrestore(&ch->ch_lock, flags); cls_copy_data_from_queue_to_uart(ch); } /* 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. */ } /* 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 */ Loading Loading @@ -804,109 +1034,22 @@ static void cls_enable_receiver(struct channel_t *ch) writeb(tmp, &ch->ch_cls_uart->ier); } static void cls_copy_data_from_uart_to_queue(struct channel_t *ch) /* * This function basically goes to sleep for secs, or until * it gets signalled that the port has fully drained. */ static int cls_drain(struct tty_struct *tty, uint seconds) { int qleft = 0; unsigned char linestatus = 0; unsigned char error_mask = 0; ushort head; ushort tail; unsigned long flags; struct channel_t *ch; struct un_t *un; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) return; if (!tty || tty->magic != TTY_MAGIC) return -ENXIO; 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++; } /* * 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); } /* * This function basically goes to sleep for secs, or until * it gets signalled that the port has fully drained. */ static int cls_drain(struct tty_struct *tty, uint seconds) { unsigned long flags; struct channel_t *ch; struct un_t *un; if (!tty || tty->magic != TTY_MAGIC) return -ENXIO; un = (struct un_t *)tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return -ENXIO; un = (struct un_t *)tty->driver_data; if (!un || un->magic != DGNC_UNIT_MAGIC) return -ENXIO; ch = un->un_ch; if (!ch || ch->magic != DGNC_CHANNEL_MAGIC) Loading @@ -926,199 +1069,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) Loading Loading @@ -1298,3 +1248,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 };