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

来源:互联网 发布:劳拉克劳馥 知乎 编辑:程序博客网 时间:2024/06/05 06:21

串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/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);//通知内核设备不支持 llseekretry_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

0 0