epca.c
来自「linux 内核源代码」· C语言 代码 · 共 2,328 行 · 第 1/5 页
C
2,328 行
/* Set baud rate, char size, stop bits, parity */ fepcmd(ch, SETCTRLFLAGS, (unsigned) cflag, 0, 0, 0); } /* * If the user has not forced CLOCAL and if the device is not a * CALLOUT device (Which is always CLOCAL) we set flags such * that the driver will wait on carrier detect. */ if (ts->c_cflag & CLOCAL) ch->asyncflags &= ~ASYNC_CHECK_CD; else ch->asyncflags |= ASYNC_CHECK_CD; mval = ch->m_dtr | ch->m_rts; } /* End CBAUD not detected */ iflag = termios2digi_i(ch, ts->c_iflag); /* Check input mode flags */ if (iflag != ch->fepiflag) { ch->fepiflag = iflag; /* * Command sets channels iflag structure on the board. Such * things as input soft flow control, handling of parity * errors, and break handling are all set here. */ /* break handling, parity handling, input stripping, flow control chars */ fepcmd(ch, SETIFLAGS, (unsigned int) ch->fepiflag, 0, 0, 0); } /* * Set the board mint value for this channel. This will cause hardware * events to be generated each time the DCD signal (Described in mint) * changes. */ writeb(ch->dcd, &bc->mint); if ((ts->c_cflag & CLOCAL) || (ch->digiext.digi_flags & DIGI_FORCEDCD)) if (ch->digiext.digi_flags & DIGI_FORCEDCD) writeb(0, &bc->mint); ch->imodem = readb(&bc->mstat); hflow = termios2digi_h(ch, ts->c_cflag); if (hflow != ch->hflow) { ch->hflow = hflow; /* * Hard flow control has been selected but the board is not * using it. Activate hard flow control now. */ fepcmd(ch, SETHFLOW, hflow, 0xff, 0, 1); } mval ^= ch->modemfake & (mval ^ ch->modem); if (ch->omodem ^ mval) { ch->omodem = mval; /* * The below command sets the DTR and RTS mstat structure. If * hard flow control is NOT active these changes will drive the * output of the actual DTR and RTS lines. If hard flow control * is active, the changes will be saved in the mstat structure * and only asserted when hard flow control is turned off. */ /* First reset DTR & RTS; then set them */ fepcmd(ch, SETMODEM, 0, ((ch->m_dtr)|(ch->m_rts)), 0, 1); fepcmd(ch, SETMODEM, mval, 0, 0, 1); } if (ch->startc != ch->fepstartc || ch->stopc != ch->fepstopc) { ch->fepstartc = ch->startc; ch->fepstopc = ch->stopc; /* * The XON / XOFF characters have changed; propagate these * changes to the card. */ fepcmd(ch, SONOFFC, ch->fepstartc, ch->fepstopc, 0, 1); } if (ch->startca != ch->fepstartca || ch->stopca != ch->fepstopca) { ch->fepstartca = ch->startca; ch->fepstopca = ch->stopca; /* * Similar to the above, this time the auxilarly XON / XOFF * characters have changed; propagate these changes to the card. */ fepcmd(ch, SAUXONOFFC, ch->fepstartca, ch->fepstopca, 0, 1); }}/* Caller holds lock */static void receive_data(struct channel *ch){ unchar *rptr; struct ktermios *ts = NULL; struct tty_struct *tty; struct board_chan __iomem *bc; int dataToRead, wrapgap, bytesAvailable; unsigned int tail, head; unsigned int wrapmask; /* * This routine is called by doint when a receive data event has taken * place. */ globalwinon(ch); if (ch->statusflags & RXSTOPPED) return; tty = ch->tty; if (tty) ts = tty->termios; bc = ch->brdchan; BUG_ON(!bc); wrapmask = ch->rxbufsize - 1; /* * Get the head and tail pointers to the receiver queue. Wrap the head * pointer if it has reached the end of the buffer. */ head = readw(&bc->rin); head &= wrapmask; tail = readw(&bc->rout) & wrapmask; bytesAvailable = (head - tail) & wrapmask; if (bytesAvailable == 0) return; /* If CREAD bit is off or device not open, set TX tail to head */ if (!tty || !ts || !(ts->c_cflag & CREAD)) { writew(head, &bc->rout); return; } if (tty_buffer_request_room(tty, bytesAvailable + 1) == 0) return; if (readb(&bc->orun)) { writeb(0, &bc->orun); printk(KERN_WARNING "epca; overrun! DigiBoard device %s\n",tty->name); tty_insert_flip_char(tty, 0, TTY_OVERRUN); } rxwinon(ch); while (bytesAvailable > 0) { /* Begin while there is data on the card */ wrapgap = (head >= tail) ? head - tail : ch->rxbufsize - tail; /* * Even if head has wrapped around only report the amount of * data to be equal to the size - tail. Remember memcpy can't * automaticly wrap around the receive buffer. */ dataToRead = (wrapgap < bytesAvailable) ? wrapgap : bytesAvailable; /* Make sure we don't overflow the buffer */ dataToRead = tty_prepare_flip_string(tty, &rptr, dataToRead); if (dataToRead == 0) break; /* * Move data read from our card into the line disciplines * buffer for translation if necessary. */ memcpy_fromio(rptr, ch->rxptr + tail, dataToRead); tail = (tail + dataToRead) & wrapmask; bytesAvailable -= dataToRead; } /* End while there is data on the card */ globalwinon(ch); writew(tail, &bc->rout); /* Must be called with global data */ tty_schedule_flip(ch->tty);}static int info_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ switch (cmd) { case DIGI_GETINFO: { struct digi_info di; int brd; if (get_user(brd, (unsigned int __user *)arg)) return -EFAULT; if (brd < 0 || brd >= num_cards || num_cards == 0) return -ENODEV; memset(&di, 0, sizeof(di)); di.board = brd; di.status = boards[brd].status; di.type = boards[brd].type ; di.numports = boards[brd].numports ; /* Legacy fixups - just move along nothing to see */ di.port = (unsigned char *)boards[brd].port ; di.membase = (unsigned char *)boards[brd].membase ; if (copy_to_user((void __user *)arg, &di, sizeof(di))) return -EFAULT; break; } case DIGI_POLLER: { int brd = arg & 0xff000000 >> 16; unsigned char state = arg & 0xff; if (brd < 0 || brd >= num_cards) { printk(KERN_ERR "epca: DIGI POLLER : brd not valid!\n"); return -ENODEV; } digi_poller_inhibited = state; break; } case DIGI_INIT: { /* * This call is made by the apps to complete the * initilization of the board(s). This routine is * responsible for setting the card to its initial * state and setting the drivers control fields to the * sutianle settings for the card in question. */ int crd; for (crd = 0; crd < num_cards; crd++) post_fep_init(crd); break; } default: return -ENOTTY; } return 0;}static int pc_tiocmget(struct tty_struct *tty, struct file *file){ struct channel *ch = (struct channel *) tty->driver_data; struct board_chan __iomem *bc; unsigned int mstat, mflag = 0; unsigned long flags; if (ch) bc = ch->brdchan; else return -EINVAL; spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); mstat = readb(&bc->mstat); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); if (mstat & ch->m_dtr) mflag |= TIOCM_DTR; if (mstat & ch->m_rts) mflag |= TIOCM_RTS; if (mstat & ch->m_cts) mflag |= TIOCM_CTS; if (mstat & ch->dsr) mflag |= TIOCM_DSR; if (mstat & ch->m_ri) mflag |= TIOCM_RI; if (mstat & ch->dcd) mflag |= TIOCM_CD; return mflag;}static int pc_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct channel *ch = (struct channel *) tty->driver_data; unsigned long flags; if (!ch) return -EINVAL; spin_lock_irqsave(&epca_lock, flags); /* * I think this modemfake stuff is broken. It doesn't correctly reflect * the behaviour desired by the TIOCM* ioctls. Therefore this is * probably broken. */ if (set & TIOCM_RTS) { ch->modemfake |= ch->m_rts; ch->modem |= ch->m_rts; } if (set & TIOCM_DTR) { ch->modemfake |= ch->m_dtr; ch->modem |= ch->m_dtr; } if (clear & TIOCM_RTS) { ch->modemfake |= ch->m_rts; ch->modem &= ~ch->m_rts; } if (clear & TIOCM_DTR) { ch->modemfake |= ch->m_dtr; ch->modem &= ~ch->m_dtr; } globalwinon(ch); /* * The below routine generally sets up parity, baud, flow control * issues, etc.... It effect both control flags and input flags. */ epcaparam(tty,ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); return 0;}static int pc_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ digiflow_t dflow; int retval; unsigned long flags; unsigned int mflag, mstat; unsigned char startc, stopc; struct board_chan __iomem *bc; struct channel *ch = (struct channel *) tty->driver_data; void __user *argp = (void __user *)arg; if (ch) bc = ch->brdchan; else return -EINVAL; /* * For POSIX compliance we need to add more ioctls. See tty_ioctl.c in * /usr/src/linux/drivers/char for a good example. In particular think * about adding TCSETAF, TCSETAW, TCSETA, TCSETSF, TCSETSW, TCSETS. */ switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); if (!arg) digi_send_break(ch, HZ / 4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); digi_send_break(ch, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: if (put_user(C_CLOCAL(tty)?1:0, (unsigned long __user *)arg)) return -EFAULT; return 0; case TIOCSSOFTCAR: { unsigned int value; if (get_user(value, (unsigned __user *)argp)) return -EFAULT; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (value ? CLOCAL : 0)); return 0; } case TIOCMODG: mflag = pc_tiocmget(tty, file); if (put_user(mflag, (unsigned long __user *)argp)) return -EFAULT; break; case TIOCMODS: if (get_user(mstat, (unsigned __user *)argp)) return -EFAULT; return pc_tiocmset(tty, file, mstat, ~mstat); case TIOCSDTR: spin_lock_irqsave(&epca_lock, flags); ch->omodem |= ch->m_dtr; globalwinon(ch); fepcmd(ch, SETMODEM, ch->m_dtr, 0, 10, 1); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case TIOCCDTR: spin_lock_irqsave(&epca_lock, flags); ch->omodem &= ~ch->m_dtr; globalwinon(ch); fepcmd(ch, SETMODEM, 0, ch->m_dtr, 10, 1); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case DIGI_GETA: if (copy_to_user(argp, &ch->digiext, sizeof(digi_t))) return -EFAULT; break; case DIGI_SETAW: case DIGI_SETAF: if (cmd == DIGI_SETAW) { /* Setup an event to indicate when the transmit buffer empties */ spin_lock_irqsave(&epca_lock, flags); setup_empty_event(tty,ch); spin_unlock_irqrestore(&epca_lock, flags); tty_wait_until_sent(tty, 0); } else { /* ldisc lock already held in ioctl */ if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); } /* Fall Thru */ case DIGI_SETA: if (copy_from_user(&ch->digiext, argp, sizeof(digi_t))) return -EFAULT; if (ch->digiext.digi_flags & DIGI_ALTPIN) { ch->dcd = ch->m_dsr; ch->dsr = ch->m_dcd; } else { ch->dcd = ch->m_dcd; ch->dsr = ch->m_dsr; } spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); /* * The below routine generally sets up parity, baud, flow * control issues, etc.... It effect both control flags and * input flags. */ epcaparam(tty,ch); memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); break; case DIGI_GETFLOW: case DIGI_GETAFLOW: spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); if (cmd == DIGI_GETFLOW) { dflow.startc = readb(&bc->startc); dflow.stopc = readb(&bc->stopc); } else { dflow.startc = readb(&bc->startca); dflow.stopc = readb(&bc->stopca); } memoff(ch); spin_unlock_irqrestore(&epca_lock, flags); if (copy_to_user(argp, &dflow, sizeof(dflow))) return -EFAULT; break; case DIGI_SETAFLOW: case DIGI_SETFLOW: if (cmd == DIGI_SETFLOW) { startc = ch->startc; stopc = ch->stopc; } else { startc = ch->startca; stopc = ch->stopca; } if (copy_from_user(&dflow, argp, sizeof(dflow))) return -EFAULT; if (dflow.startc != startc || dflow.stopc != stopc) { /* Begin if setflow toggled */ spin_lock_irqsave(&epca_lock, flags); globalwinon(ch); if (cmd == DI
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?