📄 isdn_tty.c
字号:
cmd.command =CAPI_PUT_MESSAGE;/* info->dialing = 1; strcpy(dev->num[i], n); isdn_info_update();*/ isdn_command(&cmd); }}static inline intisdn_tty_paranoia_check(modem_info * info, kdev_t device, const char *routine){#ifdef MODEM_PARANOIA_CHECK if (!info) { printk(KERN_WARNING "isdn_tty: null info_struct for (%d, %d) in %s\n", major(device), minor(device), routine); return 1; } if (info->magic != ISDN_ASYNC_MAGIC) { printk(KERN_WARNING "isdn_tty: bad magic for modem struct (%d, %d) in %s\n", major(device), minor(device), routine); return 1; }#endif return 0;}/* * This routine is called to set the UART divisor registers to match * the specified baud rate for a serial port. */static voidisdn_tty_change_speed(modem_info * info){ uint cflag, cval, fcr, quot; int i; if (!info->tty || !info->tty->termios) return; cflag = info->tty->termios->c_cflag; quot = i = cflag & CBAUD; if (i & CBAUDEX) { i &= ~CBAUDEX; if (i < 1 || i > 2) info->tty->termios->c_cflag &= ~CBAUDEX; else i += 15; } if (quot) { info->mcr |= UART_MCR_DTR; isdn_tty_modem_ncarrier(info); } else { info->mcr &= ~UART_MCR_DTR; if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) {#ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in changespeed\n");#endif if (info->online) info->ncarrier = 1; isdn_tty_modem_reset_regs(info, 0); isdn_tty_modem_hup(info, 1); } return; } /* byte size and parity */ cval = cflag & (CSIZE | CSTOPB); cval >>= 4; if (cflag & PARENB) cval |= UART_LCR_PARITY; if (!(cflag & PARODD)) cval |= UART_LCR_EPAR; fcr = 0; /* CTS flow control flag and modem status interrupts */ if (cflag & CRTSCTS) { info->flags |= ISDN_ASYNC_CTS_FLOW; } else info->flags &= ~ISDN_ASYNC_CTS_FLOW; if (cflag & CLOCAL) info->flags &= ~ISDN_ASYNC_CHECK_CD; else { info->flags |= ISDN_ASYNC_CHECK_CD; }}static intisdn_tty_startup(modem_info * info){ ulong flags; if (info->flags & ISDN_ASYNC_INITIALIZED) return 0; save_flags(flags); cli(); isdn_MOD_INC_USE_COUNT();#ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "starting up ttyi%d ...\n", 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 */ isdn_tty_change_speed(info); info->flags |= ISDN_ASYNC_INITIALIZED; info->msr |= (UART_MSR_DSR | UART_MSR_CTS); info->send_outstanding = 0; restore_flags(flags); return 0;}/* * This routine will shutdown a serial port; interrupts are disabled, and * DTR is dropped if the hangup on close termio flag is on. */static voidisdn_tty_shutdown(modem_info * info){ ulong flags; if (!(info->flags & ISDN_ASYNC_INITIALIZED)) return;#ifdef ISDN_DEBUG_MODEM_OPEN printk(KERN_DEBUG "Shutting down isdnmodem port %d ....\n", info->line);#endif save_flags(flags); cli(); /* Disable interrupts */ isdn_MOD_DEC_USE_COUNT(); info->msr &= ~UART_MSR_RI; if (!info->tty || (info->tty->termios->c_cflag & HUPCL)) { info->mcr &= ~(UART_MCR_DTR | UART_MCR_RTS); if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0);#ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in isdn_tty_shutdown\n");#endif isdn_tty_modem_hup(info, 1); } } if (info->tty) set_bit(TTY_IO_ERROR, &info->tty->flags); info->flags &= ~ISDN_ASYNC_INITIALIZED; restore_flags(flags);}/* isdn_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 isdn_tty_DLEdown() to parse DLE-codes. * - If receiving audio-data, call isdn_tty_end_vrx() to abort if needed. * - If dialing, abort dial. */static intisdn_tty_write(struct tty_struct *tty, int from_user, const u_char * buf, int count){ int c; int total = 0; modem_info *info = (modem_info *) tty->driver_data; atemu *m = &info->emu; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write")) return 0; if (from_user) down(&info->write_sem); /* See isdn_tty_senddown() */ atomic_inc(&info->xmit_lock); while (1) { c = count; if (c > info->xmit_size - info->xmit_count) c = info->xmit_size - info->xmit_count; if (info->isdn_driver >= 0 && c > dev->drv[info->isdn_driver]->maxbufsize) c = dev->drv[info->isdn_driver]->maxbufsize; if (c <= 0) break; if ((info->online > 1)#ifdef CONFIG_ISDN_AUDIO || (info->vonline & 3)#endif ) {#ifdef CONFIG_ISDN_AUDIO if (!info->vonline)#endif isdn_tty_check_esc(buf, m->mdmreg[REG_ESC], c, &(m->pluscount), &(m->lastplus), from_user); if (from_user) copy_from_user(&(info->xmit_buf[info->xmit_count]), buf, c); else memcpy(&(info->xmit_buf[info->xmit_count]), buf, c);#ifdef CONFIG_ISDN_AUDIO if (info->vonline) { int cc = isdn_tty_handleDLEdown(info, m, c); if (info->vonline & 2) { if (!cc) { /* If DLE decoding results in zero-transmit, but * c originally was non-zero, do a wakeup. */ if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty); wake_up_interruptible(&tty->write_wait); info->msr |= UART_MSR_CTS; info->lsr |= UART_LSR_TEMT; } info->xmit_count += cc; } if ((info->vonline & 3) == 1) { /* Do NOT handle Ctrl-Q or Ctrl-S * when in full-duplex audio mode. */ if (isdn_tty_end_vrx(buf, c, from_user)) { info->vonline &= ~1;#ifdef ISDN_DEBUG_MODEM_VOICE printk(KERN_DEBUG "got !^Q/^S, send DLE-ETX,VCON on ttyI%d\n", info->line);#endif isdn_tty_at_cout("\020\003\r\nVCON\r\n", info); } } } else if (TTY_IS_FCLASS1(info)) { int cc = isdn_tty_handleDLEdown(info, m, c); if (info->vonline & 4) { /* ETX seen */ isdn_ctrl c; c.command = ISDN_CMD_FAXCMD; c.driver = info->isdn_driver; c.arg = info->isdn_channel; c.parm.aux.cmd = ISDN_FAX_CLASS1_CTRL; c.parm.aux.subcmd = ETX; isdn_command(&c); } info->vonline = 0;#ifdef ISDN_DEBUG_MODEM_VOICE printk(KERN_DEBUG "fax dle cc/c %d/%d\n", cc, c);#endif info->xmit_count += cc; } else#endif info->xmit_count += c; } else { info->msr |= UART_MSR_CTS; info->lsr |= UART_LSR_TEMT; if (info->dialing) { info->dialing = 0;#ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in isdn_tty_write\n");#endif isdn_tty_modem_result(RESULT_NO_CARRIER, info); isdn_tty_modem_hup(info, 1); } else c = isdn_tty_edit_at(buf, c, info, from_user); } buf += c; count -= c; total += c; } atomic_dec(&info->xmit_lock); if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) { if (m->mdmreg[REG_DXMT] & BIT_DXMT) { isdn_tty_senddown(info); isdn_tty_tint(info); } isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1); } if (from_user) up(&info->write_sem); return total;}static intisdn_tty_write_room(struct tty_struct *tty){ modem_info *info = (modem_info *) tty->driver_data; int ret; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_write_room")) return 0; if (!info->online) return info->xmit_size; ret = info->xmit_size - info->xmit_count; return (ret < 0) ? 0 : ret;}static intisdn_tty_chars_in_buffer(struct tty_struct *tty){ modem_info *info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_chars_in_buffer")) return 0; if (!info->online) return 0; return (info->xmit_count);}static voidisdn_tty_flush_buffer(struct tty_struct *tty){ modem_info *info; unsigned long flags; save_flags(flags); cli(); if (!tty) { restore_flags(flags); return; } info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_buffer")) { restore_flags(flags); return; } isdn_tty_cleanup_xmit(info); info->xmit_count = 0; 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 voidisdn_tty_flush_chars(struct tty_struct *tty){ modem_info *info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_flush_chars")) return; if ((info->xmit_count) || (skb_queue_len(&info->xmit_queue))) isdn_timer_ctrl(ISDN_TIMER_MODEMXMIT, 1);}/* * ------------------------------------------------------------ * isdn_tty_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static voidisdn_tty_throttle(struct tty_struct *tty){ modem_info *info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_throttle")) return; if (I_IXOFF(tty)) info->x_char = STOP_CHAR(tty); info->mcr &= ~UART_MCR_RTS;}static voidisdn_tty_unthrottle(struct tty_struct *tty){ modem_info *info = (modem_info *) tty->driver_data; if (isdn_tty_paranoia_check(info, tty->device, "isdn_tty_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else info->x_char = START_CHAR(tty); } info->mcr |= UART_MCR_RTS;}/* * ------------------------------------------------------------ * isdn_tty_ioctl() and friends * ------------------------------------------------------------ *//* * isdn_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 intisdn_tty_get_lsr_info(modem_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); return put_user(result, (uint *) value);}static intisdn_tty_get_modem_info(modem_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); return put_user(result, (uint *) value);}static intisdn_tty_set_modem_info(modem_info * info, uint cmd, uint * value){ uint arg; int pre_dtr; if (get_user(arg, (uint *) value)) return -EFAULT; switch (cmd) { case TIOCMBIS:#ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIS\n", info->line);#endif if (arg & TIOCM_RTS) { info->mcr |= UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->mcr |= UART_MCR_DTR; isdn_tty_modem_ncarrier(info); } break; case TIOCMBIC:#ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "ttyI%d ioctl TIOCMBIC\n", info->line);#endif if (arg & TIOCM_RTS) { info->mcr &= ~UART_MCR_RTS; } if (arg & TIOCM_DTR) { info->mcr &= ~UART_MCR_DTR; if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0);#ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMBIC\n");#endif if (info->online) info->ncarrier = 1; isdn_tty_modem_hup(info, 1); } } break; case TIOCMSET:#ifdef ISDN_DEBUG_MODEM_IOCTL printk(KERN_DEBUG "ttyI%d ioctl TIOCMSET\n", info->line);#endif pre_dtr = (info->mcr & UART_MCR_DTR); info->mcr = ((info->mcr & ~(UART_MCR_RTS | UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0) | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); if (pre_dtr |= (info->mcr & UART_MCR_DTR)) { if (!(info->mcr & UART_MCR_DTR)) { if (info->emu.mdmreg[REG_DTRHUP] & BIT_DTRHUP) { isdn_tty_modem_reset_regs(info, 0);#ifdef ISDN_DEBUG_MODEM_HUP printk(KERN_DEBUG "Mhup in TIOCMSET\n");#endif if (info->online) info->ncarrier = 1; isdn_tty_modem_hup(info, 1); } } else isdn_tty_modem_ncarrier(info); } break; default:
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -