specialix.c
来自「linux 内核源代码」· C语言 代码 · 共 2,562 行 · 第 1/5 页
C
2,562 行
func_exit();}/* * 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; unsigned long flags; func_enter(); if (!(tty = port->tty) || !tty->termios) { func_exit(); return; } port->IER = 0; port->COR2 = 0; /* Select port on the board */ spin_lock_irqsave(&bp->lock, flags); 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); spin_unlock_irqrestore(&bp->lock, flags); dprintk (SX_DEBUG_TERMIOS, "sx: got MSVR=%02x.\n", port->MSVR); baud = tty_get_baud_rate(tty); if (baud == 38400) { if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI) baud = 57600; if ((port->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI) baud = 115200; } if (!baud) { /* Drop DTR & exit */ dprintk (SX_DEBUG_TERMIOS, "Dropping DTR... Hmm....\n"); if (!SX_CRTSCTS (tty)) { port -> MSVR &= ~ MSVR_DTR; spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_MSVR, port->MSVR ); spin_unlock_irqrestore(&bp->lock, flags); } else dprintk (SX_DEBUG_TERMIOS, "Can't drop DTR: no DTR.\n"); 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/2) / 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); } } spin_lock_irqsave(&bp->lock, flags); 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); spin_unlock_irqrestore(&bp->lock, flags); if (port->custom_divisor) baud = (SX_OSCFREQ + port->custom_divisor/2) / port->custom_divisor; baud = (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; spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_RTPR, tmp); spin_unlock_irqrestore(&bp->lock, flags); 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; spin_lock_irqsave(&bp->lock, flags); tty->hw_stopped = !(sx_in(bp, CD186x_MSVR) & (MSVR_CTS|MSVR_DSR)); spin_unlock_irqrestore(&bp->lock, flags);#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; spin_lock_irqsave(&bp->lock, flags); 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)); spin_unlock_irqrestore(&bp->lock, flags); } 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 |= sx_rxfifo; /* Setting up CD186x channel registers */ spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_COR1, cor1); sx_out(bp, CD186x_COR2, port->COR2); sx_out(bp, CD186x_COR3, cor3); spin_unlock_irqrestore(&bp->lock, flags); /* Make CD186x know about registers change */ sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CCR, CCR_CORCHG1 | CCR_CORCHG2 | CCR_CORCHG3); /* Setting up modem option registers */ dprintk (SX_DEBUG_TERMIOS, "Mcor1 = %02x, mcor2 = %02x.\n", mcor1, mcor2); sx_out(bp, CD186x_MCOR1, mcor1); sx_out(bp, CD186x_MCOR2, mcor2); spin_unlock_irqrestore(&bp->lock, flags); /* Enable CD186x transmitter & receiver */ sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); 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); spin_unlock_irqrestore(&bp->lock, flags); func_exit();}/* Must be called with interrupts enabled */static int sx_setup_port(struct specialix_board *bp, struct specialix_port *port){ unsigned long flags; func_enter(); if (port->flags & ASYNC_INITIALIZED) { func_exit(); return 0; } if (!port->xmit_buf) { /* We may sleep in get_zeroed_page() */ unsigned long tmp; if (!(tmp = get_zeroed_page(GFP_KERNEL))) { func_exit(); return -ENOMEM; } if (port->xmit_buf) { free_page(tmp); func_exit(); return -ERESTARTSYS; } port->xmit_buf = (unsigned char *) tmp; } spin_lock_irqsave(&port->lock, flags); if (port->tty) clear_bit(TTY_IO_ERROR, &port->tty->flags); port->xmit_cnt = port->xmit_head = port->xmit_tail = 0; sx_change_speed(bp, port); port->flags |= ASYNC_INITIALIZED; spin_unlock_irqrestore(&port->lock, flags); func_exit(); 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; int i; unsigned long flags; func_enter(); if (!(port->flags & ASYNC_INITIALIZED)) { func_exit(); return; } if (sx_debug & SX_DEBUG_FIFO) { dprintk(SX_DEBUG_FIFO, "sx%d: port %d: %ld overruns, FIFO hits [ ", board_No(bp), port_No(port), port->overrun); for (i = 0; i < 10; i++) { dprintk(SX_DEBUG_FIFO, "%ld ", port->hits[i]); } dprintk(SX_DEBUG_FIFO, "].\n"); } if (port->xmit_buf) { free_page((unsigned long) port->xmit_buf); port->xmit_buf = NULL; } /* Select port */ spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); if (!(tty = port->tty) || C_HUPCL(tty)) { /* Drop DTR */ sx_out(bp, CD186x_MSVDTR, 0); } spin_unlock_irqrestore(&bp->lock, flags); /* Reset port */ sx_wait_CCR(bp); spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CCR, CCR_SOFTRESET); /* Disable all interrupts from this port */ port->IER = 0; sx_out(bp, CD186x_IER, port->IER); spin_unlock_irqrestore(&bp->lock, flags); if (tty) set_bit(TTY_IO_ERROR, &tty->flags); port->flags &= ~ASYNC_INITIALIZED; if (!bp->count) sx_shutdown_board(bp); func_exit();}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; unsigned long flags; func_enter(); /* * 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) { func_exit(); return -EAGAIN; } else { func_exit(); return -ERESTARTSYS; } } /* * 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))) { port->flags |= ASYNC_NORMAL_ACTIVE; func_exit(); return 0; } 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); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { port->count--; } spin_unlock_irqrestore(&port->lock, flags); port->blocked_open++; while (1) { spin_lock_irqsave(&bp->lock, flags); sx_out(bp, CD186x_CAR, port_No(port)); CD = sx_in(bp, CD186x_MSVR) & MSVR_CD; if (SX_CRTSCTS (tty)) { /* Activate RTS */ port->MSVR |= MSVR_DTR; /* WTF? */ sx_out (bp, CD186x_MSVR, port->MSVR); } else { /* Activate DTR */ port->MSVR |= MSVR_DTR; sx_out (bp, CD186x_MSVR, port->MSVR); } spin_unlock_irqrestore(&bp->lock, flags); 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_CLOSING) && (do_clocal || CD)) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; } schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&port->open_wait, &wait); spin_lock_irqsave(&port->lock, flags); if (!tty_hung_up_p(filp)) { port->count++; } port->blocked_open--; spin_unlock_irqrestore(&port->lock, flags); if (retval) { func_exit(); return retval; } port->flags |= ASYNC_NORMAL_ACTIVE; func_exit(); 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; int i; unsigned long flags; func_enter(); board = SX_BOARD(tty->index); if (board >= SX_NBOARD || !(sx_board[board].flags & SX_BOARD_PRESENT)) { func_exit(); return -ENODEV; } bp = &sx_board[board]; port = sx_port + board * SX_NPORT + SX_PORT(tty->index); port->overrun = 0; for (i = 0; i < 10; i++) port->hits[i]=0; dprintk (SX_DEBUG_OPEN, "Board = %d, bp = %p, port = %p, portno = %d.\n", board, bp, port, SX_PORT(tty->index)); if (sx_paranoia_check(port, tty->name, "sx_open")) { func_enter(); return -ENODEV; } if ((error = sx_setup_board(bp))) { func_exit(); return error; } spin_lock_irqsave(&bp->lock, flags); port->count++; bp->count++; tty->driver_data = port; port->tty = tty; spin_unlock_irqrestore(&bp->lock, flags); if ((error = sx_setup_port(bp, port))) { func_enter(); return error; } if ((error = block_til_ready(tty, filp, port))) { func_enter(); return error; } func_exit(); return 0;}static void sx_close(struct tty_struct * tty, struct file * filp){ struct specialix_port *port = (struct specialix_port *) tty->driver_data; struct specialix_board *bp; unsigned long flags; unsigned long timeout; func_enter(); if (!port || sx_paranoia_check(port, tty->name, "close")) { func_exit(); return; } spin_lock_irqsave(&port->lock, flags); if (tty_hung_up_p(filp)) { spin_unlock_irqrestore(&port->lock, flags);
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?