📄 68360serial.c
字号:
if (!tty) return 0; bdp = info->tx_cur; while (1) { c = min(count, TX_BUF_SIZE); if (c <= 0) break; if (bdp->status & BD_SC_READY) { info->flags |= TX_WAKEUP; break; } /* memcpy(__va(bdp->buf), buf, c); */ memcpy((void *)bdp->buf, buf, c); bdp->length = c; bdp->status |= BD_SC_READY; buf += c; count -= c; ret += c; /* Get next BD. */ if (bdp->status & BD_SC_WRAP) bdp = info->tx_bd_base; else bdp++; info->tx_cur = (QUICC_BD *)bdp; } return ret;}static int rs_360_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->status & 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_360_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_360_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. */ tty_wakeup(tty); info->flags &= ~TX_WAKEUP;}/* * This function is used to send a high-priority XON/XOFF character to * the device */static void rs_360_send_xchar(struct tty_struct *tty, char ch){ volatile QUICC_BD *bdp; 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->status & BD_SC_READY); /* *((char *)__va(bdp->buf)) = ch; */ *((char *)bdp->buf) = ch; bdp->length = 1; bdp->status |= BD_SC_READY; /* Get next BD. */ if (bdp->status & BD_SC_WRAP) bdp = info->tx_bd_base; else bdp++; info->tx_cur = (QUICC_BD *)bdp;}/* * ------------------------------------------------------------ * rs_throttle() * * This routine is called by the upper-layer tty layer to signal that * incoming characters should be throttled. * ------------------------------------------------------------ */static void rs_360_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_360_send_xchar(tty, STOP_CHAR(tty));#ifdef modem_control if (tty->termios->c_cflag & CRTSCTS) info->MCR &= ~UART_MCR_RTS; local_irq_disable(); serial_out(info, UART_MCR, info->MCR); local_irq_enable();#endif}static void rs_360_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_360_send_xchar(tty, START_CHAR(tty)); }#ifdef modem_control if (tty->termios->c_cflag & CRTSCTS) info->MCR |= UART_MCR_RTS; local_irq_disable(); serial_out(info, UART_MCR, info->MCR); local_irq_enable();#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; local_irq_disable(); status = serial_in(info, UART_LSR); local_irq_enable(); result = ((status & UART_LSR_TEMT) ? TIOCSER_TEMT : 0); return put_user(result,value);}#endifstatic int rs_360_tiocmget(struct tty_struct *tty, struct file *file){ ser_info_t *info = (ser_info_t *)tty->driver_data; unsigned int result = 0;#ifdef modem_control unsigned char control, status; if (serial_paranoia_check(info, tty->name, __FUNCTION__)) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; control = info->MCR; local_irq_disable(); status = serial_in(info, UART_MSR); local_irq_enable(); 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 result;}static int rs_360_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){#ifdef modem_control ser_info_t *info = (ser_info_t *)tty->driver_data; unsigned int arg; if (serial_paranoia_check(info, tty->name, __FUNCTION__)) 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;#ifdef TIOCM_OUT1 if (set & TIOCM_OUT1) info->MCR |= UART_MCR_OUT1; if (set & TIOCM_OUT2) info->MCR |= UART_MCR_OUT2; if (clear & TIOCM_OUT1) info->MCR &= ~UART_MCR_OUT1; if (clear & TIOCM_OUT2) info->MCR &= ~UART_MCR_OUT2;#endif local_irq_disable(); serial_out(info, UART_MCR, info->MCR); local_irq_enable();#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 QUICC *cp; ushort chan; int idx; cp = pquicc; 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_cr = mk_cr_cmd(chan, CPM_CR_STOP_TX) | CPM_CR_FLG; while (cp->cp_cr & CPM_CR_FLG);}static void end_break(ser_info_t *info){ volatile QUICC *cp; ushort chan; int idx; cp = pquicc; 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_cr = mk_cr_cmd(chan, CPM_CR_RESTART_TX) | CPM_CR_FLG; while (cp->cp_cr & CPM_CR_FLG);}/* * This routine sends a break character out the serial port. */static void send_break(ser_info_t *info, unsigned int duration){#ifdef SERIAL_DEBUG_SEND_BREAK printk("rs_send_break(%d) jiff=%lu...", duration, jiffies);#endif begin_break(info); msleep_interruptible(duration); end_break(info);#ifdef SERIAL_DEBUG_SEND_BREAK printk("done jiffies=%lu\n", jiffies);#endif}static int rs_360_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; /* struct async_icount_24 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, 250); /* 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*100 : 250); 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); */ put_user(C_CLOCAL(tty) ? 1 : 0, (int *) arg); return 0; 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;#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 local_irq_disable(); /* note the counters on entry */ cprev = info->state->icount; local_irq_enable(); while (1) { interruptible_sleep_on(&info->delta_msr_wait); /* see if a signal did it */ if (signal_pending(current)) return -ERESTARTSYS; local_irq_disable(); cnow = info->state->icount; /* atomic copy */ local_irq_enable(); 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: local_irq_disable(); cnow = info->state->icount; local_irq_enable(); 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; */ put_user(cnow.cts, &p_cuser->cts); put_user(cnow.dsr, &p_cuser->dsr); put_user(cnow.rng, &p_cuser->rng); put_user(cnow.dcd, &p_cuser->dcd); return 0; default: return -ENOIOCTLCMD; } return 0;}/* FIX UP modem control here someday......*/static void rs_360_set_termios(struct tty_struct *tty, struct ktermios *old_termios){ ser_info_t *info = (ser_info_t *)tty->driver_data; 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); local_irq_disable(); serial_out(info, UART_MCR, info->MCR); local_irq_enable(); } /* 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; } local_irq_disable(); serial_out(info, UART_MCR, info->MCR); local_irq_enable(); } /* Handle turning off CRTSCTS */ if ((old_termios->c_cflag & CRTSCTS) &&
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -