📄 ctctty.c
字号:
static intctc_tty_startup(ctc_tty_info * info){ if (info->flags & CTC_ASYNC_INITIALIZED) return 0;#ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "starting up %s%d ...\n", CTC_TTY_NAME, info->line);#endif /* * Now, initialize the UART */ info->mcr = UART_MCR_DTR | UART_MCR_RTS | UART_MCR_OUT2; if (info->tty) clear_bit(TTY_IO_ERROR, &info->tty->flags); /* * and set the speed of the serial port */ ctc_tty_change_speed(info); info->flags |= CTC_ASYNC_INITIALIZED; if (!(info->flags & CTC_ASYNC_NETDEV_OPEN)) info->netdev->open(info->netdev); info->flags |= CTC_ASYNC_NETDEV_OPEN; return 0;}static voidctc_tty_stopdev(unsigned long data){ ctc_tty_info *info = (ctc_tty_info *)data; if ((!info) || (!info->netdev) || (info->flags & CTC_ASYNC_INITIALIZED)) return; info->netdev->stop(info->netdev); info->flags &= ~CTC_ASYNC_NETDEV_OPEN;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static voidctc_tty_shutdown(ctc_tty_info * info){ if (!(info->flags & CTC_ASYNC_INITIALIZED)) return;#ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "Shutting down %s%d ....\n", CTC_TTY_NAME, info->line);#endif info->msr &= ~UART_MSR_RI; if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); mod_timer(&info->stoptimer, jiffies + (10 * HZ)); skb_queue_purge(&info->tx_queue); skb_queue_purge(&info->rx_queue); info->flags &= ~CTC_ASYNC_INITIALIZED;}/* ctc_tty_write() is the main send-routine. It is called from the upper * levels within the kernel to perform sending data. Depending on the * online-flag it either directs output to the at-command-interpreter or * to the lower level. Additional tasks done here: * - If online, check for escape-sequence (+++) * - If sending audio-data, call ctc_tty_DLEdown() to parse DLE-codes. * - If receiving audio-data, call ctc_tty_end_vrx() to abort if needed. * - If dialing, abort dial. */static intctc_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count){ int c; int total = 0; ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_shuttingdown) return 0; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_write")) return 0; if (!tty) return 0; if (!info->netdev) return -ENODEV; if (from_user) down(&info->write_sem); while (1) { struct sk_buff *skb; int skb_res; c = (count < CTC_TTY_XMIT_SIZE) ? count : CTC_TTY_XMIT_SIZE; if (c <= 0) break; skb_res = info->netdev->hard_header_len + sizeof(info->mcr) + + sizeof(__u32); skb = dev_alloc_skb(skb_res + c); if (!skb) { printk(KERN_WARNING "ctc_tty: Out of memory in %s%d write\n", CTC_TTY_NAME, info->line); break; } skb_reserve(skb, skb_res); if (from_user) copy_from_user(skb_put(skb, c), buf, c); else memcpy(skb_put(skb, c), buf, c); skb_queue_tail(&info->tx_queue, skb); buf += c; total += c; count -= c; } if (skb_queue_len(&info->tx_queue)) { info->lsr &= ~UART_LSR_TEMT; queue_task(&info->tq, &tq_immediate); mark_bh(IMMEDIATE_BH); } if (from_user) up(&info->write_sem); return total;}static intctc_tty_write_room(struct tty_struct *tty){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_write_room")) return 0; return CTC_TTY_XMIT_SIZE;}static intctc_tty_chars_in_buffer(struct tty_struct *tty){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_chars_in_buffer")) return 0; return 0;}static voidctc_tty_flush_buffer(struct tty_struct *tty){ ctc_tty_info *info; unsigned long flags; save_flags(flags); cli(); if (!tty) { restore_flags(flags); return; } info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_flush_buffer")) { restore_flags(flags); return; } skb_queue_purge(&info->tx_queue); info->lsr |= UART_LSR_TEMT; restore_flags(flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty);}static voidctc_tty_flush_chars(struct tty_struct *tty){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_shuttingdown) return; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_flush_chars")) return; if (tty->stopped || tty->hw_stopped || (!skb_queue_len(&info->tx_queue))) return; queue_task(&info->tq, &tq_immediate); mark_bh(IMMEDIATE_BH);}/* * ------------------------------------------------------------ * ctc_tty_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static voidctc_tty_throttle(struct tty_struct *tty){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_throttle")) return; info->mcr &= ~UART_MCR_RTS; if (I_IXOFF(tty)) ctc_tty_inject(info, STOP_CHAR(tty)); ctc_tty_transmit_status(info);}static voidctc_tty_unthrottle(struct tty_struct *tty){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_unthrottle")) return; info->mcr |= UART_MCR_RTS; if (I_IXOFF(tty)) ctc_tty_inject(info, START_CHAR(tty)); ctc_tty_transmit_status(info);}/* * ------------------------------------------------------------ * ctc_tty_ioctl() and friends * ------------------------------------------------------------ *//* * ctc_tty_get_lsr_info - get line status register info * * Purpose: Let user call ioctl() to get info when the UART physically * is emptied. On bus types like RS485, the transmitter must * release the bus after transmitting. This must be done when * the transmit shift register is empty, not be done when the * transmit holding register is empty. This functionality * allows RS485 driver to be written in user space. */static intctc_tty_get_lsr_info(ctc_tty_info * info, uint * value){ u_char status; uint result; ulong flags; save_flags(flags); cli(); status = info->lsr; restore_flags(flags); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); put_user(result, (uint *) value); return 0;}static intctc_tty_get_ctc_tty_info(ctc_tty_info * info, uint * value){ u_char control, status; uint result; ulong flags; control = info->mcr; save_flags(flags); cli(); status = info->msr; restore_flags(flags); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0) | ((status & UART_MSR_DCD) ? TIOCM_CAR : 0) | ((status & UART_MSR_RI) ? TIOCM_RNG : 0) | ((status & UART_MSR_DSR) ? TIOCM_DSR : 0) | ((status & UART_MSR_CTS) ? TIOCM_CTS : 0); put_user(result, (uint *) value); return 0;}static intctc_tty_set_ctc_tty_info(ctc_tty_info * info, uint cmd, uint * value){ uint arg; int old_mcr = info->mcr & (UART_MCR_RTS | UART_MCR_DTR); get_user(arg, (uint *) value); switch (cmd) { case TIOCMBIS:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCMBIS\n", CTC_TTY_NAME, info->line);#endif if (arg & TIOCM_RTS) info->mcr |= UART_MCR_RTS; if (arg & TIOCM_DTR) info->mcr |= UART_MCR_DTR; break; case TIOCMBIC:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCMBIC\n", CTC_TTY_NAME, info->line);#endif if (arg & TIOCM_RTS) info->mcr &= ~UART_MCR_RTS; if (arg & TIOCM_DTR) info->mcr &= ~UART_MCR_DTR; break; case TIOCMSET:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCMSET\n", CTC_TTY_NAME, info->line);#endif info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); break; default: return -EINVAL; } if ((info->mcr & (UART_MCR_RTS | UART_MCR_DTR)) != old_mcr) ctc_tty_transmit_status(info); return 0;}static intctc_tty_ioctl(struct tty_struct *tty, struct file *file, uint cmd, ulong arg){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; int error; int retval; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_ioctl")) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TCSBRK\n", CTC_TTY_NAME, info->line);#endif retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TCSBRKP\n", CTC_TTY_NAME, info->line);#endif retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); return 0; case TIOCGSOFTCAR:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCGSOFTCAR\n", CTC_TTY_NAME, info->line);#endif error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(long)); if (error) return error; put_user(C_CLOCAL(tty) ? 1 : 0, (ulong *) arg); return 0; case TIOCSSOFTCAR:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCSSOFTCAR\n", CTC_TTY_NAME, info->line);#endif error = verify_area(VERIFY_READ, (void *) arg, sizeof(long)); if (error) return error; get_user(arg, (ulong *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCMGET:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCMGET\n", CTC_TTY_NAME, info->line);#endif error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); if (error) return error; return ctc_tty_get_ctc_tty_info(info, (uint *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: error = verify_area(VERIFY_READ, (void *) arg, sizeof(uint)); if (error) return error; return ctc_tty_set_ctc_tty_info(info, cmd, (uint *) arg); case TIOCSERGETLSR: /* Get line status register */#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "%s%d ioctl TIOCSERGETLSR\n", CTC_TTY_NAME, info->line);#endif error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(uint)); if (error) return error; else return ctc_tty_get_lsr_info(info, (uint *) arg); default:#ifdef CTC_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "UNKNOWN ioctl 0x%08x on %s%d\n", cmd, CTC_TTY_NAME, info->line);#endif return -ENOIOCTLCMD; } return 0;}static voidctc_tty_set_termios(struct tty_struct *tty, struct termios *old_termios){ ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; unsigned int cflag = tty->termios->c_cflag; ctc_tty_change_speed(info); /* Handle transition to B0 */ if ((old_termios->c_cflag & CBAUD) && !(cflag & CBAUD)) { info->mcr &= ~(UART_MCR_DTR|UART_MCR_RTS); ctc_tty_transmit_status(info); } /* Handle transition from B0 to other */ if (!(old_termios->c_cflag & CBAUD) && (cflag & CBAUD)) { info->mcr |= UART_MCR_DTR; if (!(tty->termios->c_cflag & CRTSCTS) || !test_bit(TTY_THROTTLED, &tty->flags)) { info->mcr |= UART_MCR_RTS; } ctc_tty_transmit_status(info); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) tty->hw_stopped = 0;}/* * ------------------------------------------------------------ * ctc_tty_open() and friends * ------------------------------------------------------------ */static int
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -