📄 serial_netarm.c
字号:
case TIOCGICOUNT: save_flags(flags); cli(); cnow = info->state->icount; restore_flags(flags); icount.cts = cnow.cts; icount.dsr = cnow.dsr; icount.rng = cnow.rng; icount.dcd = cnow.dcd; icount.rx = cnow.rx; icount.tx = cnow.tx; icount.frame = cnow.frame; icount.overrun = cnow.overrun; icount.parity = cnow.parity; icount.brk = cnow.brk; icount.buf_overrun = cnow.buf_overrun; if (copy_to_user((void *)arg, &icount, sizeof(icount))) return -EFAULT; return 0; case TIOCSERGWILD: case TIOCSERSWILD: /* "setserial -W" is called in Debian boot */ printk ("TIOCSER?WILD ioctl obsolete, ignored.\n"); return 0; default: return -ENOIOCTLCMD; } return 0;}static voidrs_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct netarm_async_struct *info = (struct netarm_async_struct *)tty->driver_data; unsigned int cflag = tty->termios->c_cflag; if ( (cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; change_speed(info, old_termios); /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_start(tty); }}/* * ------------------------------------------------------------ * rs_close() * * This routine is called when the serial port gets closed. First, we * wait for the last remaining data to be sent. Then, we unlink its * async structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. * ------------------------------------------------------------ */static voidrs_close(struct tty_struct *tty, struct file * filp){ struct netarm_async_struct * info = (struct netarm_async_struct *)tty->driver_data; struct serial_state *state; unsigned long flags; if (!info || serial_paranoia_check(info, tty->device, "rs_close")) return; state = info->state; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); MOD_DEC_USE_COUNT; restore_flags(flags); return; } #ifdef SERIAL_DEBUG_OPEN printk("rs_close ttyS%d, count = %d\n", info->line, state->count);#endif if ((tty->count == 1) && (state->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. state->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("rs_close: bad serial port count; tty->count is 1, " "state->count is %d\n", state->count); state->count = 1; } if (--state->count < 0) { printk("rs_close: bad serial port count for ttys%d: %d\n", info->line, state->count); state->count = 0; } if (state->count) { DBG_CNT("before DEC-2"); MOD_DEC_USE_COUNT; restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; restore_flags(flags); /* * Save the termios structure, since this port may have * separate termios for callout and dialin. */ if (info->flags & ASYNC_NORMAL_ACTIVE) info->state->normal_termios = *tty->termios; if (info->flags & ASYNC_CALLOUT_ACTIVE) info->state->callout_termios = *tty->termios; /* * Now we wait for the transmit buffer to clear; and we notify * the line discipline to only process XON/XOFF characters. */ tty->closing = 1; if (info->closing_wait != ASYNC_CLOSING_WAIT_NONE) tty_wait_until_sent(tty, info->closing_wait); /* * 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 & ASYNC_INITIALIZED) { info->registers->ctrl_a &= ~NETARM_SER_CTLA_IE_RX_ALL; /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ rs_wait_until_sent(tty, info->timeout); } shutdown(info); if (tty->driver.flush_buffer) tty->driver.flush_buffer(tty); if (tty->ldisc.flush_buffer) tty->ldisc.flush_buffer(tty); tty->closing = 0; info->event = 0; info->tty = 0; if (info->blocked_open) { if (info->close_delay) { set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE| ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); MOD_DEC_USE_COUNT;}/* * rs_wait_until_sent() --- wait until the transmitter is empty * (only used in rs_close? --rp) */static voidrs_wait_until_sent(struct tty_struct *tty, int timeout){ struct netarm_async_struct * info = (struct netarm_async_struct *)tty->driver_data; unsigned long orig_jiffies, char_time; int lsr; if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent")) return; if (info->state->type == PORT_UNKNOWN) return; if (info->xmit_fifo_size == 0) return; /* Just in case.... */ orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the estimated time to * send a single character, and make it at least 1. The check * interval should also be less than the timeout. * * Note: we have to use pretty tight timings here to satisfy * the NIST-PCTS. */ char_time = (info->timeout - HZ/50) / info->xmit_fifo_size; char_time = char_time / 5; if (char_time == 0) char_time = 1; if (timeout && timeout < char_time) char_time = timeout; /* * If the transmitter hasn't cleared in twice the approximate * amount of time to send the entire FIFO, it probably won't * ever clear. This assumes the UART isn't doing flow * control, which is currently the case. Hence, if it ever * takes longer than info->timeout, this is probably due to a * UART bug of some kind. So, we clamp the timeout parameter at * 2*info->timeout. */ if (!timeout || timeout > 2*info->timeout) timeout = 2*info->timeout;#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("In rs_wait_until_sent(%d) check=%lu...", timeout, char_time); printk("jiff=%lu...", jiffies);#endif while (!((lsr = info->registers->status_a) & NETARM_SER_STATA_TX_FULL)) {#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies);#endif set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; }#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...done\n", lsr, jiffies);#endif}/* * rs_hangup() --- called by tty_hangup() when a hangup is signaled. */static voidrs_hangup(struct tty_struct *tty){ struct netarm_async_struct * info = (struct netarm_async_struct *)tty->driver_data; struct serial_state *state = info->state; if (serial_paranoia_check(info, tty->device, "rs_hangup")) return; state = info->state; rs_flush_buffer(tty); if (info->flags & ASYNC_CLOSING) return; shutdown(info); info->event = 0; state->count = 0; info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CALLOUT_ACTIVE); info->tty = 0; wake_up_interruptible(&info->open_wait);}/* * ------------------------------------------------------------ * rs_open() and friends * ------------------------------------------------------------ */static intblock_til_ready(struct tty_struct *tty, struct file * filp, struct netarm_async_struct *info){ DECLARE_WAITQUEUE(wait, current); struct serial_state *state = info->state; int retval; int do_clocal = 0, extra_count = 0; unsigned long flags; /* * 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) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); return -EAGAIN; } /* * If this is a callout device, then just make sure the normal * device isn't being used. */ if (tty->driver.subtype == SERIAL_TYPE_CALLOUT) { if (info->flags & ASYNC_NORMAL_ACTIVE) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_SESSION_LOCKOUT) && (info->session != current->session)) return -EBUSY; if ((info->flags & ASYNC_CALLOUT_ACTIVE) && (info->flags & ASYNC_PGRP_LOCKOUT) && (info->pgrp != current->pgrp)) return -EBUSY; info->flags |= ASYNC_CALLOUT_ACTIVE; return 0; } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { if (info->flags & ASYNC_CALLOUT_ACTIVE) return -EBUSY; info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } if (info->flags & ASYNC_CALLOUT_ACTIVE) { if (state->normal_termios.c_cflag & CLOCAL) do_clocal = 1; } else { if (tty->termios->c_cflag & CLOCAL) do_clocal = 1; } /* * Block waiting for the carrier detect and the line to become * free (i.e., not in use by the callout). While we are in * this loop, state->count is dropped by one, so that * rs_close() knows when to free things. We restore it upon * exit, either normal or abnormal. */ retval = 0; add_wait_queue(&info->open_wait, &wait);#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready before block: ttys%d, count = %d\n", state->line, state->count);#endif save_flags(flags); cli(); if (!tty_hung_up_p(filp)) { extra_count = 1; state->count--; } restore_flags(flags); info->blocked_open++; while (1) { save_flags(flags); cli(); if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && (tty->termios->c_cflag & CBAUD)) info->registers->ctrl_a |= NETARM_SER_CTLA_DTR_EN | NETARM_SER_CTLA_RTS_EN; restore_flags(flags); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) { retval = -EAGAIN; break; } if (!(info->flags & ASYNC_CALLOUT_ACTIVE) && !(info->flags & ASYNC_CLOSING) && (do_clocal || (info->registers->status_a & NETARM_SER_STATA_DCD))) break; if (signal_pending(current)) { retval = -ERESTARTSYS; break; }#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready blocking: ttys%d, count = %d\n", info->line, state->count);#endif schedule(); } set_current_state(TASK_RUNNING); remove_wait_queue(&info->open_wait, &wait); if (extra_count) state->count++; info->blocked_open--;#ifdef SERIAL_DEBUG_OPEN printk("block_til_ready after blocking: ttys%d, count = %d\n", info->line, state->count);#endif if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static intget_async_struct(int line, struct netarm_async_struct **ret_info){ struct netarm_async_struct *info; struct serial_state *sstate; /* only two lines are available */ if ((line == 0) || (line == 1)) { sstate = rs_table + line; sstate->count++; if (sstate->info) { /* this shouldn't happen here */ *ret_info = (struct netarm_async_struct *) sstate->info; return 0; } info = &NAS_ports[line]; memset(info, 0, sizeof(struct netarm_async_struct)); init_waitqueue_head(&info->open_wait); init_waitqueue_head(&info->close_wait); init_waitqueue_head(&info->delta_msr_wait); info->magic = NAS_MAGIC; info->port = sstate->port; info->flags = sstate->flags; info->io_type = sstate->io_type; info->registers = get_serial_channel( line ); info->xmit_fifo_size = sstate->xmit_fifo_size; info->line = line; info->tqueue.routine = do_softint; info->tqueue.data = info; info->state = sstate; sstate->info = (struct async_struct *) info; *ret_info = info; return 0; } else return -ENODEV;}/* * 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 intrs_open(struct tty_struct *tty, struct file * filp){ struct netarm_async_struct *info; int retval, line; unsigned long page; MOD_INC_USE_COUNT; line = MINOR(tty->device) - tty->driver.minor_start; if ((line < 0) || (line >= NR_NAS_PORTS)) { MOD_DEC_USE_COUNT; return -ENODEV; } retval = get_async_struct(line, &info); if (retval) { MOD_DEC_USE_COUNT; return retval; } tty->driver_data = info; info->tty = tty; if (serial_paranoia_check(info, tty->device, "rs_open")) { MOD_DEC_USE_COUNT; return -ENODEV; }#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s%d, count = %d\n", tty->driver.name, info->line, info->state->count);#endif#if (LINUX_VERSION_CODE > 0x20100) info->tty->low_latency = (info->flags & ASYNC_LOW_LATENCY) ? 1 : 0;#endif if (!tmp_buf) { page = get_zeroed_page(GFP_KERNEL); if (tmp_buf) free_page(page); else { if (!page) { MOD_DEC_USE_COUNT; return -ENOMEM; } tmp_buf = (unsigned char *) page; } } /* * If the port is the middle of closing, bail out now */ if (tty_hung_up_p(filp) || (info->flags & ASYNC_CLOSING)) { if (info->flags & ASYNC_CLOSING) interruptible_sleep_on(&info->close_wait); MOD_DEC_USE_COUNT; return -EAGAIN; } /* * Start up serial port */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -