📄 uart.c
字号:
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->device, "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->device, "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; ser_info_t *info = (ser_info_t *)tty->driver_data; if (serial_paranoia_check(info, tty->device, "rs_send_char")) return; bdp = info->tx_cur; while (bdp->cbd_sc & BD_SC_READY); *((char *)__va(bdp->cbd_bufaddr)) = 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->device, "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->device, "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 void begin_break(ser_info_t *info){ volatile cpm8260_t *cp; uint page, sblock; ushort num; cp = cpmp; if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { if (num == 0) { page = CPM_CR_SMC1_PAGE; sblock = CPM_CR_SMC1_SBLOCK; } else { page = CPM_CR_SMC2_PAGE; sblock = CPM_CR_SMC2_SBLOCK; } } else { num -= SCC_NUM_BASE; switch (num) { case 0: page = CPM_CR_SCC1_PAGE; sblock = CPM_CR_SCC1_SBLOCK; break; case 1: page = CPM_CR_SCC2_PAGE; sblock = CPM_CR_SCC2_SBLOCK; break; case 2: page = CPM_CR_SCC3_PAGE; sblock = CPM_CR_SCC3_SBLOCK; break; case 3: page = CPM_CR_SCC4_PAGE; sblock = CPM_CR_SCC4_SBLOCK; break; default: return; } } cp->cp_cpcr = mk_cr_cmd(page, sblock, 0, CPM_CR_STOP_TX) | CPM_CR_FLG; while (cp->cp_cpcr & CPM_CR_FLG);}static void end_break(ser_info_t *info){ volatile cpm8260_t *cp; uint page, sblock; ushort num; cp = cpmp; if ((num = info->state->smc_scc_num) < SCC_NUM_BASE) { if (num == 0) { page = CPM_CR_SMC1_PAGE; sblock = CPM_CR_SMC1_SBLOCK; } else { page = CPM_CR_SMC2_PAGE; sblock = CPM_CR_SMC2_SBLOCK; } } else { num -= SCC_NUM_BASE; switch (num) { case 0: page = CPM_CR_SCC1_PAGE; sblock = CPM_CR_SCC1_SBLOCK; break; case 1: page = CPM_CR_SCC2_PAGE; sblock = CPM_CR_SCC2_SBLOCK; break; case 2: page = CPM_CR_SCC3_PAGE; sblock = CPM_CR_SCC3_SBLOCK; break; case 3: page = CPM_CR_SCC4_PAGE; sblock = CPM_CR_SCC4_SBLOCK; break; default: return; } } cp->cp_cpcr = mk_cr_cmd(page, sblock, 0, 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->device, "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; 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;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -