⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 serial_sicc.c

📁 底层驱动开发
💻 C
📖 第 1 页 / 共 5 页
字号:
        result &= TIOCSER_TEMT;    return put_user(result, value);}static int get_modem_info(struct SICC_info *info, unsigned int *value){    unsigned int result = info->mctrl;    return put_user(result, value);}static int set_modem_info(struct SICC_info *info, unsigned int cmd,              unsigned int *value){    unsigned int arg, old;    unsigned long flags;    if (get_user(arg, value))        return -EFAULT;    old = info->mctrl;    switch (cmd) {    case TIOCMBIS:        info->mctrl |= arg;        break;    case TIOCMBIC:        info->mctrl &= ~arg;        break;    case TIOCMSET:        info->mctrl = arg;        break;    default:        return -EINVAL;    }    /* disable interrupts while setting modem control lines */    spin_lock_irqsave(&info->state->sicc_lock,flags);    if (old != info->mctrl)        info->port->set_mctrl(info->port, info->mctrl);    spin_unlock_irqrestore(&info->state->sicc_lock,flags);    return 0;}static void siccuart_break_ctl(struct tty_struct *tty, int break_state){    struct SICC_info *info = tty->driver_data;    unsigned long flags;    unsigned int lcr_h;    /* disable interrupts while setting break state */    spin_lock_irqsave(&info->state->sicc_lock,flags);    lcr_h = readb(info->port + BL_SICC_LSR);    if (break_state == -1)        lcr_h |=  _LSR_LB_MASK;    else        lcr_h &= ~_LSR_LB_MASK;    writeb(lcr_h, info->port + BL_SICC_LSRS);    spin_unlock_irqrestore(&info->state->sicc_lock,flags);}static int siccuart_ioctl(struct tty_struct *tty, struct file *file,               unsigned int cmd, unsigned long arg){    struct SICC_info *info = tty->driver_data;    struct SICC_icount cnow;    struct serial_icounter_struct icount;    unsigned long flags;    if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&        (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGSTRUCT) &&        (cmd != TIOCMIWAIT) && (cmd != TIOCGICOUNT)) {        if (tty->flags & (1 << TTY_IO_ERROR))            return -EIO;    }    switch (cmd) {        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);        case TIOCGSERIAL:            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 */            return get_lsr_info(info, (unsigned int *)arg);        /*         * 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:            return 0;        /*         * 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:            /* disable interrupts while getting interrupt count */            spin_lock_irqsave(&info->state->sicc_lock,flags);            cnow = info->state->icount;            spin_unlock_irqrestore(&info->state->sicc_lock,flags);            icount.cts = cnow.cts;            icount.dsr = cnow.dsr;            icount.rng = cnow.rng;            icount.dcd = cnow.dcd;            icount.rx  = cnow.rx;            icount.tx  = cnow.tx;            icount.frame = cnow.frame;            icount.overrun = cnow.overrun;            icount.parity = cnow.parity;            icount.brk = cnow.brk;            icount.buf_overrun = cnow.buf_overrun;            return copy_to_user((void *)arg, &icount, sizeof(icount))                    ? -EFAULT : 0;        default:            return -ENOIOCTLCMD;    }    return 0;}static void siccuart_set_termios(struct tty_struct *tty, struct termios *old_termios){    struct SICC_info *info = tty->driver_data;    unsigned long flags;    unsigned int cflag = tty->termios->c_cflag;    if ((cflag ^ old_termios->c_cflag) == 0 &&        RELEVENT_IFLAG(tty->termios->c_iflag ^ old_termios->c_iflag) == 0)        return;    siccuart_change_speed(info, old_termios);    /* Handle transition to B0 status */    if ((old_termios->c_cflag & CBAUD) &&        !(cflag & CBAUD)) {        /* disable interrupts while setting break state */        spin_lock_irqsave(&info->state->sicc_lock,flags);        info->mctrl &= ~(TIOCM_RTS | TIOCM_DTR);        info->port->set_mctrl(info->port, info->mctrl);        spin_unlock_irqrestore(&info->state->sicc_lock,flags);    }    /* Handle transition away from B0 status */    if (!(old_termios->c_cflag & CBAUD) &&        (cflag & CBAUD)) {        /* disable interrupts while setting break state */        spin_lock_irqsave(&info->state->sicc_lock,flags);        info->mctrl |= TIOCM_DTR;        if (!(cflag & CRTSCTS) ||            !test_bit(TTY_THROTTLED, &tty->flags))            info->mctrl |= TIOCM_RTS;        info->port->set_mctrl(info->port, info->mctrl);        spin_unlock_irqrestore(&info->state->sicc_lock,flags);    }    /* Handle turning off CRTSCTS */    if ((old_termios->c_cflag & CRTSCTS) &&        !(cflag & CRTSCTS)) {        tty->hw_stopped = 0;        siccuart_start(tty);    }#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}static void siccuart_close(struct tty_struct *tty, struct file *filp){    struct SICC_info *info = tty->driver_data;    struct SICC_state *state;    unsigned long flags;    if (!info)        return;    state = info->state;    //pr_debug("siccuart_close() called\n");    /* lock tty->driver_data while closing port */    spin_lock_irqsave(&info->state->sicc_lock,flags);    if (tty_hung_up_p(filp)) {        goto quick_close;    }    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("siccuart_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 %s: %d\n", tty->name, state->count);        state->count = 0;    }    if (state->count) {        goto quick_close;    }    info->flags |= ASYNC_CLOSING;    spin_unlock_irqrestore(&info->state->sicc_lock,flags);    /*     * 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->state->closing_wait != ASYNC_CLOSING_WAIT_NONE)        tty_wait_until_sent(tty, info->state->closing_wait);    /*     * At this point, we stop accepting input.  To do this, we     * disable the receive line status interrupts.     */    if (info->flags & ASYNC_INITIALIZED) {        siccuart_disable_rx_interrupt(info);        /*         * Before we drop DTR, make sure the UART transmitter         * has completely drained; this is especially         * important if there is a transmit FIFO!         */        siccuart_wait_until_sent(tty, info->timeout);    }    siccuart_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 = NULL;    if (info->blocked_open) {        if (info->state->close_delay) {            set_current_state(TASK_INTERRUPTIBLE);            schedule_timeout(info->state->close_delay);        }        wake_up_interruptible(&info->open_wait);    }    info->flags &= ~(ASYNC_NORMAL_ACTIVE|ASYNC_CLOSING);    wake_up_interruptible(&info->close_wait);    return;quick_close:    spin_unlock_irqrestore(&info->state->sicc_lock,flags);    return;}static void siccuart_wait_until_sent(struct tty_struct *tty, int timeout){    struct SICC_info *info = (struct SICC_info *) tty->driver_data;    unsigned long char_time, expire;    if (info->port->fifosize == 0)        return;    /*     * 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 = (info->timeout - HZ/50) / info->port->fifosize;    char_time = char_time / 5;    if (char_time == 0)        char_time = 1;    // Crazy!!   sometimes the input arg 'timeout' can be negtive numbers  :-(    if (timeout >= 0 && timeout < char_time)        char_time = timeout;    /*     * If the transmitter hasn't cleared in twice the approximate     * amount of time to send the entire FIFO, it probably won't     * ever clear.  This assumes the UART isn't doing flow     * control, which is currently the case.  Hence, if it ever     * takes longer than info->timeout, this is probably due to a     * UART bug of some kind.  So, we clamp the timeout parameter at     * 2*info->timeout.     */    if (!timeout || timeout > 2 * info->timeout)        timeout = 2 * info->timeout;    expire = jiffies + timeout;    pr_debug("siccuart_wait_until_sent(%d), jiff=%lu, expire=%lu  char_time=%lu...\n",           tty->index, jiffies,           expire, char_time);    while ((readb(info->port->uart_base + BL_SICC_LSR) & _LSR_TX_ALL) != _LSR_TX_ALL) {        set_current_state(TASK_INTERRUPTIBLE);        schedule_timeout(char_time);        if (signal_pending(current))            break;        if (timeout && time_after(jiffies, expire))            break;    }    set_current_state(TASK_RUNNING);}static void siccuart_hangup(struct tty_struct *tty){    struct SICC_info *info = tty->driver_data;    struct SICC_state *state = info->state;    siccuart_flush_buffer(tty);    if (info->flags & ASYNC_CLOSING)        return;    siccuart_shutdown(info);    info->event = 0;    state->count = 0;    info->flags &= ~ASYNC_NORMAL_ACTIVE;    info->tty = NULL;    wake_up_interruptible(&info->open_wait);}static int block_til_ready(struct tty_struct *tty, struct file *filp,               struct SICC_info *info){    DECLARE_WAITQUEUE(wait, current);    struct SICC_state *state = info->state;    unsigned long flags;    int do_clocal = 0, extra_count = 0, retval;    /*     * 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);        return (info->flags & ASYNC_HUP_NOTIFY) ?            -EAGAIN : -ERESTARTSYS;    }    /*     * 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, 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;    add_wait_queue(&info->open_wait, &wait);    /* lock while decrementing state->count */    spin_lock_irqsave(&info->state->sicc_lock,flags);    if (!tty_hung_up_p(filp)) {        extra_count = 1;        state->count--;    }    spin_unlock_irqrestore(&info->state->sicc_lock,flags);    info->blocked_open++;    while (1) {        /* disable interrupts while setting modem control lines */        spin_lock_irqsave(&info->state->sicc_lock,flags);        if (tty->termios->c_cflag & CBAUD) {            info->mctrl = TIOCM_DTR | TIOCM_RTS;            info->port->set_mctrl(info->port, info->mctrl);        }        spin_unlock_irqrestore(&info->state->sicc_lock,flags);        set_current_state(TASK_INTERRUPTIBLE);        if (tty_hung_up_p(filp) ||            !(info->flags & ASYNC_INITIALIZED)) {            if (info->flags & ASYNC_HUP_NOTIFY)                retval = -EAGAIN;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -