📄 specialix.c
字号:
if (bp->flags & SX_BOARD_ACTIVE) return 0; 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; MOD_INC_USE_COUNT; return 0;}/* Called with disabled interrupts */extern 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); MOD_DEC_USE_COUNT;}/* * 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 int 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; /* Set input FIFO size (1-8 bytes) */ cor3 |= SPECIALIX_RXFIFO; /* Setting up CD186x channel registers */ sx_out(bp, CD186x_COR1, cor1); sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_COR3, cor3); /* Make CD186x know about registers change */ sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); /* Setting up modem option registers */#ifdef DEBUG_SPECIALIX printk ("Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2);#endif sx_out(bp, CD186x_MCOR1, mcor1); sx_out(bp, CD186x_MCOR2, mcor2); /* Enable CD186x transmitter & receiver */ sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_TXEN | CCR_RXEN); /* Enable interrupts */ sx_out(bp, CD186x_IER, port->IER); /* And finally set the modem lines... */ sx_out(bp, CD186x_MSVR, port->MSVR);}/* Must be called with interrupts enabled */static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port){ unsigned long flags; if (port->flags & ASYNC_INITIALIZED) return 0; if (!port->xmit_buf) { /* We may sleep in get_free_page() */ unsigned long tmp; if (!(tmp = get_free_page(GFP_KERNEL))) return -ENOMEM; if (port->xmit_buf) { free_page(tmp); return -ERESTARTSYS; } port->xmit_buf = (unsigned char *) tmp; } save_flags(flags); cli(); if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); if (port->count == 1) bp->count++; port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; sx_change_speed(bp, port); port->flags |= ASYNC_INITIALIZED; restore_flags(flags); return 0;}/* Must be called with interrupts disabled */static void sx_shutdown_port(struct specialix_board *bp, struct specialix_port *port){ struct tty_struct *tty; if (!(port->flags & ASYNC_INITIALIZED)) return; #ifdef SX_REPORT_OVERRUN printk(KERN_INFO "sx%d: port %d: Total %ld overruns were detected.\n", board_No(bp), port_No(port), port->overrun);#endif #ifdef SX_REPORT_FIFO { int i; printk(KERN_INFO "sx%d: port %d: FIFO hits [ ", board_No(bp), port_No(port)); for (i = 0; i < 10; i++) { printk("%ld ", port->hits[i]); } printk("].\n"); }#endif if (port->xmit_buf) { free_page((unsigned long) port->xmit_buf); port->xmit_buf = NULL; } /* Select port */ sx_out(bp, CD186x_CAR, port_No(port)); if (!(tty = port->tty) || C_HUPCL(tty)) { /* Drop DTR */ sx_out(bp, CD186x_MSVDTR, 0); } /* Reset port */ sx_wait_CCR(bp); sx_out(bp, CD186x_CCR, CCR_SOFTRESET); /* Disable all interrupts from this port */ port->IER = 0; sx_out(bp, CD186x_IER, port->IER); if (tty) set_bit(TTY_IO_ERROR, &tty->flags); port->flags &= ~ASYNC_INITIALIZED; if (--bp->count < 0) { printk(KERN_ERR "sx%d: sx_shutdown_port: bad board count: %d\n", board_No(bp), bp->count); bp->count = 0; } /* * If this is the last opened port on the board * shutdown whole board */ if (!bp->count) sx_shutdown_board(bp);} static int block_til_ready(struct tty_struct *tty, struct file * filp, struct specialix_port *port){ DECLARE_WAITQUEUE(wait, current); struct specialix_board *bp = port_Board(port); int retval; int do_clocal = 0; int CD; /* * If the device is in the middle of being closed, then block * until it's done, and then try again. */ if (tty_hung_up_p(filp) || port->flags & ASYNC_CLOSING) { interruptible_sleep_on(&port->close_wait); if (port->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS; } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if (tty->driver.subtype == SPECIALIX_TYPE_CALLOUT) { if (port->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; if ((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_SESSION_LOCKOUT) && (port->session != current->session)) return -EBUSY; if ((port->flags & ASYNC_CALLOUT_ACTIVE) && (port->flags & ASYNC_PGRP_LOCKOUT) && (port->pgrp != current->pgrp)) return -EBUSY; port->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { if (port->flags & ASYNC_CALLOUT_ACTIVE) return -EBUSY; port->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (port->flags & ASYNC_CALLOUT_ACTIVE) { if (port->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (C_CLOCAL(tty)) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, info->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&port->open_wait, &wait); cli(); if (!tty_hung_up_p(filp)) port->count--; sti(); port->blocked_open++; while (1) { cli(); sx_out(bp, CD186x_CAR, port_No(port)); CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (!(port->flags & ASYNC_CALLOUT_ACTIVE)) { if (SX_CRTSCTS (tty)) { /* Activate RTS */ port->MSVR |= MSVR_DTR; sx_out (bp, CD186x_MSVR, port->MSVR); } else { /* Activate DTR */ port->MSVR |= MSVR_DTR; sx_out (bp, CD186x_MSVR, port->MSVR); } } sti(); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(port->flags & ASYNC_INITIALIZED)) { if (port->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS; break; } if (!(port->flags & ASYNC_CALLOUT_ACTIVE) && !(port->flags & ASYNC_CLOSING) && (do_clocal || CD)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } current->state = TASK_RUNNING; remove_wait_queue(&port->open_wait, &wait); if (!tty_hung_up_p(filp)) port->count++; port->blocked_open--; if (retval) return retval; port->flags |= ASYNC_NORMAL_ACTIVE; return 0;} static int sx_open(struct tty_struct * tty, struct file * filp){ int board; int error; struct specialix_port * port; struct specialix_board * bp;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -