zoukankan      html  css  js  c++  java
  • ARM-Linux S5PV210 UART驱动(5)----串口的open操作(tty_open、uart_open)

    串口驱动初始化后,串口作为字符驱动也已经注册到系统了,/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
    ", 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

  • 相关阅读:
    P1828 [USACO3.2]香甜的黄油 Sweet Butter 题解
    P2058 海港 题解
    浅谈三分算法
    海伦公式的证明
    一年一回首
    再谈单调队列优化 & 背包九讲
    浅谈单调队列
    P1440 求m区间内的最小值 题解
    CF1374B Multiply by 2, divide by 6 题解
    组合数、杨辉三角与递推算法
  • 原文地址:https://www.cnblogs.com/hello2mhb/p/3359547.html
Copyright © 2011-2022 走看看