zoukankan      html  css  js  c++  java
  • Linux串口驱动程序设计

    1. 在Linux系统中,终端是一类字符型设备,它包括多种类型,通常使用tty来简称各种类型的终端设备。

    (1)串口终端(/dev/ttyS*):串口终端是使用计算机串口连接的终端设备。Linux把每个串行端口都看作是一个字符设备。这些串行端口所对应的设备名称是/dev/ttySAC0;/dev/ttySAC1……

    (2)控制台终端(/dev/console):在Linux系统中,计算机的输出设备通常被称为控制台终端(Console),这里特指printk信息输出到的设备。/dev/console是一个虚拟的设备,它需要映射到真正的tty上,比如通过内核启动参数” console=ttySAC0”就把console映射到了串口0

    (3)虚拟终端(/dev/tty*):

    2. Linux tty子系统包含:tty核心,tty线路规程和tty驱动。

    (1)tty核心是对整个tty设备的抽象,对用户提供统一的接口。

    (2)tty线路规程是对传输数据的格式化。

    (3)tty驱动则是面向tty设备的硬件驱动。

    3. Linux中Uart框架:

      整个 uart 框架大概的样子如上图所示。简单来分的话可以说成两层,一层是下层我们的串口驱动层,它直接与硬件相接触,我们需要填充一个 struct uart_ops 的结构体,另一层是上层 tty 层,包括 tty 核心以及线路规程,它们各自都有一个 Ops 结构,用户空通过间是 tty 注册的字符设备节点来访问,这么说来如上图所示涉及到了4个 ops 结构了,层层跳转。

    4. 在 s3c2440平台,注册串口驱动的步骤是,分配一个struct uart_driver 简单填充,并调用uart_register_driver 注册到内核中去。uart_driver源码

    struct uart_driver {
        struct module        *owner;
        const char        *driver_name;
        const char        *dev_name;
        int             major;
        int             minor;
        int             nr;
        struct console        *cons;
    
        /*
         * these are private; the low level driver should not
         * touch these; they should be initialised to NULL
         */
        struct uart_state    *state;
        struct tty_driver    *tty_driver;
    };

    (1) 分配uart_driver结构并简单填充(state和tty_driver将会在uart_register_driver时候赋值)

    static struct uart_driver s3c24xx_uart_drv = {
        .owner        = THIS_MODULE,
        .dev_name    = "s3c2410_serial",
        .nr        = CONFIG_SERIAL_SAMSUNG_UARTS,
        .cons        = S3C24XX_SERIAL_CONSOLE,
        .driver_name    = S3C24XX_SERIAL_NAME,
        .major        = S3C24XX_SERIAL_MAJOR,
        .minor        = S3C24XX_SERIAL_MINOR,
    };

    (2) 调用uart_register_driver主要填充uart_driver的state和tty_deriver域

    int uart_register_driver(struct uart_driver *drv)
    {
        struct tty_driver *normal = NULL;
        int i, retval;
    
        BUG_ON(drv->state);
    
        /*
         * Maybe we should be using a slab cache for this, especially if
         * we have a large number of ports to handle.
         */
        drv->state = kzalloc(sizeof(struct uart_state) * drv->nr, GFP_KERNEL);
        retval = -ENOMEM;
        if (!drv->state)
            goto out;
    
        normal  = alloc_tty_driver(drv->nr);
        if (!normal)
            goto out;
    
        drv->tty_driver = normal;
    
        normal->owner        = drv->owner;
        normal->driver_name    = drv->driver_name;
        normal->name        = drv->dev_name;
        normal->major        = drv->major;
        normal->minor_start    = drv->minor;
        normal->type        = TTY_DRIVER_TYPE_SERIAL;
        normal->subtype        = SERIAL_TYPE_NORMAL;
        normal->init_termios    = tty_std_termios;
        normal->init_termios.c_cflag = B9600 | CS8 | CREAD | HUPCL | CLOCAL;
        normal->init_termios.c_ispeed = normal->init_termios.c_ospeed = 9600;
        normal->flags        = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
        normal->driver_state    = drv;
        tty_set_operations(normal, &uart_ops);
    
        /*
         * Initialise the UART state(s).
         */
        for (i = 0; i < drv->nr; i++) {
            struct uart_state *state = drv->state + i;
    
            state->close_delay     = 500;    /* .5 seconds */
            state->closing_wait    = 30000;    /* 30 seconds */
            mutex_init(&state->mutex);
    
            tty_port_init(&state->info.port);
            init_waitqueue_head(&state->info.delta_msr_wait);
            tasklet_init(&state->info.tlet, uart_tasklet_action,
                     (unsigned long)state);
        }
    
        retval = tty_register_driver(normal);
     out:
        if (retval < 0) {
            put_tty_driver(normal);
            kfree(drv->state);
        }
        return retval;
    }


    5. uart_driver的state域(下层,主要是对uart口的统一封装与描述)

    (1)state的类型为struct uart_state,定义如下

    struct uart_state {
        unsigned int        close_delay;        /* msec */
        unsigned int        closing_wait;        /* msec */
    
    #define USF_CLOSING_WAIT_INF    (0)
    #define USF_CLOSING_WAIT_NONE    (~0U)
    
        int            count;
        int            pm_state;
        struct uart_info    info;
        struct uart_port    *port;
    
        struct mutex        mutex;
    };

    (2)在注册driver时,即在uart_register_driver()函数内,会根据 uart_driver->nr 来申请 nr 个 uart_state 空间,用来存放驱动所支持的串口(端口)的物理信息。在Linux内核中,每一个串口的信息由struct uart_port结构来描述,

    struct uart_port {
        spinlock_t        lock;            /* port lock */
        unsigned long        iobase;            /* in/out[bwl] */
        unsigned char __iomem    *membase;        /* read/write[bwl] */
        unsigned int        (*serial_in)(struct uart_port *, int);
        void            (*serial_out)(struct uart_port *, int, int);
        unsigned int        irq;            /* irq number */
        unsigned int        uartclk;        /* base uart clock */
        unsigned int        fifosize;        /* tx fifo size */
        unsigned char        x_char;            /* xon/xoff char */
        unsigned char        regshift;        /* reg offset shift */
        unsigned char        iotype;            /* io access style */
        unsigned char        unused1;
    
    #define UPIO_PORT        (0)
    #define UPIO_HUB6        (1)
    #define UPIO_MEM        (2)
    #define UPIO_MEM32        (3)
    #define UPIO_AU            (4)            /* Au1x00 type IO */
    #define UPIO_TSI        (5)            /* Tsi108/109 type IO */
    #define UPIO_DWAPB        (6)            /* DesignWare APB UART */
    #define UPIO_RM9000        (7)            /* RM9000 type IO */
    
        unsigned int        read_status_mask;    /* driver specific */
        unsigned int        ignore_status_mask;    /* driver specific */
        struct uart_info    *info;            /* pointer to parent info */
        struct uart_icount    icount;            /* statistics */
    
        struct console        *cons;            /* struct console, if any */
    #ifdef CONFIG_SERIAL_CORE_CONSOLE
        unsigned long        sysrq;            /* sysrq timeout */
    #endif
    
        upf_t            flags;
    
    #define UPF_FOURPORT        ((__force upf_t) (1 << 1))
    #define UPF_SAK            ((__force upf_t) (1 << 2))
    #define UPF_SPD_MASK        ((__force upf_t) (0x1030))
    #define UPF_SPD_HI        ((__force upf_t) (0x0010))
    #define UPF_SPD_VHI        ((__force upf_t) (0x0020))
    #define UPF_SPD_CUST        ((__force upf_t) (0x0030))
    #define UPF_SPD_SHI        ((__force upf_t) (0x1000))
    #define UPF_SPD_WARP        ((__force upf_t) (0x1010))
    #define UPF_SKIP_TEST        ((__force upf_t) (1 << 6))
    #define UPF_AUTO_IRQ        ((__force upf_t) (1 << 7))
    #define UPF_HARDPPS_CD        ((__force upf_t) (1 << 11))
    #define UPF_LOW_LATENCY        ((__force upf_t) (1 << 13))
    #define UPF_BUGGY_UART        ((__force upf_t) (1 << 14))
    #define UPF_NO_TXEN_TEST    ((__force upf_t) (1 << 15))
    #define UPF_MAGIC_MULTIPLIER    ((__force upf_t) (1 << 16))
    #define UPF_CONS_FLOW        ((__force upf_t) (1 << 23))
    #define UPF_SHARE_IRQ        ((__force upf_t) (1 << 24))
    /* The exact UART type is known and should not be probed.  */
    #define UPF_FIXED_TYPE        ((__force upf_t) (1 << 27))
    #define UPF_BOOT_AUTOCONF    ((__force upf_t) (1 << 28))
    #define UPF_FIXED_PORT        ((__force upf_t) (1 << 29))
    #define UPF_DEAD        ((__force upf_t) (1 << 30))
    #define UPF_IOREMAP        ((__force upf_t) (1 << 31))
    
    #define UPF_CHANGE_MASK        ((__force upf_t) (0x17fff))
    #define UPF_USR_MASK        ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))
    
        unsigned int        mctrl;            /* current modem ctrl settings */
        unsigned int        timeout;        /* character-based timeout */
        unsigned int        type;            /* port type */
        const struct uart_ops    *ops;
        unsigned int        custom_divisor;
        unsigned int        line;            /* port index */
        resource_size_t        mapbase;        /* for ioremap */
        struct device        *dev;            /* parent device */
        unsigned char        hub6;            /* this should be in the 8250 driver */
        unsigned char        suspended;
        unsigned char        unused[2];
        void            *private_data;        /* generic platform data pointer */
    };

      注:这个结构体,是需要我们自己来填充的,比如我们 s3c2440 有3个串口,那么就需要填充3个 uart_port ,并且通过 uart_add_one_port 添加到 uart_driver->uart_state->uart_port 中去。当然 uart_driver 有多个 uart_state ,每个 uart_state 有一个 uart_port 

    (3)在 uart_port 里还有一个非常重要的成员 struct uart_ops *ops ,这个也是需要我们自己来实现的。该结构体描述了针对某一串口的具体操作方法

    struct uart_ops {
        unsigned int    (*tx_empty)(struct uart_port *);
        void        (*set_mctrl)(struct uart_port *, unsigned int mctrl);
        unsigned int    (*get_mctrl)(struct uart_port *);
        void        (*stop_tx)(struct uart_port *);
        void        (*start_tx)(struct uart_port *);
        void        (*send_xchar)(struct uart_port *, char ch);
        void        (*stop_rx)(struct uart_port *);
        void        (*enable_ms)(struct uart_port *);
        void        (*break_ctl)(struct uart_port *, int ctl);
        int        (*startup)(struct uart_port *);
        void        (*shutdown)(struct uart_port *);
        void        (*flush_buffer)(struct uart_port *);
        void        (*set_termios)(struct uart_port *, struct ktermios *new,
                           struct ktermios *old);
        void        (*set_ldisc)(struct uart_port *);
        void        (*pm)(struct uart_port *, unsigned int state,
                      unsigned int oldstate);
        int        (*set_wake)(struct uart_port *, unsigned int state);
    
        /*
         * Return a string describing the type of the port
         */
        const char *(*type)(struct uart_port *);
    
        /*
         * Release IO and memory resources used by the port.
         * This includes iounmap if necessary.
         */
        void        (*release_port)(struct uart_port *);
    
        /*
         * Request IO and memory resources used by the port.
         * This includes iomapping the port if necessary.
         */
        int        (*request_port)(struct uart_port *);
        void        (*config_port)(struct uart_port *, int);
        int        (*verify_port)(struct uart_port *, struct serial_struct *);
        int        (*ioctl)(struct uart_port *, unsigned int, unsigned long);
    #ifdef CONFIG_CONSOLE_POLL
        void    (*poll_put_char)(struct uart_port *, unsigned char);
        int        (*poll_get_char)(struct uart_port *);
    #endif
    };


    6.  uart_driver的tty_driver域(上层)

    (1)tty_driver 是在注册过程中构建的,即在uart_register_driver()函数中构建起来的。

    (2)注册过程即uart_register_driver()所做的工作

      ① 根据driver支持的最大设备数,申请n个 uart_state 空间,每一个 uart_state 都有一个 uart_port 。

      ② 分配一个 tty_driver 结构,并将drv->tty_driver 指向它

      ③ 对 tty_driver 进行设置,其中包括默认波特率、校验方式等

      ④ 初始化每一个 uart_state 的 tasklet

      ⑤ 注册tty_driver

       注: 注册 uart_driver 实际上是注册 tty_driver,因此与用户空间打交道的工作完全交给了 tty_driver ,而且这一部分都是内核实现好的,我们不需要修改,了解一下工作原理即可。

     (3)tty_dr的注册tty_register_driver()

    /*
     * Called by a tty driver to register itself.
     */
    int tty_register_driver(struct tty_driver *driver)
    {
        int error;
        int i;
        dev_t dev;
        void **p = NULL;
    
        if (!(driver->flags & TTY_DRIVER_DEVPTS_MEM) && driver->num) {
            p = kzalloc(driver->num * 2 * sizeof(void *), GFP_KERNEL);
            if (!p)
                return -ENOMEM;
        }
    
        if (!driver->major) {
            error = alloc_chrdev_region(&dev, driver->minor_start,
                            driver->num, driver->name);
            if (!error) {
                driver->major = MAJOR(dev);
                driver->minor_start = MINOR(dev);
            }
        } else {
            dev = MKDEV(driver->major, driver->minor_start);
            error = register_chrdev_region(dev, driver->num, driver->name);
        }
        if (error < 0) {
            kfree(p);
            return error;
        }
    
        if (p) {
            driver->ttys = (struct tty_struct **)p;
            driver->termios = (struct ktermios **)(p + driver->num);
        } else {
            driver->ttys = NULL;
            driver->termios = NULL;
        }
    
        cdev_init(&driver->cdev, &tty_fops);
        driver->cdev.owner = driver->owner;
        error = cdev_add(&driver->cdev, dev, driver->num);
        if (error) {
            unregister_chrdev_region(dev, driver->num);
            driver->ttys = NULL;
            driver->termios = NULL;
            kfree(p);
            return error;
        }
    
        mutex_lock(&tty_mutex);
        list_add(&driver->tty_drivers, &tty_drivers);
        mutex_unlock(&tty_mutex);
    
        if (!(driver->flags & TTY_DRIVER_DYNAMIC_DEV)) {
            for (i = 0; i < driver->num; i++)
                tty_register_device(driver, i, NULL);
        }
        proc_tty_register_driver(driver);
        driver->flags |= TTY_DRIVER_INSTALLED;
        return 0;
    }

      tty_driver注册过程

      ① 为线路规程和termios分配空间,并使 tty_driver 相应的成员指向它们。

      ② 注册字符设备,名字是 uart_driver->name 我们这里是“ttySAC”,文件操作函数集是 tty_fops。

      ③ 将该 uart_driver->tty_drivers 添加到全局链表 tty_drivers

      ④ 向 proc 文件系统添加 driver

    7. 调用过程分析:tty_driver 注册了一个字符设备,我们以它的 tty_fops 入手,以 open、read、write 为例,分析用户空间如何访问到最底层的硬件操作函数

    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,
    };

    (1)tty_open分析

    static int tty_open(struct inode *inode, struct file *filp)
    {
        int ret;
    
        lock_kernel();
        ret = __tty_open(inode, filp);
        unlock_kernel();
        return ret;
    }

      ① tty_open的核心代码是调用函数__tty_open(),__tty_open()的简略代码如下

    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;
    
        ...    
    
        //在全局tty_drivers链表中获取Core注册的tty_driver
    
        driver = get_tty_driver(device, &index);
    
        tty = tty_init_dev(driver, index, 0);    // tty->ops = driver->ops;
    
        filp->private_data = tty;
    
        if (tty->ops->open)
            /* 调用tty_driver->tty_foperation->open */
            retval = tty->ops->open(tty, filp);
    
        return 0;
    }

      从 tty_drivers 全局链表获取到前边我们注册进去的 tty_driver ,然后分配设置一个 struct tty_struct 的结构,最后调用 tty_struct->ops->open 函数,其实 tty_struct->ops == tty_driver->ops  。

      ② tty_init_dev源代码为

    struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
                                    int first_ok)
    {
        struct tty_struct *tty;
        int retval;
    
        /* Check if pty master is being opened multiple times */
        if (driver->subtype == PTY_TYPE_MASTER &&
            (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
            return ERR_PTR(-EIO);
    
        /*
         * First time open is complex, especially for PTY devices.
         * This code guarantees that either everything succeeds and the
         * TTY is ready for operation, or else the table slots are vacated
         * and the allocated memory released.  (Except that the termios
         * and locked termios may be retained.)
         */
    
        if (!try_module_get(driver->owner))
            return ERR_PTR(-ENODEV);
    
        tty = alloc_tty_struct();
        if (!tty)
            goto fail_no_mem;
        initialize_tty_struct(tty, driver, idx);
    
        retval = tty_driver_install_tty(driver, tty);
        if (retval < 0) {
            free_tty_struct(tty);
            module_put(driver->owner);
            return ERR_PTR(retval);
        }
    
        /*
         * Structures all installed ... call the ldisc open routines.
         * If we fail here just call release_tty to clean up.  No need
         * to decrement the use counts, as release_tty doesn't care.
         */
    
        retval = tty_ldisc_setup(tty, tty->link);
        if (retval)
            goto release_mem_out;
        return tty;
    
    fail_no_mem:
        module_put(driver->owner);
        return ERR_PTR(-ENOMEM);
    
        /* call the tty release_tty routine to clean out this slot */
    release_mem_out:
        if (printk_ratelimit())
            printk(KERN_INFO "tty_init_dev: ldisc open failed, "
                     "clearing slot %d
    ", idx);
        release_tty(tty, idx);
        return ERR_PTR(retval);
    }

      initialize_tty_struct():用tty_driver来初始化tty_struct结构

      tty_ldisc_setup():调用线路规程中的open函数

    (2)tty_open总结

      ① 获取tty_driver

      ② 根据tty_driver初始化一个tty_struct结构

        * 设置 tty_struct 的线路规程为 N_TTY (不同类型的线路规程有不同的 ops)

        * 初始化一个延时工作队列,唤醒时调用flush_to_ldisc ,读函数时我们需要分析它

        * 初始化 tty_struct 里的两个等待队列头

        * 设置 tty_struct->ops == tty_driver->ops

      ③ 在 tty_ldisc_setup 函数中调用到线路规程的open函数

      ④ 如果 tty_struct->ops 也就是 tty_driver->ops 定义了 open 函数则调用,显然是有的 uart_open

  • 相关阅读:
    用Java来获取访问者真实的IP地址
    springMVC 配置多个视图解析器
    java 内部类(inner class)详解
    安装mysql步骤
    Jquery 获取父页面下指定iframe里的指定元素
    Oracle 中文排序
    Oracle update时做表关联
    Jquery IE8兼容性
    JQuery 动态创建表单,并自动提交
    JQuery 使用.show()和.hide()做的可爱动画
  • 原文地址:https://www.cnblogs.com/wulei0630/p/9554719.html
Copyright © 2011-2022 走看看