📄 moxa.c
字号:
} if (!MoxaPortIsValid(port)) { tty->driver_data = NULL; return (-ENODEV); } ch = &moxa_ports[port]; ch->count++; tty->driver_data = ch; ch->tty = tty; if (!(ch->asyncflags & ASYNC_INITIALIZED)) { ch->statusflags = 0; moxa_set_tty_param(tty); MoxaPortLineCtrl(ch->port, 1, 1); MoxaPortEnable(ch->port); ch->asyncflags |= ASYNC_INITIALIZED; } retval = moxa_block_till_ready(tty, filp, ch); moxa_unthrottle(tty); if (ch->type == PORT_16550A) { MoxaSetFifo(ch->port, 1); } else { MoxaSetFifo(ch->port, 0); } return (retval);}static void moxa_close(struct tty_struct *tty, struct file *filp){ struct moxa_port *ch; int port; port = tty->index; if (port == MAX_PORTS) { return; } if (!MoxaPortIsValid(port)) { pr_debug("Invalid portno in moxa_close\n"); tty->driver_data = NULL; return; } if (tty->driver_data == NULL) { return; } if (tty_hung_up_p(filp)) { return; } ch = (struct moxa_port *) tty->driver_data; if ((tty->count == 1) && (ch->count != 1)) { printk(KERN_WARNING "moxa_close: bad serial port count; " "tty->count is 1, ch->count is %d\n", ch->count); ch->count = 1; } if (--ch->count < 0) { printk(KERN_WARNING "moxa_close: bad serial port count, " "device=%s\n", tty->name); ch->count = 0; } if (ch->count) { return; } ch->asyncflags |= ASYNC_CLOSING; ch->cflag = tty->termios->c_cflag; if (ch->asyncflags & ASYNC_INITIALIZED) { moxa_setup_empty_event(tty); tty_wait_until_sent(tty, 30 * HZ); /* 30 seconds timeout */ del_timer_sync(&moxa_ports[ch->port].emptyTimer); } moxa_shut_down(ch); MoxaPortFlushData(port, 2); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; ch->event = 0; ch->tty = NULL; if (ch->blocked_open) { if (ch->close_delay) { msleep_interruptible(jiffies_to_msecs(ch->close_delay)); } wake_up_interruptible(&ch->open_wait); } ch->asyncflags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_CLOSING); complete_all(&ch->close_wait);}static int moxa_write(struct tty_struct *tty, const unsigned char *buf, int count){ struct moxa_port *ch; int len, port; unsigned long flags; ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return (0); port = ch->port; spin_lock_irqsave(&moxa_lock, flags); len = MoxaPortWriteData(port, (unsigned char *) buf, count); spin_unlock_irqrestore(&moxa_lock, flags); /********************************************* if ( !(ch->statusflags & LOWWAIT) && ((len != count) || (MoxaPortTxFree(port) <= 100)) ) ************************************************/ ch->statusflags |= LOWWAIT; return (len);}static int moxa_write_room(struct tty_struct *tty){ struct moxa_port *ch; if (tty->stopped) return (0); ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return (0); return (MoxaPortTxFree(ch->port));}static void moxa_flush_buffer(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return; MoxaPortFlushData(ch->port, 1); tty_wakeup(tty);}static int moxa_chars_in_buffer(struct tty_struct *tty){ int chars; struct moxa_port *ch = (struct moxa_port *) tty->driver_data; /* * Sigh...I have to check if driver_data is NULL here, because * if an open() fails, the TTY subsystem eventually calls * tty_wait_until_sent(), which calls the driver's chars_in_buffer() * routine. And since the open() failed, we return 0 here. TDJ */ if (ch == NULL) return (0); chars = MoxaPortTxQueue(ch->port); if (chars) { /* * Make it possible to wakeup anything waiting for output * in tty_ioctl.c, etc. */ if (!(ch->statusflags & EMPTYWAIT)) moxa_setup_empty_event(tty); } return (chars);}static void moxa_flush_chars(struct tty_struct *tty){ /* * Don't think I need this, because this is called to empty the TX * buffer for the 16450, 16550, etc. */}static void moxa_put_char(struct tty_struct *tty, unsigned char c){ struct moxa_port *ch; int port; unsigned long flags; ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return; port = ch->port; spin_lock_irqsave(&moxa_lock, flags); MoxaPortWriteData(port, &c, 1); spin_unlock_irqrestore(&moxa_lock, flags); /************************************************ if ( !(ch->statusflags & LOWWAIT) && (MoxaPortTxFree(port) <= 100) ) *************************************************/ ch->statusflags |= LOWWAIT;}static int moxa_tiocmget(struct tty_struct *tty, struct file *file){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; int port; int flag = 0, dtr, rts; port = tty->index; if ((port != MAX_PORTS) && (!ch)) return (-EINVAL); MoxaPortGetLineOut(ch->port, &dtr, &rts); if (dtr) flag |= TIOCM_DTR; if (rts) flag |= TIOCM_RTS; dtr = MoxaPortLineStatus(ch->port); if (dtr & 1) flag |= TIOCM_CTS; if (dtr & 2) flag |= TIOCM_DSR; if (dtr & 4) flag |= TIOCM_CD; return flag;}static int moxa_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; int port; int dtr, rts; port = tty->index; if ((port != MAX_PORTS) && (!ch)) return (-EINVAL); MoxaPortGetLineOut(ch->port, &dtr, &rts); if (set & TIOCM_RTS) rts = 1; if (set & TIOCM_DTR) dtr = 1; if (clear & TIOCM_RTS) rts = 0; if (clear & TIOCM_DTR) dtr = 0; MoxaPortLineCtrl(ch->port, dtr, rts); return 0;}static int moxa_ioctl(struct tty_struct *tty, struct file *file, unsigned int cmd, unsigned long arg){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; register int port; void __user *argp = (void __user *)arg; int retval; port = tty->index; if ((port != MAX_PORTS) && (!ch)) return (-EINVAL); switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return (retval); moxa_setup_empty_event(tty); tty_wait_until_sent(tty, 0); if (!arg) MoxaPortSendBreak(ch->port, 0); return (0); case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return (retval); moxa_setup_empty_event(tty); tty_wait_until_sent(tty, 0); MoxaPortSendBreak(ch->port, arg); return (0); case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long __user *) argp); case TIOCSSOFTCAR: if(get_user(retval, (unsigned long __user *) argp)) return -EFAULT; arg = retval; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); if (C_CLOCAL(tty)) ch->asyncflags &= ~ASYNC_CHECK_CD; else ch->asyncflags |= ASYNC_CHECK_CD; return (0); case TIOCGSERIAL: return moxa_get_serial_info(ch, argp); case TIOCSSERIAL: return moxa_set_serial_info(ch, argp); default: retval = MoxaDriverIoctl(cmd, arg, port); } return (retval);}static void moxa_throttle(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; ch->statusflags |= THROTTLE;}static void moxa_unthrottle(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; ch->statusflags &= ~THROTTLE;}static void moxa_set_termios(struct tty_struct *tty, struct ktermios *old_termios){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return; moxa_set_tty_param(tty); if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&ch->open_wait);}static void moxa_stop(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return; MoxaPortTxDisable(ch->port); ch->statusflags |= TXSTOPPED;}static void moxa_start(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; if (ch == NULL) return; if (!(ch->statusflags & TXSTOPPED)) return; MoxaPortTxEnable(ch->port); ch->statusflags &= ~TXSTOPPED;}static void moxa_hangup(struct tty_struct *tty){ struct moxa_port *ch = (struct moxa_port *) tty->driver_data; moxa_flush_buffer(tty); moxa_shut_down(ch); ch->event = 0; ch->count = 0; ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; ch->tty = NULL; wake_up_interruptible(&ch->open_wait);}static void moxa_poll(unsigned long ignored){ register int card; struct moxa_port *ch; struct tty_struct *tp; int i, ports; del_timer(&moxaTimer); if (MoxaDriverPoll() < 0) { mod_timer(&moxaTimer, jiffies + HZ / 50); return; } for (card = 0; card < MAX_BOARDS; card++) { if ((ports = MoxaPortsOfCard(card)) <= 0) continue; ch = &moxa_ports[card * MAX_PORTS_PER_BOARD]; for (i = 0; i < ports; i++, ch++) { if ((ch->asyncflags & ASYNC_INITIALIZED) == 0) continue; if (!(ch->statusflags & THROTTLE) && (MoxaPortRxQueue(ch->port) > 0)) moxa_receive_data(ch); if ((tp = ch->tty) == 0) continue; if (ch->statusflags & LOWWAIT) { if (MoxaPortTxQueue(ch->port) <= WAKEUP_CHARS) { if (!tp->stopped) { ch->statusflags &= ~LOWWAIT; tty_wakeup(tp); } } } if (!I_IGNBRK(tp) && (MoxaPortResetBrkCnt(ch->port) > 0)) { tty_insert_flip_char(tp, 0, TTY_BREAK); tty_schedule_flip(tp); } if (MoxaPortDCDChange(ch->port)) { if (ch->asyncflags & ASYNC_CHECK_CD) { if (MoxaPortDCDON(ch->port)) wake_up_interruptible(&ch->open_wait); else { tty_hangup(tp); wake_up_interruptible(&ch->open_wait); ch->asyncflags &= ~ASYNC_NORMAL_ACTIVE; } } } } } mod_timer(&moxaTimer, jiffies + HZ / 50);}/******************************************************************************/static void moxa_set_tty_param(struct tty_struct *tty){ register struct ktermios *ts; struct moxa_port *ch; int rts, cts, txflow, rxflow, xany; ch = (struct moxa_port *) tty->driver_data; ts = tty->termios; if (ts->c_cflag & CLOCAL) ch->asyncflags &= ~ASYNC_CHECK_CD; else ch->asyncflags |= ASYNC_CHECK_CD; rts = cts = txflow = rxflow = xany = 0; if (ts->c_cflag & CRTSCTS) rts = cts = 1; if (ts->c_iflag & IXON) txflow = 1; if (ts->c_iflag & IXOFF) rxflow = 1; if (ts->c_iflag & IXANY) xany = 1; MoxaPortFlowCtrl(ch->port, rts, cts, txflow, rxflow, xany); MoxaPortSetTermio(ch->port, ts, tty_get_baud_rate(tty));}static int moxa_block_till_ready(struct tty_struct *tty, struct file *filp, struct moxa_port *ch){ DECLARE_WAITQUEUE(wait,current); unsigned long flags; int retval; int do_clocal = C_CLOCAL(tty); /* * 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) || (ch->asyncflags & ASYNC_CLOSING)) { if (ch->asyncflags & ASYNC_CLOSING) wait_for_completion_interruptible(&ch->close_wait);#ifdef SERIAL_DO_RESTART if (ch->asyncflags & ASYNC_HUP_NOTIFY) return (-EAGAIN); else return (-ERESTARTSYS);#else return (-EAGAIN);#endif } /* * If non-blocking mode is set, then make the check up front * and then exit. */ if (filp->f_flags & O_NONBLOCK) { ch->asyncflags |= ASYNC_NORMAL_ACTIVE; return (0); } /* * Block waiting for the carrier detect and the line to become free */ retval = 0; add_wait_queue(&ch->open_wait, &wait); pr_debug("block_til_ready before block: ttys%d, count = %d\n", ch->port, ch->count); spin_lock_irqsave(&moxa_lock, flags); if (!tty_hung_up_p(filp)) ch->count--; ch->blocked_open++; spin_unlock_irqrestore(&moxa_lock, flags); while (1) { set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(ch->asyncflags & ASYNC_INITIALIZED)) {#ifdef SERIAL_DO_RESTART
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -