📄 uart.c
字号:
*cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; /* Get next BD. */ if (bdp->cbd_sc & BD_SC_WRAP) bdp = info->tx_bd_base; else bdp++; info->tx_cur = (cbd_t *)bdp;}static int rs_8xx_write(struct tty_struct * tty, int from_user, const unsigned char *buf, int count){ int c, ret = 0; ser_info_t *info = (ser_info_t *)tty->driver_data; volatile cbd_t *bdp; unsigned char *cp;#ifdef CONFIG_KGDB_CONSOLE /* Try to let stub handle output. Returns true if it did. */ if (kgdb_output_string(buf, count)) return ret;#endif if (serial_paranoia_check(info, tty->name, "rs_write")) return 0; if (!tty) return 0; bdp = info->tx_cur; while (1) { c = min(count, TX_BUF_SIZE); if (c <= 0) break; if (bdp->cbd_sc & BD_SC_READY) { info->flags |= TX_WAKEUP; break; } cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); if (from_user) { if (copy_from_user((void *)cp, buf, c)) { if (!ret) ret = -EFAULT; break; } } else { memcpy((void *)cp, buf, c); } bdp->cbd_datlen = c; bdp->cbd_sc |= BD_SC_READY; buf += c; count -= c; ret += c; /* Get next BD. */ if (bdp->cbd_sc & BD_SC_WRAP) bdp = info->tx_bd_base; else bdp++; info->tx_cur = (cbd_t *)bdp; } return ret;}static int rs_8xx_write_room(struct tty_struct *tty){ ser_info_t *info = (ser_info_t *)tty->driver_data; int ret; if (serial_paranoia_check(info, tty->name, "rs_write_room")) return 0; if ((info->tx_cur->cbd_sc & BD_SC_READY) == 0) { info->flags &= ~TX_WAKEUP; ret = TX_BUF_SIZE; } else { info->flags |= TX_WAKEUP; ret = 0; } return ret;}/* I could track this with transmit counters....maybe later.*/static int rs_8xx_chars_in_buffer(struct tty_struct *tty){ ser_info_t *info = (ser_info_t *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_chars_in_buffer")) return 0; return 0;}static void rs_8xx_flush_buffer(struct tty_struct *tty){ ser_info_t *info = (ser_info_t *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_flush_buffer")) return; /* There is nothing to "flush", whatever we gave the CPM * is on its way out. */ wake_up_interruptible(&tty->write_wait); if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) && tty->ldisc.write_wakeup) (tty->ldisc.write_wakeup)(tty); info->flags &= ~TX_WAKEUP;}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void rs_8xx_send_xchar(struct tty_struct *tty, char ch){ volatile cbd_t *bdp; unsigned char *cp; ser_info_t *info = (ser_info_t *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "rs_send_char")) return; bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); cp = info->tx_va_base + ((bdp - info->tx_bd_base) * TX_BUF_SIZE); *cp = ch; bdp->cbd_datlen = 1; bdp->cbd_sc |= BD_SC_READY; /* Get next BD. */ if (bdp->cbd_sc & BD_SC_WRAP) bdp = info->tx_bd_base; else bdp++; info->tx_cur = (cbd_t *)bdp;}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_8xx_throttle(struct tty_struct * tty){ ser_info_t *info = (ser_info_t *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("throttle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->name, "rs_throttle")) return; if (I_IXOFF(tty)) rs_8xx_send_xchar(tty, STOP_CHAR(tty));#ifdef modem_control if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; cli(); serial_out(info, UART_MCR, info->MCR); sti();#endif}static void rs_8xx_unthrottle(struct tty_struct * tty){ ser_info_t *info = (ser_info_t *)tty->driver_data;#ifdef SERIAL_DEBUG_THROTTLE char buf[64]; printk("unthrottle %s: %d....\n", _tty_name(tty, buf), tty->ldisc.chars_in_buffer(tty));#endif if (serial_paranoia_check(info, tty->name, "rs_unthrottle")) return; if (I_IXOFF(tty)) { if (info->x_char) info->x_char = 0; else rs_8xx_send_xchar(tty, START_CHAR(tty)); }#ifdef modem_control if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS; cli(); serial_out(info, UART_MCR, info->MCR); sti();#endif}/* * ------------------------------------------------------------ * rs_ioctl() and friends * ------------------------------------------------------------ */#ifdef maybe/* * 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 an RS485 driver to be written in user space. */static int get_lsr_info(struct async_struct * info, unsigned int *value){ unsigned char status; unsigned int result; cli(); status = serial_in(info, UART_LSR); sti(); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value);}#endifstatic int get_modem_info(ser_info_t *info, unsigned int *value){ unsigned int result = 0;#ifdef modem_control unsigned char control, status; control = info->MCR; cli(); status = serial_in(info, UART_MSR); sti(); result = ((control & UART_MCR_RTS) ? TIOCM_RTS : 0) | ((control & UART_MCR_DTR) ? TIOCM_DTR : 0)#ifdef TIOCM_OUT1 | ((control & UART_MCR_OUT1) ? TIOCM_OUT1 : 0) | ((control & UART_MCR_OUT2) ? TIOCM_OUT2 : 0)#endif | ((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);#endif return put_user(result,value);}static int set_modem_info(ser_info_t *info, unsigned int cmd, unsigned int *value){ int error; unsigned int arg; error = get_user(arg, value); if (error) return error;#ifdef modem_control switch (cmd) { case TIOCMBIS: if (arg & TIOCM_RTS) info->MCR |= UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR |= UART_MCR_DTR;#ifdef TIOCM_OUT1 if (arg & TIOCM_OUT1) info->MCR |= UART_MCR_OUT1; if (arg & TIOCM_OUT2) info->MCR |= UART_MCR_OUT2;#endif break; case TIOCMBIC: if (arg & TIOCM_RTS) info->MCR &= ~UART_MCR_RTS; if (arg & TIOCM_DTR) info->MCR &= ~UART_MCR_DTR;#ifdef TIOCM_OUT1 if (arg & TIOCM_OUT1) info->MCR &= ~UART_MCR_OUT1; if (arg & TIOCM_OUT2) info->MCR &= ~UART_MCR_OUT2;#endif break; case TIOCMSET: info->MCR = ((info->MCR & ~(UART_MCR_RTS |#ifdef TIOCM_OUT1 UART_MCR_OUT1 | UART_MCR_OUT2 |#endif UART_MCR_DTR)) | ((arg & TIOCM_RTS) ? UART_MCR_RTS : 0)#ifdef TIOCM_OUT1 | ((arg & TIOCM_OUT1) ? UART_MCR_OUT1 : 0) | ((arg & TIOCM_OUT2) ? UART_MCR_OUT2 : 0)#endif | ((arg & TIOCM_DTR) ? UART_MCR_DTR : 0)); break; default: return -EINVAL; } cli(); serial_out(info, UART_MCR, info->MCR); sti();#endif return 0;}/* Sending a break is a two step process on the SMC/SCC. It is accomplished * by sending a STOP TRANSMIT command followed by a RESTART TRANSMIT * command. We take advantage of the begin/end functions to make this * happen. */static ushort smc_chan_map[] = { CPM_CR_CH_SMC1, CPM_CR_CH_SMC2};static ushort scc_chan_map[] = { CPM_CR_CH_SCC1, CPM_CR_CH_SCC2, CPM_CR_CH_SCC3, CPM_CR_CH_SCC4};static void begin_break(ser_info_t *info){ volatile cpm8xx_t *cp; ushort chan; int idx; cp = cpmp; idx = PORT_NUM(info->state->smc_scc_num); if (info->state->smc_scc_num & NUM_IS_SCC) chan = scc_chan_map[idx]; else chan = smc_chan_map[idx]; cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG);}static void end_break(ser_info_t *info){ volatile cpm8xx_t *cp; ushort chan; int idx; cp = cpmp; idx = PORT_NUM(info->state->smc_scc_num); if (info->state->smc_scc_num & NUM_IS_SCC) chan = scc_chan_map[idx]; else chan = smc_chan_map[idx]; cp->cp_cpcr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG);}/* * This routine sends a break character out the serial port. */static void send_break(ser_info_t *info, int duration){ current->state = TASK_INTERRUPTIBLE;#ifdef SERIAL_DEBUG_SEND_BREAK printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);#endif begin_break(info); schedule_timeout(duration); end_break(info);#ifdef SERIAL_DEBUG_SEND_BREAK printk("done jiffies=%lu\n", jiffies);#endif}static int rs_8xx_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ int error; ser_info_t *info = (ser_info_t *)tty->driver_data; int retval; struct async_icount cnow; /* kernel counter temps */ struct serial_icounter_struct *p_cuser; /* user space */ if (serial_paranoia_check(info, tty->name, "rs_ioctl")) return -ENODEV; if ((cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) { if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; } switch (cmd) { case TCSBRK: /* SVID version: non-zero arg --> no break */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); if (signal_pending(current)) return -EINTR; if (!arg) { send_break(info, HZ/4); /* 1/4 second */ if (signal_pending(current)) return -EINTR; } return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); if (signal_pending(current)) return -EINTR; send_break(info, arg ? arg*(HZ/10) : HZ/4); if (signal_pending(current)) return -EINTR; return 0; case TIOCSBRK: retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); begin_break(info); return 0; case TIOCCBRK: retval = tty_check_change(tty); if (retval) return retval; end_break(info); return 0; case TIOCGSOFTCAR: return put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); case TIOCSSOFTCAR: error = get_user(arg, (unsigned int *) arg); if (error) return error; tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCMGET: return get_modem_info(info, (unsigned int *) arg); case TIOCMBIS: case TIOCMBIC: case TIOCMSET: return set_modem_info(info, cmd, (unsigned int *) arg);#ifdef maybe case TIOCSERGETLSR: /* Get line status register */ return get_lsr_info(info, (unsigned int *) arg);#endif /* * Wait for any of the 4 modem inputs (DCD,RI,DSR,CTS) to change * - mask passed in arg for lines of interest * (use |'ed TIOCM_RNG/DSR/CD/CTS for masking) * Caller should use TIOCGICOUNT to see which one it was */ case TIOCMIWAIT:#ifdef modem_control cli(); /* note the counters on entry */ cprev = info->state->icount; sti(); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; cli(); cnow = info->state->icount; /* atomic copy */ sti(); if (cnow.rng == cprev.rng && cnow.dsr == cprev.dsr && cnow.dcd == cprev.dcd && cnow.cts == cprev.cts) return -EIO; /* no change => error */ if ( ((arg & TIOCM_RNG) && (cnow.rng != cprev.rng)) || ((arg & TIOCM_DSR) && (cnow.dsr != cprev.dsr)) || ((arg & TIOCM_CD) && (cnow.dcd != cprev.dcd)) || ((arg & TIOCM_CTS) && (cnow.cts != cprev.cts)) ) { return 0; } cprev = cnow; } /* NOTREACHED */#else return 0;#endif /* * Get counter of input serial line interrupts (DCD,RI,DSR,CTS) * Return: write counters to the user passed counter struct * NB: both 1->0 and 0->1 transitions are counted except for * RI where only 0->1 is counted. */ case TIOCGICOUNT: cli(); cnow = info->state->icount;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -