static int ctc_tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear) { ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; DBF_TEXT(trace, 4, __FUNCTION__); if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl")) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; if (set & TIOCM_RTS) info->mcr |= UART_MCR_RTS; if (set & TIOCM_DTR) info->mcr |= UART_MCR_DTR; if (clear & TIOCM_RTS) info->mcr &= ~UART_MCR_RTS; if (clear & TIOCM_DTR) info->mcr &= ~UART_MCR_DTR; if ((set | clear) & (TIOCM_RTS|TIOCM_DTR)) ctc_tty_transmit_status(info); return 0; }
static void ctc_tty_flush_buffer(struct tty_struct *tty) { ctc_tty_info *info; unsigned long flags; DBF_TEXT(trace, 4, __FUNCTION__); if (!tty) goto ex; spin_lock_irqsave(&ctc_tty_lock, flags); info = (ctc_tty_info *) tty->driver_data; if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_buffer")) { spin_unlock_irqrestore(&ctc_tty_lock, flags); goto ex; } skb_queue_purge(&info->tx_queue); info->lsr |= UART_LSR_TEMT; spin_unlock_irqrestore(&ctc_tty_lock, flags); wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup) (tty); ex: DBF_TEXT_(trace, 2, "ex: %s ", __FUNCTION__); return; }
static void ctc_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 int ctc_tty_tiocmget(struct tty_struct *tty, struct file *file) { ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; u_char control, status; uint result; ulong flags; DBF_TEXT(trace, 4, __FUNCTION__); if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_ioctl")) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; control = info->mcr; spin_lock_irqsave(&ctc_tty_lock, flags); status = info->msr; spin_unlock_irqrestore(&ctc_tty_lock, 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 result; }
/* 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 int ctc_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; DBF_TEXT(trace, 5, __FUNCTION__); if (ctc_tty_shuttingdown) goto ex; if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_write")) goto ex; if (!tty) goto ex; if (!info->netdev) { total = -ENODEV; goto ex; } 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), (const u_char __user *)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; tasklet_schedule(&info->tasklet); } if (from_user) up(&info->write_sem); ex: DBF_TEXT(trace, 6, __FUNCTION__); return total; }
static int ctc_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 int ctc_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; }
/* 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 int ctc_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 void ctc_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_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */ static void ctc_tty_throttle(struct tty_struct *tty) { ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; DBF_TEXT(trace, 4, __FUNCTION__); if (ctc_tty_paranoia_check(info, tty->name, "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); }
/* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */ static int ctc_tty_open(struct tty_struct *tty, struct file *filp) { ctc_tty_info *info; unsigned long saveflags; int retval, line; line = MINOR(tty->device) - tty->driver.minor_start; if (line < 0 || line > CTC_TTY_MAX_DEVICES) return -ENODEV; info = &driver->info[line]; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_open")) return -ENODEV; if (!info->netdev) return -ENODEV; #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open %s%d, count = %d\n", tty->driver.name, info->line, info->count); #endif spin_lock_irqsave(&ctc_tty_lock, saveflags); info->count++; tty->driver_data = info; info->tty = tty; spin_unlock_irqrestore(&ctc_tty_lock, saveflags); /* * Start up serial port */ retval = ctc_tty_startup(info); if (retval) { #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open return after startup\n"); #endif return retval; } retval = ctc_tty_block_til_ready(tty, filp, info); if (retval) { #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open return after ctc_tty_block_til_ready \n"); #endif return retval; } if ((info->count == 1) && (info->flags & CTC_ASYNC_SPLIT_TERMIOS)) { *tty->termios = info->normal_termios; ctc_tty_change_speed(info); } #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open %s%d successful...\n", CTC_TTY_NAME, info->line); #endif return 0; }
static void ctc_tty_flush_chars(struct tty_struct *tty) { ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; DBF_TEXT(trace, 4, __FUNCTION__); if (ctc_tty_shuttingdown) return; if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_flush_chars")) return; if (tty->stopped || tty->hw_stopped || (!skb_queue_len(&info->tx_queue))) return; tasklet_schedule(&info->tasklet); }
static void ctc_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); }
/* * This routine is called whenever a serial port is opened. It * enables interrupts for a serial port, linking in its async structure into * the IRQ chain. It also performs the serial-specific * initialization for the tty structure. */ static int ctc_tty_open(struct tty_struct *tty, struct file *filp) { ctc_tty_info *info; unsigned long saveflags; int retval, line; DBF_TEXT(trace, 3, __FUNCTION__); line = tty->index; if (line < 0 || line > CTC_TTY_MAX_DEVICES) return -ENODEV; info = &driver->info[line]; if (ctc_tty_paranoia_check(info, tty->name, "ctc_tty_open")) return -ENODEV; if (!info->netdev) return -ENODEV; #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open %s, count = %d\n", tty->name, info->count); #endif spin_lock_irqsave(&ctc_tty_lock, saveflags); info->count++; tty->driver_data = info; info->tty = tty; spin_unlock_irqrestore(&ctc_tty_lock, saveflags); /* * Start up serial port */ retval = ctc_tty_startup(info); if (retval) { #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open return after startup\n"); #endif return retval; } retval = ctc_tty_block_til_ready(tty, filp, info); if (retval) { #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open return after ctc_tty_block_til_ready \n"); #endif return retval; } #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_open %s successful...\n", tty->name); #endif return 0; }
/* * ctc_tty_hangup() --- called by tty_hangup() when a hangup is signaled. */ static void ctc_tty_hangup(struct tty_struct *tty) { ctc_tty_info *info = (ctc_tty_info *)tty->driver_data; unsigned long saveflags; if (ctc_tty_paranoia_check(info, tty->device, "ctc_tty_hangup")) return; ctc_tty_shutdown(info); info->count = 0; info->flags &= ~CTC_ASYNC_NORMAL_ACTIVE; spin_lock_irqsave(&ctc_tty_lock, saveflags); info->tty = 0; spin_unlock_irqrestore(&ctc_tty_lock, saveflags); wake_up_interruptible(&info->open_wait); }
static int ctc_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 void ctc_tty_close(struct tty_struct *tty, struct file *filp) { ctc_tty_info *info = (ctc_tty_info *) tty->driver_data; unsigned long saveflags; ulong flags; ulong timeout; if (!info || ctc_tty_paranoia_check(info, tty->device, "ctc_tty_close")) return; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { restore_flags(flags); #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_close return after tty_hung_up_p\n"); #endif return; } if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->count should always * be one in these conditions. If it's greater than * one, we've got real problems, since it means the * serial port won't be shutdown. */ printk(KERN_ERR "ctc_tty_close: bad port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { printk(KERN_ERR "ctc_tty_close: bad port count for %s%d: %d\n", CTC_TTY_NAME, info->line, info->count); info->count = 0; } if (info->count) { restore_flags(flags); #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_close after info->count != 0\n"); #endif return; } info->flags |= CTC_ASYNC_CLOSING; /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & CTC_ASYNC_NORMAL_ACTIVE) info->normal_termios = *tty->termios; tty->closing = 1; /* * At this point we stop accepting input. To do this, we * disable the receive line status interrupts, and tell the * interrupt driver to stop checking the data ready bit in the * line status register. */ if (info->flags & CTC_ASYNC_INITIALIZED) { tty_wait_until_sent(tty, 3000); /* 30 seconds timeout */ /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ timeout = jiffies + HZ; while (!(info->lsr & UART_LSR_TEMT)) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(20); if (time_after(jiffies,timeout)) break; } } ctc_tty_shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); spin_lock_irqsave(&ctc_tty_lock, saveflags); info->tty = 0; spin_unlock_irqrestore(&ctc_tty_lock, saveflags); tty->closing = 0; if (info->blocked_open) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(50); wake_up_interruptible(&info->open_wait); } info->flags &= ~(CTC_ASYNC_NORMAL_ACTIVE | CTC_ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); restore_flags(flags); #ifdef CTC_DEBUG_MODEM_OPEN printk(KERN_DEBUG "ctc_tty_close normal exit\n"); #endif }