specialix.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 2,265 行 · 第 1/4 页
C
2,265 行
channel = sx_in(bp, CD186x_GICR) >> GICR_CHAN_OFF; if (channel < CD186x_NCH) { port = &sx_port[board_No(bp) * SX_NPORT + channel]; if (port->flags & ASYNC_INITIALIZED) { return port; } } printk(KERN_INFO "sx%d: %s interrupt from invalid port %d\n", board_No(bp), what, channel); return NULL;}static inline void sx_receive_exc(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char status; unsigned char ch; if (!(port = sx_get_port(bp, "Receive"))) return; tty = port->tty; if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port)); return; } #ifdef SX_REPORT_OVERRUN status = sx_in(bp, CD186x_RCSR); if (status & RCSR_OE) { port->overrun++;#if SPECIALIX_DEBUG printk(KERN_DEBUG "sx%d: port %d: Overrun. Total %ld overruns.\n", board_No(bp), port_No(port), port->overrun);#endif } status &= port->mark_mask;#else status = sx_in(bp, CD186x_RCSR) & port->mark_mask;#endif ch = sx_in(bp, CD186x_RDR); if (!status) { return; } if (status & RCSR_TOUT) { printk(KERN_INFO "sx%d: port %d: Receiver timeout. Hardware problems ?\n", board_No(bp), port_No(port)); return; } else if (status & RCSR_BREAK) {#ifdef SPECIALIX_DEBUG printk(KERN_DEBUG "sx%d: port %d: Handling break...\n", board_No(bp), port_No(port));#endif *tty->flip.flag_buf_ptr++ = TTY_BREAK; if (port->flags & ASYNC_SAK) do_SAK(tty); } else if (status & RCSR_PE) *tty->flip.flag_buf_ptr++ = TTY_PARITY; else if (status & RCSR_FE) *tty->flip.flag_buf_ptr++ = TTY_FRAME; else if (status & RCSR_OE) *tty->flip.flag_buf_ptr++ = TTY_OVERRUN; else *tty->flip.flag_buf_ptr++ = 0; *tty->flip.char_buf_ptr++ = ch; tty->flip.count++; schedule_delayed_work(&tty->flip.work, 1);}static inline void sx_receive(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; if (!(port = sx_get_port(bp, "Receive"))) return; tty = port->tty; count = sx_in(bp, CD186x_RDCR); #ifdef SX_REPORT_FIFO port->hits[count > 8 ? 9 : count]++;#endif while (count--) { if (tty->flip.count >= TTY_FLIPBUF_SIZE) { printk(KERN_INFO "sx%d: port %d: Working around flip buffer overflow.\n", board_No(bp), port_No(port)); break; } *tty->flip.char_buf_ptr++ = sx_in(bp, CD186x_RDR); *tty->flip.flag_buf_ptr++ = 0; tty->flip.count++; } schedule_delayed_work(&tty->flip.work, 1);}static inline void sx_transmit(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char count; if (!(port = sx_get_port(bp, "Transmit"))) return; tty = port->tty; if (port->IER & IER_TXEMPTY) { /* FIFO drained */ sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXEMPTY; sx_out(bp, CD186x_IER, port->IER); return; } if ((port->xmit_cnt <= 0 && !port->break_length) || tty->stopped || tty->hw_stopped) { sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXRDY; sx_out(bp, CD186x_IER, port->IER); return; } if (port->break_length) { if (port->break_length > 0) { if (port->COR2 & COR2_ETC) { sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_SBRK); port->COR2 &= ~COR2_ETC; } count = min_t(int, port->break_length, 0xff); sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_DELAY); sx_out(bp, CD186x_TDR, count); if (!(port->break_length -= count)) port->break_length--; } else { sx_out(bp, CD186x_TDR, CD186x_C_ESC); sx_out(bp, CD186x_TDR, CD186x_C_EBRK); sx_out(bp, CD186x_COR2, port->COR2); sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_CORCHG2); port->break_length = 0; } return; } count = CD186x_NFIFO; do { sx_out(bp, CD186x_TDR, port->xmit_buf[port->xmit_tail++]); port->xmit_tail = port->xmit_tail & (SERIAL_XMIT_SIZE-1); if (--port->xmit_cnt <= 0) break; } while (--count > 0); if (port->xmit_cnt <= 0) { sx_out(bp, CD186x_CAR, port_No(port)); port->IER &= ~IER_TXRDY; sx_out(bp, CD186x_IER, port->IER); } if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP);}static inline void sx_check_modem(struct specialix_board * bp){ struct specialix_port *port; struct tty_struct *tty; unsigned char mcr;#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "Modem intr. ");#endif if (!(port = sx_get_port(bp, "Modem"))) return; tty = port->tty; mcr = sx_in(bp, CD186x_MCR); printk ("mcr = %02x.\n", mcr); if ((mcr & MCR_CDCHG)) {#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "CD just changed... ");#endif if (sx_in(bp, CD186x_MSVR) & MSVR_CD) {#ifdef SPECIALIX_DEBUG printk ( "Waking up guys in open.\n");#endif wake_up_interruptible(&port->open_wait); } else {#ifdef SPECIALIX_DEBUG printk ( "Sending HUP.\n");#endif schedule_work(&port->tqueue_hangup); } } #ifdef SPECIALIX_BRAIN_DAMAGED_CTS if (mcr & MCR_CTSCHG) { if (sx_in(bp, CD186x_MSVR) & MSVR_CTS) { tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } sx_out(bp, CD186x_IER, port->IER); } if (mcr & MCR_DSSXHG) { if (sx_in(bp, CD186x_MSVR) & MSVR_DSR) { tty->hw_stopped = 0; port->IER |= IER_TXRDY; if (port->xmit_cnt <= port->wakeup_chars) sx_mark_event(port, RS_EVENT_WRITE_WAKEUP); } else { tty->hw_stopped = 1; port->IER &= ~IER_TXRDY; } sx_out(bp, CD186x_IER, port->IER); }#endif /* SPECIALIX_BRAIN_DAMAGED_CTS */ /* Clear change bits */ sx_out(bp, CD186x_MCR, 0);}/* The main interrupt processing routine */static irqreturn_t sx_interrupt(int irq, void *dev_id, struct pt_regs *regs){ unsigned char status; unsigned char ack; struct specialix_board *bp; unsigned long loop = 0; int saved_reg; bp = dev_id; if (!bp || !(bp->flags & SX_BOARD_ACTIVE)) {#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "sx: False interrupt. irq %d.\n", irq);#endif return IRQ_NONE; } saved_reg = bp->reg; while ((++loop < 16) && (status = (sx_in(bp, CD186x_SRSR) & (SRSR_RREQint | SRSR_TREQint | SRSR_MREQint)))) { if (status & SRSR_RREQint) { ack = sx_in(bp, CD186x_RRAR); if (ack == (SX_ID | GIVR_IT_RCV)) sx_receive(bp); else if (ack == (SX_ID | GIVR_IT_REXC)) sx_receive_exc(bp); else printk(KERN_ERR "sx%d: Bad receive ack 0x%02x.\n", board_No(bp), ack); } else if (status & SRSR_TREQint) { ack = sx_in(bp, CD186x_TRAR); if (ack == (SX_ID | GIVR_IT_TX)) sx_transmit(bp); else printk(KERN_ERR "sx%d: Bad transmit ack 0x%02x.\n", board_No(bp), ack); } else if (status & SRSR_MREQint) { ack = sx_in(bp, CD186x_MRAR); if (ack == (SX_ID | GIVR_IT_MODEM)) sx_check_modem(bp); else printk(KERN_ERR "sx%d: Bad modem ack 0x%02x.\n", board_No(bp), ack); } sx_out(bp, CD186x_EOIR, 0); /* Mark end of interrupt */ } bp->reg = saved_reg; outb (bp->reg, bp->base + SX_ADDR_REG); return IRQ_HANDLED;}/* * Routines for open & close processing. */void turn_ints_off (struct specialix_board *bp){ if (bp->flags & SX_BOARD_IS_PCI) { /* This was intended for enabeling the interrupt on the * PCI card. However it seems that it's already enabled * and as PCI interrupts can be shared, there is no real * reason to have to turn it off. */ } (void) sx_in_off (bp, 0); /* Turn off interrupts. */}void turn_ints_on (struct specialix_board *bp){ if (bp->flags & SX_BOARD_IS_PCI) { /* play with the PCI chip. See comment above. */ } (void) sx_in (bp, 0); /* Turn ON interrupts. */}/* Called with disabled interrupts */static inline int sx_setup_board(struct specialix_board * bp){ int error; if (bp->flags & SX_BOARD_ACTIVE) return 0; if (bp->flags & SX_BOARD_IS_PCI) error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT | SA_SHIRQ, "specialix IO8+", bp); else error = request_irq(bp->irq, sx_interrupt, SA_INTERRUPT, "specialix IO8+", bp); if (error) return error; turn_ints_on (bp); bp->flags |= SX_BOARD_ACTIVE; return 0;}/* Called with disabled interrupts */static inline void sx_shutdown_board(struct specialix_board *bp){ if (!(bp->flags & SX_BOARD_ACTIVE)) return; bp->flags &= ~SX_BOARD_ACTIVE; #if SPECIALIX_DEBUG > 2 printk ("Freeing IRQ%d for board %d.\n", bp->irq, board_No (bp));#endif free_irq(bp->irq, bp); turn_ints_off (bp);}/* * Setting up port characteristics. * Must be called with disabled interrupts */static void sx_change_speed(struct specialix_board *bp, struct specialix_port *port){ struct tty_struct *tty; unsigned long baud; long tmp; unsigned char cor1 = 0, cor3 = 0; unsigned char mcor1 = 0, mcor2 = 0; static unsigned long again; if (!(tty = port->tty) || !tty->termios) return; port->IER = 0; port->COR2 = 0; /* Select port on the board */ sx_out(bp, CD186x_CAR, port_No(port)); /* The Specialix board doens't implement the RTS lines. They are used to set the IRQ level. Don't touch them. */ if (SX_CRTSCTS(tty)) port->MSVR = MSVR_DTR | (sx_in(bp, CD186x_MSVR) & MSVR_RTS); else port->MSVR = (sx_in(bp, CD186x_MSVR) & MSVR_RTS);#ifdef DEBUG_SPECIALIX printk (KERN_DEBUG "sx: got MSVR=%02x.\n", port->MSVR);#endif baud = C_BAUD(tty); if (baud & CBAUDEX) { baud &= ~CBAUDEX; if (baud < 1 || baud > 2) port->tty->termios->c_cflag &= ~CBAUDEX; else baud += 15; } if (baud == 15) { if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud ++; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud += 2; } if (!baud_table[baud]) { /* Drop DTR & exit */#ifdef SPECIALIX_DEBUG printk (KERN_DEBUG "Dropping DTR... Hmm....\n");#endif if (!SX_CRTSCTS (tty)) { port -> MSVR &= ~ MSVR_DTR; sx_out(bp, CD186x_MSVR, port->MSVR ); } #ifdef DEBUG_SPECIALIX else printk (KERN_DEBUG "Can't drop DTR: no DTR.\n");#endif return; } else { /* Set DTR on */ if (!SX_CRTSCTS (tty)) { port ->MSVR |= MSVR_DTR; } } /* * Now we must calculate some speed depended things */ /* Set baud rate for port */ tmp = port->custom_divisor ; if ( tmp ) printk (KERN_INFO "sx%d: Using custom baud rate divisor %ld. \n" "This is an untested option, please be carefull.\n", port_No (port), tmp); else tmp = (((SX_OSCFREQ + baud_table[baud]/2) / baud_table[baud] + CD186x_TPC/2) / CD186x_TPC); if ((tmp < 0x10) && time_before(again, jiffies)) { again = jiffies + HZ * 60; /* Page 48 of version 2.0 of the CL-CD1865 databook */ if (tmp >= 12) { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" "Performance degradation is possible.\n" "Read specialix.txt for more info.\n", port_No (port), tmp); } else { printk (KERN_INFO "sx%d: Baud rate divisor is %ld. \n" "Warning: overstressing Cirrus chip. " "This might not work.\n" "Read specialix.txt for more info.\n", port_No (port), tmp); } } sx_out(bp, CD186x_RBPRH, (tmp >> 8) & 0xff); sx_out(bp, CD186x_TBPRH, (tmp >> 8) & 0xff); sx_out(bp, CD186x_RBPRL, tmp & 0xff); sx_out(bp, CD186x_TBPRL, tmp & 0xff); if (port->custom_divisor) { baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor; baud = ( baud + 5 ) / 10; } else baud = (baud_table[baud] + 5) / 10; /* Estimated CPS */ /* Two timer ticks seems enough to wakeup something like SLIP driver */ tmp = ((baud + HZ/2) / HZ) * 2 - CD186x_NFIFO; port->wakeup_chars = (tmp < 0) ? 0 : ((tmp >= SERIAL_XMIT_SIZE) ? SERIAL_XMIT_SIZE - 1 : tmp); /* Receiver timeout will be transmission time for 1.5 chars */ tmp = (SPECIALIX_TPS + SPECIALIX_TPS/2 + baud/2) / baud; tmp = (tmp > 0xff) ? 0xff : tmp; sx_out(bp, CD186x_RTPR, tmp); switch (C_CSIZE(tty)) { case CS5: cor1 |= COR1_5BITS; break; case CS6: cor1 |= COR1_6BITS; break; case CS7: cor1 |= COR1_7BITS; break; case CS8: cor1 |= COR1_8BITS; break; } if (C_CSTOPB(tty)) cor1 |= COR1_2SB; cor1 |= COR1_IGNORE; if (C_PARENB(tty)) { cor1 |= COR1_NORMPAR; if (C_PARODD(tty)) cor1 |= COR1_ODDP; if (I_INPCK(tty)) cor1 &= ~COR1_IGNORE; } /* Set marking of some errors */ port->mark_mask = RCSR_OE | RCSR_TOUT; if (I_INPCK(tty)) port->mark_mask |= RCSR_FE | RCSR_PE; if (I_BRKINT(tty) || I_PARMRK(tty)) port->mark_mask |= RCSR_BREAK; if (I_IGNPAR(tty)) port->mark_mask &= ~(RCSR_FE | RCSR_PE); if (I_IGNBRK(tty)) { port->mark_mask &= ~RCSR_BREAK; if (I_IGNPAR(tty)) /* Real raw mode. Ignore all */ port->mark_mask &= ~RCSR_OE; } /* Enable Hardware Flow Control */ if (C_CRTSCTS(tty)) {#ifdef SPECIALIX_BRAIN_DAMAGED_CTS port->IER |= IER_DSR | IER_CTS; mcor1 |= MCOR1_DSRZD | MCOR1_CTSZD; mcor2 |= MCOR2_DSROD | MCOR2_CTSOD; tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR));#else port->COR2 |= COR2_CTSAE; #endif } /* Enable Software Flow Control. FIXME: I'm not sure about this */ /* Some people reported that it works, but I still doubt it */ if (I_IXON(tty)) { port->COR2 |= COR2_TXIBE; cor3 |= (COR3_FCT | COR3_SCDE); if (I_IXANY(tty)) port->COR2 |= COR2_IXM; sx_out(bp, CD186x_SCHR1, START_CHAR(tty)); sx_out(bp, CD186x_SCHR2, STOP_CHAR(tty)); sx_out(bp, CD186x_SCHR3, START_CHAR(tty)); sx_out(bp, CD186x_SCHR4, STOP_CHAR(tty)); } if (!C_CLOCAL(tty)) { /* Enable CD check */ port->IER |= IER_CD; mcor1 |= MCOR1_CDZD; mcor2 |= MCOR2_CDOD; } if (C_CREAD(tty)) /* Enable receiver */ port->IER |= IER_RXD;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?