mcfserial.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,892 行 · 第 1/4 页
C
1,892 行
info->baud_base = new_serial.baud_base; info->flags = ((info->flags & ~ASYNC_FLAGS) | (new_serial.flags & ASYNC_FLAGS)); info->type = new_serial.type; info->close_delay = new_serial.close_delay; info->closing_wait = new_serial.closing_wait;check_and_exit: retval = startup(info); return retval;}/* * 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 mcf_serial * info, unsigned int *value){ volatile unsigned char *uartp; unsigned long flags; unsigned char status; local_irq_save(flags); uartp = info->addr; status = (uartp[MCFUART_USR] & MCFUART_USR_TXEMPTY) ? TIOCSER_TEMT : 0; local_irq_restore(flags); return put_user(status,value);}/* * This routine sends a break character out the serial port. */static void send_break( struct mcf_serial * info, int duration){ volatile unsigned char *uartp; unsigned long flags; if (!info->addr) return; current->state = TASK_INTERRUPTIBLE; uartp = info->addr; local_irq_save(flags); uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTART; schedule_timeout(jiffies + duration); uartp[MCFUART_UCR] = MCFUART_UCR_CMDBREAKSTOP; local_irq_restore(flags);}static int mcfrs_tiocmget(struct tty_struct *tty, struct file *file){ struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; return mcfrs_getsignals(info);}static int mcfrs_tiocmset(struct tty_struct *tty, struct file *file, unsigned int set, unsigned int clear){ struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; int rts = -1, dtr = -1; if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) return -ENODEV; if (tty->flags & (1 << TTY_IO_ERROR)) return -EIO; if (set & TIOCM_RTS) rts = 1; if (set & TIOCM_DTR) dtr = 1; if (clear & TIOCM_RTS) rts = 0; if (clear & TIOCM_DTR) dtr = 0; mcfrs_setsignals(info, dtr, rts); return 0;}static int mcfrs_ioctl(struct tty_struct *tty, struct file * file, unsigned int cmd, unsigned long arg){ struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; int retval, error; if (serial_paranoia_check(info, tty->name, "mcfrs_ioctl")) return -ENODEV; if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) && (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) && (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) { 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 (!arg) send_break(info, HZ/4); /* 1/4 second */ return 0; case TCSBRKP: /* support for POSIX tcsendbreak() */ retval = tty_check_change(tty); if (retval) return retval; tty_wait_until_sent(tty, 0); send_break(info, arg ? arg*(HZ/10) : HZ/4); return 0; case TIOCGSOFTCAR: error = put_user(C_CLOCAL(tty) ? 1 : 0, (unsigned long *) arg); if (error) return error; return 0; case TIOCSSOFTCAR: get_user(arg, (unsigned long *) arg); tty->termios->c_cflag = ((tty->termios->c_cflag & ~CLOCAL) | (arg ? CLOCAL : 0)); return 0; case TIOCGSERIAL: error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(struct serial_struct)); if (error) return error; return get_serial_info(info, (struct serial_struct *) arg); case TIOCSSERIAL: return set_serial_info(info, (struct serial_struct *) arg); case TIOCSERGETLSR: /* Get line status register */ error = verify_area(VERIFY_WRITE, (void *) arg, sizeof(unsigned int)); if (error) return error; else return get_lsr_info(info, (unsigned int *) arg); case TIOCSERGSTRUCT: error = copy_to_user((struct mcf_serial *) arg, info, sizeof(struct mcf_serial)); if (error) return -EFAULT; return 0; #ifdef TIOCSET422 case TIOCSET422: { unsigned int val; get_user(val, (unsigned int *) arg); mcf_setpa(MCFPP_PA11, (val ? 0 : MCFPP_PA11)); break; } case TIOCGET422: { unsigned int val; val = (mcf_getpa() & MCFPP_PA11) ? 0 : 1; put_user(val, (unsigned int *) arg); break; }#endif default: return -ENOIOCTLCMD; } return 0;}static void mcfrs_set_termios(struct tty_struct *tty, struct termios *old_termios){ struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; if (tty->termios->c_cflag == old_termios->c_cflag) return; mcfrs_change_speed(info); if ((old_termios->c_cflag & CRTSCTS) && !(tty->termios->c_cflag & CRTSCTS)) { tty->hw_stopped = 0; mcfrs_setsignals(info, -1, 1);#if 0 mcfrs_start(tty);#endif }}/* * ------------------------------------------------------------ * mcfrs_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 * S structure from the interrupt chain if necessary, and we free * that IRQ if nothing is left in the chain. * ------------------------------------------------------------ */static void mcfrs_close(struct tty_struct *tty, struct file * filp){ volatile unsigned char *uartp; struct mcf_serial *info = (struct mcf_serial *)tty->driver_data; unsigned long flags; if (!info || serial_paranoia_check(info, tty->name, "mcfrs_close")) return; local_irq_save(flags); if (tty_hung_up_p(filp)) { local_irq_restore(flags); return; } #ifdef SERIAL_DEBUG_OPEN printk("mcfrs_close ttyS%d, count = %d\n", info->line, info->count);#endif if ((tty->count == 1) && (info->count != 1)) { /* * Uh, oh. tty->count is 1, which means that the tty * structure will be freed. Info->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("MCFRS: bad serial port count; tty->count is 1, " "info->count is %d\n", info->count); info->count = 1; } if (--info->count < 0) { printk("MCFRS: bad serial port count for ttyS%d: %d\n", info->line, info->count); info->count = 0; } if (info->count) { local_irq_restore(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->imr &= ~MCFUART_UIR_RXREADY; uartp = info->addr; uartp[MCFUART_UIMR] = info->imr;#if 0 /* FIXME: do we need to keep this enabled for console?? */ if (mcfrs_console_inited && (mcfrs_console_port == info->line)) { /* Do not disable the UART */ ; } else#endif shutdown(info); if (tty->driver->flush_buffer) tty->driver->flush_buffer(tty); tty_ldisc_flush(tty); tty->closing = 0; info->event = 0; info->tty = 0;#if 0 if (tty->ldisc.num != ldiscs[N_TTY].num) { if (tty->ldisc.close) (tty->ldisc.close)(tty); tty->ldisc = ldiscs[N_TTY]; tty->termios->c_line = N_TTY; if (tty->ldisc.open) (tty->ldisc.open)(tty); }#endif 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); local_irq_restore(flags);}/* * mcfrs_wait_until_sent() --- wait until the transmitter is empty */static voidmcfrs_wait_until_sent(struct tty_struct *tty, int timeout){#ifdef CONFIG_M5272#define MCF5272_FIFO_SIZE 25 /* fifo size + shift reg */ struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; volatile unsigned char *uartp; unsigned long orig_jiffies, fifo_time, char_time, fifo_cnt; if (serial_paranoia_check(info, tty->name, "mcfrs_wait_until_sent")) return; orig_jiffies = jiffies; /* * Set the check interval to be 1/5 of the approximate time * to send the entire fifo, 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. */ fifo_time = (MCF5272_FIFO_SIZE * HZ * 10) / info->baud; char_time = fifo_time / 5; if (char_time == 0) char_time = 1; if (timeout && timeout < char_time) char_time = timeout; /* * Clamp the timeout period at 2 * the time to empty the * fifo. Just to be safe, set the minimum at .5 seconds. */ fifo_time *= 2; if (fifo_time < (HZ/2)) fifo_time = HZ/2; if (!timeout || timeout > fifo_time) timeout = fifo_time; /* * Account for the number of bytes in the UART * transmitter FIFO plus any byte being shifted out. */ uartp = (volatile unsigned char *) info->addr; for (;;) { fifo_cnt = (uartp[MCFUART_UTF] & MCFUART_UTF_TXB); if ((uartp[MCFUART_USR] & (MCFUART_USR_TXREADY| MCFUART_USR_TXEMPTY)) == MCFUART_USR_TXREADY) fifo_cnt++; if (fifo_cnt == 0) break; set_current_state(TASK_INTERRUPTIBLE); schedule_timeout(char_time); if (signal_pending(current)) break; if (timeout && time_after(jiffies, orig_jiffies + timeout)) break; }#else /* * For the other coldfire models, assume all data has been sent */#endif}/* * mcfrs_hangup() --- called by tty_hangup() when a hangup is signaled. */void mcfrs_hangup(struct tty_struct *tty){ struct mcf_serial * info = (struct mcf_serial *)tty->driver_data; if (serial_paranoia_check(info, tty->name, "mcfrs_hangup")) return; mcfrs_flush_buffer(tty); shutdown(info); info->event = 0; info->count = 0; info->flags &= ~ASYNC_NORMAL_ACTIVE; info->tty = 0; wake_up_interruptible(&info->open_wait);}/* * ------------------------------------------------------------ * mcfrs_open() and friends * ------------------------------------------------------------ */static int block_til_ready(struct tty_struct *tty, struct file * filp, struct mcf_serial *info){ DECLARE_WAITQUEUE(wait, current); 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 (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 ((filp->f_flags & O_NONBLOCK) || (tty->flags & (1 << TTY_IO_ERROR))) { 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, info->count is dropped by one, so that * mcfrs_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", info->line, info->count);#endif info->count--; info->blocked_open++; while (1) { local_irq_disable(); mcfrs_setsignals(info, 1, 1); local_irq_enable(); 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 || (mcfrs_getsignals(info) & TIOCM_CD))) break;
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?