📄 uart.c
字号:
sti(); p_cuser = (struct serial_icounter_struct *) arg; error = put_user(cnow.cts, &p_cuser->cts); if (error) return error; error = put_user(cnow.dsr, &p_cuser->dsr); if (error) return error; error = put_user(cnow.rng, &p_cuser->rng); if (error) return error; error = put_user(cnow.dcd, &p_cuser->dcd); if (error) return error; return 0; default: return -ENOIOCTLCMD; } return 0;}/* FIX UP modem control here someday......*/static void rs_8xx_set_termios(struct tty_struct *tty, struct termios *old_termios){ ser_info_t *info = (ser_info_t *)tty->driver_data; if ( (tty->termios->c_cflag == old_termios->c_cflag) && ( RELEVANT_IFLAG(tty->termios->c_iflag) == RELEVANT_IFLAG(old_termios->c_iflag))) return; change_speed(info);#ifdef modem_control /* Handle transition to B0 status */ if ((old_termios->c_cflag & CBAUD) && !(tty->termios->c_cflag & CBAUD)) { info->MCR &= ~(UART_MCR_DTR|UART_MCR_RTS); cli(); serial_out(info, UART_MCR, info->MCR); sti(); } /* Handle transition away from B0 status */ if (!(old_termios->c_cflag & CBAUD) && (tty->termios->c_cflag & CBAUD)) { info->MCR |= UART_MCR_DTR; if (!tty->hw_stopped || !(tty->termios->c_cflag & CRTSCTS)) { info->MCR |= UART_MCR_RTS; } cli(); serial_out(info, UART_MCR, info->MCR); sti(); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; rs_8xx_start(tty); }#endif#if 0 /* * No need to wake up processes in open wait, since they * sample the CLOCAL flag once, and don't recheck it. * XXX It's not clear whether the current behavior is correct * or not. Hence, this may change..... */ if (!(old_termios->c_cflag & CLOCAL) && (tty->termios->c_cflag & CLOCAL)) wake_up_interruptible(&info->open_wait);#endif}/* * ------------------------------------------------------------ * 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 void rs_8xx_close(struct tty_struct *tty, struct file * filp){ ser_info_t *info = (ser_info_t *)tty->driver_data; struct serial_state *state; unsigned long flags; int idx; volatile smc_t *smcp; volatile scc_t *sccp; if (!info || serial_paranoia_check(info, tty->name, "rs_close")) return; state = info->state; save_flags(flags); cli(); if (tty_hung_up_p(filp)) { DBG_CNT("before DEC-hung"); 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"); restore_flags(flags); return; } info->flags |= ASYNC_CLOSING; /* * 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. */ info->read_status_mask &= ~BD_SC_EMPTY; if (info->flags & ASYNC_INITIALIZED) { idx = PORT_NUM(info->state->smc_scc_num); if (info->state->smc_scc_num & NUM_IS_SCC) { sccp = &cpmp->cp_scc[idx]; sccp->scc_sccm &= ~UART_SCCM_RX; sccp->scc_gsmrl &= ~SCC_GSMRL_ENR; } else { smcp = &cpmp->cp_smc[idx]; smcp->smc_smcm &= ~SMCM_RX; smcp->smc_smcmr &= ~SMCMR_REN; } /* * Before we drop DTR, make sure the UART transmitter * has completely drained; this is especially * important if there is a transmit FIFO! */ rs_8xx_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) { current->state = TASK_INTERRUPTIBLE; schedule_timeout(info->close_delay); } wake_up_interruptible(&info->open_wait); } info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING); wake_up_interruptible(&info->close_wait); restore_flags(flags);}/* * rs_wait_until_sent() --- wait until the transmitter is empty */static void rs_8xx_wait_until_sent(struct tty_struct *tty, int timeout){ ser_info_t *info = (ser_info_t *)tty->driver_data; unsigned long orig_jiffies, char_time; /*int lsr;*/ volatile cbd_t *bdp; if (serial_paranoia_check(info, tty->name, "rs_wait_until_sent")) return;#ifdef maybe if (info->state->type == PORT_UNKNOWN) return;#endif 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 = 1; if (timeout) char_time = min(char_time, 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 /* We go through the loop at least once because we can't tell * exactly when the last character exits the shifter. There can * be at least two characters waiting to be sent after the buffers * are empty. */ do {#ifdef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT printk("lsr = %d (jiff=%lu)...", lsr, jiffies);#endif current->state = TASK_INTERRUPTIBLE;/* current->dyn_prio = 0; make us low-priority */ schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; /* The 'tx_cur' is really the next buffer to send. We * have to back up to the previous BD and wait for it * to go. This isn't perfect, because all this indicates * is the buffer is available. There are still characters * in the CPM FIFO. */ bdp = info->tx_cur; if (bdp == info->tx_bd_base) bdp += (TX_NUM_FIFO-1); else bdp--; } while (bdp->cbd_sc & BD_SC_READY); current->state = TASK_RUNNING;#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 void rs_8xx_hangup(struct tty_struct *tty){ ser_info_t *info = (ser_info_t *)tty->driver_data; struct serial_state *state = info->state; if (serial_paranoia_check(info, tty->name, "rs_hangup")) return; state = info->state; rs_8xx_flush_buffer(tty); shutdown(info); info->event = 0; state->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = 0; wake_up_interruptible(&info->open_wait);}/* * ------------------------------------------------------------ * rs_open() and friends * ------------------------------------------------------------ */static int block_til_ready(struct tty_struct *tty, struct file * filp, ser_info_t *info){#ifdef DO_THIS_LATER DECLARE_WAITQUEUE(wait, current);#endif struct serial_state *state = info->state; int retval; int do_clocal = 0; /* * 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);#ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) return -EAGAIN; else return -ERESTARTSYS;#else return -EAGAIN;#endif } /* * If non-blocking mode is set, or the port is not enabled, * then make the check up front and then exit. * If this is an SMC port, we don't have modem control to wait * for, so just get out here. */ if ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR)) || !(info->state->smc_scc_num & NUM_IS_SCC)) { info->flags |= ASYNC_NORMAL_ACTIVE; return 0; } 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;#ifdef DO_THIS_LATER 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 cli(); if (!tty_hung_up_p(filp)) state->count--; sti(); info->blocked_open++; while (1) { cli(); if ((tty->termios->c_cflag & CBAUD)) serial_out(info, UART_MCR, serial_inp(info, UART_MCR) | (UART_MCR_DTR | UART_MCR_RTS)); sti(); set_current_state(TASK_INTERRUPTIBLE); if (tty_hung_up_p(filp) || !(info->flags & ASYNC_INITIALIZED)) {#ifdef SERIAL_DO_RESTART if (info->flags & ASYNC_HUP_NOTIFY) retval = -EAGAIN; else retval = -ERESTARTSYS;#else retval = -EAGAIN;#endif break; } if (!(info->flags & ASYNC_CLOSING) && (do_clocal || (serial_in(info, UART_MSR) & UART_MSR_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(); } current->state = TASK_RUNNING; remove_wait_queue(&info->open_wait, &wait); if (!tty_hung_up_p(filp)) 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#endif /* DO_THIS_LATER */ if (retval) return retval; info->flags |= ASYNC_NORMAL_ACTIVE; return 0;}static int get_async_struct(int line, ser_info_t **ret_info){ struct serial_state *sstate; sstate = rs_table + line; if (sstate->info) { sstate->count++; *ret_info = (ser_info_t *)sstate->info; return 0; } else { return -ENOMEM; }}/* * 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 rs_8xx_open(struct tty_struct *tty, struct file * filp){ ser_info_t *info; int retval, line; line = tty->index; if ((line < 0) || (line >= NR_PORTS)) return -ENODEV; retval = get_async_struct(line, &info); if (retval) return retval; if (serial_paranoia_check(info, tty->name, "rs_open")) return -ENODEV;#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s, count = %d\n", tty->name, info->state->count);#endif tty->driver_data = info; info->tty = tty; /* * Start up serial port */ retval = startup(info); if (retval) return retval; retval = block_til_ready(tty, filp, info); if (retval) {#ifdef SERIAL_DEBUG_OPEN printk("rs_open returning after block_til_ready with %d\n", retval);#endif return retval; }#ifdef SERIAL_DEBUG_OPEN printk("rs_open %s successful...", tty->name);#endif return 0;}/* * /proc fs routines.... */static inline int line_info(char *buf, struct serial_state *state){#ifdef notdef struct async_struct *info = state->info, scr_info; char stat_buf[30], control, status;#endif int ret; ret = sprintf(buf, "%d: uart:%s port:%X irq:%d", state->line, (state->smc_scc_num & NUM_IS_SCC) ? "SCC" : "SMC", (unsigned int)(state->port), state->irq); if (!state->port || (state->type == PORT_UNKNOWN)) { ret += sprintf(buf+ret, "\n"); return ret; }#ifdef notdef /* * Figure out the current RS-232 lines */ if (!info) { info = &scr_info; /* This is just for serial_{in,out} */ info->magic = SERIAL_MAGIC; info->port = state->port; info->flags = state->flags; info->quot = 0; info->tty = 0; } cli(); status = serial_in(info, UART_MSR); control = info ? info->MCR : serial_in(info, UART_MCR); sti(); stat_buf[0] = 0; stat_buf[1] = 0; if (control & UART_MCR_RTS) strcat(stat_buf, "|RTS"); if (status & UART_MSR_CTS) strcat(stat_buf, "|CTS"); if (control & UART_MCR_DTR) strcat(stat_buf, "|DTR"); if (status & UART_MSR_DSR) strcat(stat_buf, "|DSR"); if (status & UART_MSR_DCD) strcat(stat_buf, "|CD"); if (status & UART_MSR_RI) strcat(stat_buf, "|RI"); if (info->quot) { ret += sprintf(buf+ret, " baud:%d", state->baud_base / info->quot); } ret += sprintf(buf+ret, " tx:%d rx:%d",
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -