ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)

时间:2021-11-07 09:21:20

 

串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/dev目录下也有设备文件节点了。

那接下来uart的操作是如何进行的呢?

操作硬件之前都是要先open设备,先来分析下这里的open函数具体做了那些工作。

s3c24xx_serial_modinit -->uart_register_driver -->tty_register_driver 中有如下语句:

    cdev_init(&driver->cdev, &tty_fops);

此处将 driver->cdev->ops=&tty_fops

而tty_fops如下: 

static const struct file_operations tty_fops = {
    .llseek        = no_llseek,
    .read        = tty_read,
    .write        = tty_write,
    .poll        = tty_poll,
    .unlocked_ioctl    = tty_ioctl,
    .compat_ioctl    = tty_compat_ioctl,
    .open        = tty_open,
    .release    = tty_release,
    .fasync        = tty_fasync,
};

所以应用层通过open系统调用open(“/dev/s3c2410_serial0”,)一层一层调用到会调用到tty_open。

/**
 *    tty_open        -    open a tty device
 *    @inode: inode of device file
 *    @filp: file pointer to tty
 *
 *    tty_open and tty_release keep up the tty count that contains the
 *    number of opens done on a tty. We cannot use the inode-count, as
 *    different inodes might point to the same tty.
 *
 *    Open-counting is needed for pty masters, as well as for keeping
 *    track of serial lines: DTR is dropped when the last close happens.
 *    (This is not done solely through tty->count, now.  - Ted 1/27/92)
 *
 *    The termios state of a pty is reset on first open so that
 *    settings don't persist across reuse.
 *
 *    Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
 *         tty->count should protect the rest.
 *         ->siglock protects ->signal/->sighand
 */

static int tty_open(struct inode *inode, struct file *filp)
{
    struct tty_struct *tty = NULL;
    int noctty, retval;
    struct tty_driver *driver;
    int index;
    dev_t device = inode->i_rdev;//获取主次设备号
    unsigned saved_flags = filp->f_flags;

    nonseekable_open(inode, filp);//通知内核设备不支持 llseek

retry_open:
    noctty = filp->f_flags & O_NOCTTY;
    index  = -1;
    retval = 0;

    mutex_lock(&tty_mutex);
    lock_kernel();

    if (device == MKDEV(TTYAUX_MAJOR, 0)) {//判断打开的设备是否是5 0(/dev/tty)
        tty = get_current_tty();
        if (!tty) {
            unlock_kernel();
            mutex_unlock(&tty_mutex);
            return -ENXIO;
        }
        driver = tty_driver_kref_get(tty->driver);
        index = tty->index;
        filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
        /* noctty = 1; */
        /* FIXME: Should we take a driver reference ? */
        tty_kref_put(tty);
        goto got_driver;
    }
#ifdef CONFIG_VT
    if (device == MKDEV(TTY_MAJOR, 0)) {// 4 0(/dev/tty0)
        extern struct tty_driver *console_driver;
        driver = tty_driver_kref_get(console_driver);
        index = fg_console;
        noctty = 1;
        goto got_driver;
    }
#endif
    if (device == MKDEV(TTYAUX_MAJOR, 1)) {//5 1(/dev/console)
        struct tty_driver *console_driver = console_device(&index);
        if (console_driver) {
            driver = tty_driver_kref_get(console_driver);
            if (driver) {
                /* Don't let /dev/console block */
                filp->f_flags |= O_NONBLOCK;
                noctty = 1;
                goto got_driver;
            }
        }
        unlock_kernel();
        mutex_unlock(&tty_mutex);
        return -ENODEV;
    }
/********************若都没有,则执行下面这句******************************/
/*
此函数的作用就是通过设备号来找到设备对应的tty_driver,并且将索引号码保存在index中
因为一个tty_driver对应的是所有此种类型的tty设备,比如所有的串口设备,所以需要通过这个索引号
index来判断打开的是具体哪个设备。并且每个具体的设备对应着一个用来描述自己的tty_struct。
而系统后面的操作全部和这个tty_struct相关。
*/
    driver = get_tty_driver(device, &index);

    if (!driver) {
        unlock_kernel();
        mutex_unlock(&tty_mutex);
        return -ENODEV;
    }
got_driver:
    if (!tty) {
        /* check whether we're reopening an existing tty */
        tty = tty_driver_lookup_tty(driver, inode, index);

        if (IS_ERR(tty)) {
            unlock_kernel();
            mutex_unlock(&tty_mutex);
            return PTR_ERR(tty);
        }
    }

    if (tty) {
        retval = tty_reopen(tty);//判断是否有tty_struct
        if (retval)
            tty = ERR_PTR(retval);
    } else
        tty = tty_init_dev(driver, index, 0);//不存在则创建并初始化一个tty_struct

    mutex_unlock(&tty_mutex);
    tty_driver_kref_put(driver);
    if (IS_ERR(tty)) {
        unlock_kernel();
        return PTR_ERR(tty);
    }

    filp->private_data = tty;
    file_move(filp, &tty->tty_files);
    check_tty_count(tty, "tty_open");
    if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
        tty->driver->subtype == PTY_TYPE_MASTER)
        noctty = 1;
#ifdef TTY_DEBUG_HANGUP
    printk(KERN_DEBUG "opening %s...", tty->name);
#endif
    if (!retval) {
        if (tty->ops->open)
/********************************************************/
             /*调用uart_open*/
            retval = tty->ops->open(tty, filp);        // ===============>>>>>>>>> /********************************************************/
        else
            retval = -ENODEV;
    }
    filp->f_flags = saved_flags;

    if (!retval && test_bit(TTY_EXCLUSIVE, &tty->flags) &&
                        !capable(CAP_SYS_ADMIN))
        retval = -EBUSY;

    if (retval) {
#ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "error %d in opening %s...", retval,
               tty->name);
#endif
        tty_release(inode, filp);
        if (retval != -ERESTARTSYS) {
            unlock_kernel();
            return retval;
        }
        if (signal_pending(current)) {
            unlock_kernel();
            return retval;
        }
        schedule();
        /*
         * Need to reset f_op in case a hangup happened.
         */
        if (filp->f_op == &hung_up_tty_fops)
            filp->f_op = &tty_fops;
        unlock_kernel();
        goto retry_open;
    }
    unlock_kernel();


    mutex_lock(&tty_mutex);
    lock_kernel();
    spin_lock_irq(&current->sighand->siglock);
    if (!noctty &&
        current->signal->leader &&
        !current->signal->tty &&
        tty->session == NULL)
        __proc_set_tty(current, tty);
    spin_unlock_irq(&current->sighand->siglock);
    unlock_kernel();
    mutex_unlock(&tty_mutex);
    return 0;
}

 

 

/*
 * calls to uart_open are serialised by the BKL in
 *   fs/char_dev.c:chrdev_open()
 * Note that if this fails, then uart_close() _will_ be called.
 *
 * In time, we want to scrap the "opening nonpresent ports"
 * behaviour and implement an alternative way for setserial
 * to set base addresses/ports/types.  This will allow us to
 * get rid of a certain amount of extra tests.
 */
static int uart_open(struct tty_struct *tty, struct file *filp)
{
    struct uart_driver *drv = (struct uart_driver *)tty->driver->driver_state;
    struct uart_state *state;
    struct tty_port *port;
    int retval, line = tty->index;

    BUG_ON(!kernel_locked());
    pr_debug("uart_open(%d) called\n", line);

    /*
     * tty->driver->num won't change, so we won't fail here with
     * tty->driver_data set to something non-NULL (and therefore
     * we won't get caught by uart_close()).
     */
    retval = -ENODEV;
    if (line >= tty->driver->num)
        goto fail;

    /*
     * We take the semaphore inside uart_get to guarantee that we won't
     * be re-entered while allocating the state structure, or while we
     * request any IRQs that the driver may need.  This also has the nice
     * side-effect that it delays the action of uart_hangup, so we can
     * guarantee that state->port.tty will always contain something
     * reasonable.
     */

    /*
    找到保存在tty_driver中的uart_state。
       最后将其值赋值给tty_struct。此处特别注意一下,这个uart_state会被放到tty_struct的driver_data中的!
       因为后面的write、read都是从driver_data中找到这个uart_state的!
    */
    state = uart_get(drv, line);
    if (IS_ERR(state)) {
        retval = PTR_ERR(state);
        goto fail;
    }
    port = &state->port;

    /*
     * Once we set tty->driver_data here, we are guaranteed that
     * uart_close() will decrement the driver module use count.
     * Any failures from here onwards should not touch the count.
     */
    tty->driver_data = state;
    state->uart_port->state = state;
    tty->low_latency = (state->uart_port->flags & UPF_LOW_LATENCY) ? 1 : 0;
    tty->alt_speed = 0;
    tty_port_tty_set(port, tty);

    /*
     * If the port is in the middle of closing, bail out now.
     */
    if (tty_hung_up_p(filp)) {
        retval = -EAGAIN;
        port->count--;
        mutex_unlock(&port->mutex);
        goto fail;
    }

    /*
     * Make sure the device is in D0 state.
     */
    if (port->count == 1)
        uart_change_pm(state, 0);

    /*
     * Start up the serial port.
     */
    retval = uart_startup(state, 0);//初始化串口硬件  ==========>>>>>>>>>>>>>>>

    /*
     * If we succeeded, wait until the port is ready.
     */
    if (retval == 0)
        retval = uart_block_til_ready(filp, state);
    mutex_unlock(&port->mutex);

    /*
     * If this is the first open to succeed, adjust things to suit.
     */
    if (retval == 0 && !(port->flags & ASYNC_NORMAL_ACTIVE)) {
        set_bit(ASYNCB_NORMAL_ACTIVE, &port->flags);

        uart_update_termios(state);
    }

fail:
    return retval;
}

 

 

 

/*
 * Startup the port.  This will be called once per open.  All calls
 * will be serialised by the per-port mutex.
 */
static int uart_startup(struct uart_state *state, int init_hw)
{
    struct uart_port *uport = state->uart_port;
    struct tty_port *port = &state->port;
    unsigned long page;
    int retval = 0;

    if (port->flags & ASYNC_INITIALIZED)
        return 0;

    /*
     * Set the TTY IO error marker - we will only clear this
     * once we have successfully opened the port.  Also set
     * up the tty->alt_speed kludge
     */
    set_bit(TTY_IO_ERROR, &port->tty->flags);

    if (uport->type == PORT_UNKNOWN)
        return 0;

    /*
     * Initialise and allocate the transmit and temporary
     * buffer.
     */
    if (!state->xmit.buf) {
        /* This is protected by the per port mutex */
        page = get_zeroed_page(GFP_KERNEL);
        if (!page)
            return -ENOMEM;

        state->xmit.buf = (unsigned char *) page;
        uart_circ_clear(&state->xmit);
    }

    retval = uport->ops->startup(uport);     ==========>>>>>>>>>调用s3c24xx_serial_startup()
    if (retval == 0) {
        if (init_hw) {
            /*
             * Initialise the hardware port settings.
             */
            uart_change_speed(state, NULL);

            /*
             * Setup the RTS and DTR signals once the
             * port is open and ready to respond.
             */
            if (port->tty->termios->c_cflag & CBAUD)
                uart_set_mctrl(uport, TIOCM_RTS | TIOCM_DTR);
        }

        if (port->flags & ASYNC_CTS_FLOW) {
            spin_lock_irq(&uport->lock);
            if (!(uport->ops->get_mctrl(uport) & TIOCM_CTS))
                port->tty->hw_stopped = 1;
            spin_unlock_irq(&uport->lock);
        }

        set_bit(ASYNCB_INITIALIZED, &port->flags);

        clear_bit(TTY_IO_ERROR, &port->tty->flags);
    }

    if (retval && capable(CAP_SYS_ADMIN))
        retval = 0;

    return retval;
}

 

 至此,通过open函数后s3c24xx的uart硬件部分也初始化好了,接着可以通过write、read函数收发数据了。

 

《《《《=============总结===============》》》》

open的主要作用是 在内核通过创建并初始化一个tty_struct来描述具体对应的一个硬件设备,比如这里就是用一个tty_struct来描述s3c24xx上的uart0的,然后找到uart_port

中ops的startup方法初始化uart的硬件。

具体的tty_struct初始化过程中最重要的几步如下

1.初始化tty-struct的ops,就是将tty_driver中的ops赋值给tty_struct

2.初始化tty线路规程操作集

3.初始化tty_struct中的uart_state,uart_state中包含uart_port信息,这一步通过步骤1中ops中的open方法来完成。

4.根据步骤3中找到的uart_state,找到里面的uart_port的ops中的startup方法来初始化uart硬件。

open的流程大致如下:

open

--tty_open

    |

    --get_tty_driver

    --tty_init_dev

    --tty->ops->open(uart_open)

        |

        --uart_startup

            |

            --uport->ops->startup(s3c24xx_serial_startup())

                |

                --request_irq(rx_irq)

                --request_irq(tx_irq)

 

 

 参考:http://blog.csdn.net/rockrockwu/article/details/7897283