Linux kernel 之 uart 驱动解析
- uart 是一种非常之常见的总线,比如DEBUG信息输出,小数据量数据传输,485,以及蓝牙的控制,GPS,很多都是通过uart 进行数据传输并进行控制。
* #### 在Linux kernel 内部,uart 通常是作为 一个 tty 设备对其进行控制,也是就是一个字符设备文件,可对其进行读写操作。
* #### kernel version 4.4.12
* #### 首先先看一下基本的 结构体 和 API 操作。
```c
    // include/linux/serial_core.h
// uart 驱动结构体
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; // tty 驱动结构体
};  
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);
    void            (*set_termios)(struct uart_port *,
                           struct ktermios *new,
                           struct ktermios *old);
    void            (*set_mctrl)(struct uart_port *, unsigned int);
    int         (*startup)(struct uart_port *port);
    void            (*shutdown)(struct uart_port *port);
    void            (*throttle)(struct uart_port *port);
    void            (*unthrottle)(struct uart_port *port);
    int         (*handle_irq)(struct uart_port *);
    void            (*pm)(struct uart_port *, unsigned int state,
                  unsigned int old);
    void            (*handle_break)(struct uart_port *);
    int         (*rs485_config)(struct uart_port *,
                    struct serial_rs485 *rs485);
    unsigned int        irq;            /* irq number */
    unsigned long       irqflags;       /* irq flags  */
    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       (SERIAL_IO_PORT)    /* 8b I/O port access */
#define UPIO_HUB6       (SERIAL_IO_HUB6)    /* Hub6 ISA card */
#define UPIO_MEM        (SERIAL_IO_MEM)     /* 8b MMIO access */
#define UPIO_MEM32      (SERIAL_IO_MEM32)   /* 32b little endian */
#define UPIO_AU         (SERIAL_IO_AU)      /* Au1x00 and RT288x type IO */
#define UPIO_TSI        (SERIAL_IO_TSI)     /* Tsi108/109 type IO */
#define UPIO_MEM32BE        (SERIAL_IO_MEM32BE) /* 32b big endian */            
    unsigned int        read_status_mask;   /* driver specific */
    unsigned int        ignore_status_mask; /* driver specific */
    struct uart_state   *state;         /* pointer to parent state */
    struct uart_icount  icount;         /* statistics */                        
    struct console      *cons;          /* struct console, if any */
#if defined(CONFIG_SERIAL_CORE_CONSOLE) || defined(SUPPORT_SYSRQ)
    unsigned long       sysrq;          /* sysrq timeout */
#endif                                                                          
    /* flags must be updated while holding port mutex */
    upf_t           flags;                                                      
    /*
     * These flags must be equivalent to the flags defined in
     * include/uapi/linux/tty_flags.h which are the userspace definitions
    * assigned from the serial_struct flags in uart_set_info()
     * [for bit definitions in the UPF_CHANGE_MASK]
     *
     * Bits [0..UPF_LAST_USER] are userspace defined/visible/changeable
     * except bit 15 (UPF_NO_TXEN_TEST) which is masked off.
     * The remaining bits are serial-core specific and not modifiable by
     * userspace.
     */
#define UPF_FOURPORT        ((__force upf_t) ASYNC_FOURPORT       /* 1  */ )
#define UPF_SAK         ((__force upf_t) ASYNC_SAK            /* 2  */ )
#define UPF_SPD_HI      ((__force upf_t) ASYNC_SPD_HI         /* 4  */ )
#define UPF_SPD_VHI     ((__force upf_t) ASYNC_SPD_VHI        /* 5  */ )
#define UPF_SPD_CUST        ((__force upf_t) ASYNC_SPD_CUST   /* 0x0030 */ )
#define UPF_SPD_WARP        ((__force upf_t) ASYNC_SPD_WARP   /* 0x1010 */ )
#define UPF_SPD_MASK        ((__force upf_t) ASYNC_SPD_MASK   /* 0x1030 */ )
#define UPF_SKIP_TEST       ((__force upf_t) ASYNC_SKIP_TEST      /* 6  */ )
#define UPF_AUTO_IRQ        ((__force upf_t) ASYNC_AUTO_IRQ       /* 7  */ )
#define UPF_HARDPPS_CD      ((__force upf_t) ASYNC_HARDPPS_CD     /* 11 */ )
#define UPF_SPD_SHI     ((__force upf_t) ASYNC_SPD_SHI        /* 12 */ )
#define UPF_LOW_LATENCY     ((__force upf_t) ASYNC_LOW_LATENCY    /* 13 */ )
#define UPF_BUGGY_UART      ((__force upf_t) ASYNC_BUGGY_UART     /* 14 */ )
#define UPF_NO_TXEN_TEST    ((__force upf_t) (1 << 15))
#define UPF_MAGIC_MULTIPLIER    ((__force upf_t) ASYNC_MAGIC_MULTIPLIER /* 16 */ )
/* Port has hardware-assisted h/w flow control */
#define UPF_AUTO_CTS        ((__force upf_t) (1 << 20))
#define UPF_AUTO_RTS        ((__force upf_t) (1 << 21))
#define UPF_HARD_FLOW       ((__force upf_t) (UPF_AUTO_CTS | UPF_AUTO_RTS))
/* Port has hardware-assisted s/w flow control */
#define UPF_SOFT_FLOW       ((__force upf_t) (1 << 22))
#define UPF_CONS_FLOW       ((__force upf_t) (1 << 23))
#define UPF_SHARE_IRQ       ((__force upf_t) (1 << 24))
#define UPF_EXAR_EFR        ((__force upf_t) (1 << 25))
#define UPF_BUG_THRE        ((__force upf_t) (1 << 26))
/* 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   0x17fff
#define UPF_CHANGE_MASK     ((__force upf_t) __UPF_CHANGE_MASK)
#define UPF_USR_MASK        ((__force upf_t) (UPF_SPD_MASK|UPF_LOW_LATENCY))    
#if __UPF_CHANGE_MASK > ASYNC_FLAGS
#error Change mask not equivalent to userspace-visible bit defines
#endif                                                                          
    /*
     * Must hold termios_rwsem, port mutex and port lock to change;
     * can hold any one lock to read.
     */
    upstat_t        status;                                                     
#define UPSTAT_CTS_ENABLE   ((__force upstat_t) (1 << 0))
#define UPSTAT_DCD_ENABLE   ((__force upstat_t) (1 << 1))
#define UPSTAT_AUTORTS      ((__force upstat_t) (1 << 2))
#define UPSTAT_AUTOCTS      ((__force upstat_t) (1 << 3))
#define UPSTAT_AUTOXOFF     ((__force upstat_t) (1 << 4))                       
    int         hw_stopped;     /* sw-assisted CTS flow state */
    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 */
    unsigned int        minor;
    resource_size_t     mapbase;        /* for ioremap */
    resource_size_t     mapsize;
    struct device       *dev;           /* parent device */
    unsigned char       hub6;           /* this should be in the 8250 driver */
    unsigned char       suspended;
    unsigned char       irq_wake;
    unsigned char       unused[2];
    struct attribute_group  *attr_group;        /* port specific attributes */
    const struct attribute_group **tty_groups;  /* all attributes (serial core use only) */
    struct serial_rs485     rs485;
    void            *private_data;      /* generic platform data pointer */
};                                                                              
// uart 驱动注册
int uart_register_driver(struct uart_driver *uart);
// uart 驱动注销
void uart_unregister_driver(struct uart_driver *uart); 
int uart_add_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_remove_one_port(struct uart_driver *reg, struct uart_port *port);
int uart_match_port(struct uart_port *port1, struct uart_port *port2);
* #### 通过实例看一下具体是怎么实现一个完整的 uart 驱动的
```c
    // drivers/tty/serial/omap-serial.c
    // 在文件最后,还是老套路
    module_init(serial_omap_init);
    module_exit(serial_omap_exit);                                                  
    MODULE_DESCRIPTION("OMAP High Speed UART driver");
    MODULE_LICENSE("GPL");
    MODULE_AUTHOR("Texas Instruments Inc");  
    // 让我们跟到 serial_omap_init
    static int __init serial_omap_init(void)
    {
        int ret;
        //  uart 驱动注册
        ret = uart_register_driver(&serial_omap_reg);
        if (ret != 0)
            return ret;
        // uart 平台驱动注册
        ret = platform_driver_register(&serial_omap_driver);
        if (ret != 0)
            uart_unregister_driver(&serial_omap_reg);
        return ret;
    } 
    // serial_omap_reg
    static struct uart_driver serial_omap_reg = {
        .owner      = THIS_MODULE,
        .driver_name    = "OMAP-SERIAL",
        .dev_name   = OMAP_SERIAL_NAME,
        .nr     = OMAP_MAX_HSUART_PORTS,
        .cons       = OMAP_CONSOLE,
    };   
    // serial_omap_driver
    static struct platform_driver serial_omap_driver = {
        .probe          = serial_omap_probe,   // probe 开始函数
        .remove         = serial_omap_remove,
        .driver     = {
            .name   = DRIVER_NAME,
            .pm = &serial_omap_dev_pm_ops,
            .of_match_table = of_match_ptr(omap_serial_of_match), // of_match_table 匹配函数
        },
    }; 
    // probe 函数
    static int serial_omap_probe(struct platform_device *pdev)
    {
        struct omap_uart_port_info *omap_up_info = dev_get_platdata(&pdev->dev);
        struct uart_omap_port *up;
        struct resource *mem;
        void __iomem *base;
        int uartirq = 0;
        int wakeirq = 0;
        int ret;                                                                    
        //  获取相关信息
        /* The optional wakeirq may be specified in the board dts file */
        if (pdev->dev.of_node) {
            uartirq = irq_of_parse_and_map(pdev->dev.of_node, 0);
            if (!uartirq)
                return -EPROBE_DEFER;
            wakeirq = irq_of_parse_and_map(pdev->dev.of_node, 1);
            omap_up_info = of_get_uart_port_info(&pdev->dev);
            pdev->dev.platform_data = omap_up_info;
        } else {
            uartirq = platform_get_irq(pdev, 0);
            if (uartirq < 0)
                return -EPROBE_DEFER;
        }                                                                           
        up = devm_kzalloc(&pdev->dev, sizeof(*up), GFP_KERNEL);
        if (!up)
            return -ENOMEM;                                                         
        // 内存,地址
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        base = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(base))
            return PTR_ERR(base);                                                   
        up->dev = &pdev->dev;
        up->port.dev = &pdev->dev;
        up->port.type = PORT_OMAP;
        up->port.iotype = UPIO_MEM;
        up->port.irq = uartirq;
        up->port.regshift = 2;
        up->port.fifosize = 64;
        up->port.ops = &serial_omap_pops;                                           
        if (pdev->dev.of_node)  // 获取id
            ret = of_alias_get_id(pdev->dev.of_node, "serial");
        else
            ret = pdev->id;                                                         
        if (ret < 0) {
            dev_err(&pdev->dev, "failed to get alias/pdev id, errno %d\n",
                ret);
            goto err_port_line;
        }
            up->port.line = ret;                                                        
        if (up->port.line >= OMAP_MAX_HSUART_PORTS) {
            dev_err(&pdev->dev, "uart ID %d >  MAX %d.\n", up->port.line,
                OMAP_MAX_HSUART_PORTS);
            ret = -ENXIO;
            goto err_port_line;
        }                                                                           
        up->wakeirq = wakeirq;
        if (!up->wakeirq)
            dev_info(up->port.dev, "no wakeirq for uart%d\n",
                 up->port.line);                                                    
        ret = serial_omap_probe_rs485(up, pdev->dev.of_node);
        if (ret < 0)
            goto err_rs485;
        // 设置相关信息
        sprintf(up->name, "OMAP UART%d", up->port.line);
        up->port.mapbase = mem->start;
        up->port.membase = base;
        up->port.flags = omap_up_info->flags;
        up->port.uartclk = omap_up_info->uartclk;
        up->port.rs485_config = serial_omap_config_rs485;
        if (!up->port.uartclk) {
            up->port.uartclk = DEFAULT_CLK_SPEED;
            dev_warn(&pdev->dev,
                 "No clock speed specified: using default: %d\n",
                 DEFAULT_CLK_SPEED);
        }                                                                           
        up->latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
        up->calc_latency = PM_QOS_CPU_DMA_LAT_DEFAULT_VALUE;
        pm_qos_add_request(&up->pm_qos_request,
            PM_QOS_CPU_DMA_LATENCY, up->latency);
        INIT_WORK(&up->qos_work, serial_omap_uart_qos_work);                        
        platform_set_drvdata(pdev, up);
        if (omap_up_info->autosuspend_timeout == 0)
            omap_up_info->autosuspend_timeout = -1;                                 
        device_init_wakeup(up->dev, true);
        pm_runtime_use_autosuspend(&pdev->dev);
        pm_runtime_set_autosuspend_delay(&pdev->dev,
            omap_up_info->autosuspend_timeout);                                 
        pm_runtime_irq_safe(&pdev->dev);
        pm_runtime_enable(&pdev->dev);                                              
        pm_runtime_get_sync(&pdev->dev);                                            
        omap_serial_fill_features_erratas(up);                                      
        ui[up->port.line] = up;
        serial_omap_add_console_port(up);                                           
        ret = uart_add_one_port(&serial_omap_reg, &up->port);
        if (ret != 0)
            goto err_add_port;                                                      
        pm_runtime_mark_last_busy(up->dev);
        pm_runtime_put_autosuspend(up->dev);
        // John add.    这个是另外的,不属于原生驱动
        #define GPIO_TO_PIN(bank, gpio)     (32 * (bank) + (gpio))
        devm_gpio_request(up->dev, GPIO_TO_PIN(0,22), "omap-serial");
        devm_gpio_request(up->dev, GPIO_TO_PIN(0,23), "omap-serial");
        devm_gpio_request(up->dev, GPIO_TO_PIN(0,19), "omap-serial");
        devm_gpio_request(up->dev, GPIO_TO_PIN(0,12), "omap-serial");
        gpio_direction_output(GPIO_TO_PIN(0,22),1); //COM0_MODE_0=1
        gpio_direction_output(GPIO_TO_PIN(0,23),0); //COM0_MODE_1=0
        gpio_direction_output(GPIO_TO_PIN(0,19),0); //COM0_TERM=0
        gpio_direction_output(GPIO_TO_PIN(0,12),1); //LVDS_BLKT_ON=1
        return 0;                                                                   
    err_add_port:
        pm_runtime_put(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
        pm_qos_remove_request(&up->pm_qos_request);
        device_init_wakeup(up->dev, false);
    err_rs485:
    err_port_line:
        return ret;
    }
- 看一下 uart_register_driver 内部是怎么实现的
    int uart_register_driver(struct uart_driver *drv)
    {
        struct tty_driver *normal;   // 主要是对这个 tty 驱动结构体进行了初始化。
        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);
        if (!drv->state)
            goto out;
        // 申请了一个  tty 驱动结构体
        normal = alloc_tty_driver(drv->nr);
        if (!normal)
            goto out_kfree;                                                         
        drv->tty_driver = normal;                                                   
        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;
            struct tty_port *port = &state->port;                                   
            tty_port_init(port);
            port->ops = &uart_port_ops;
        }
        // tty  驱动的注册
        retval = tty_register_driver(normal);
        if (retval >= 0)
            return retval;                                                          
        for (i = 0; i < drv->nr; i++)
            tty_port_destroy(&drv->state[i].port);
        put_tty_driver(normal);
    out_kfree:
        kfree(drv->state);
    out:
        return -ENOMEM;
    }
Linux kernel 之 uart 驱动解析的更多相关文章
- 【Linux开发】如何查看Linux kernel的内置模块驱动列表和进程ID
		[Linux开发]如何查看Linux kernel的内置模块驱动列表和进程ID 标签:[Linux开发] 命令: cat /lib/modules/$(uname -r)/modules.builti ... 
- linux kernel的cmdline参数解析原理分析【转】
		转自:https://blog.csdn.net/skyflying2012/article/details/41142801 版权声明:本文为博主kerneler辛苦原创,未经允许不得转载. htt ... 
- 【转】Linux Kernel __setup(str, fn)解析
		__setup这条宏在Linux Kernel中使用最多的地方就是定义处理Kernel的启动参数的函数及数据结构,宏定义如下: #define __setup(str, fn) \ __setup_p ... 
- linux之I2C裸机驱动解析(转)
		1 硬件特性 1.1 概述 I2C总线是由Philips公司开发的两线式串行总线,这两根线为时钟线(SCL)和双向数据线(SDA).由于I2C总线仅需要两根线,因此在电路板上占用的空间更少, ... 
- usb 转 uart cp210x 驱动解析
		USB 转 uart (cp210x.c) 驱动解析 * usb_serial_driver 结构体解析 include/linux/usb/serial.h /** 描述一个usb 串口设备驱动 * ... 
- 【嵌入式开发】 Linux Kernel 下载 配置 编译 安装 及 驱动简介
		作者 : 韩曙亮 转载请出名出处 : http://blog.csdn.net/shulianghan/article/details/38636827 一. Linux 内核简介 1. 内核功能简介 ... 
- Linux kernel中断子系统之(五):驱动申请中断API【转】
		转自:http://www.wowotech.net/linux_kenrel/request_threaded_irq.html 一.前言 本文主要的议题是作为一个普通的驱动工程师,在撰写自己负责的 ... 
- linux kernel的cmdline參数解析原理分析
		利用工作之便,今天研究了kernel下cmdline參数解析过程.记录在此.与大家共享.转载请注明出处.谢谢. Kernel 版本:3.4.55 Kernel启动时会解析cmdline,然后依据这些參 ... 
- Linux UART驱动分析
		1. 介绍 8250是IBM PC及兼容机使用的一种串口芯片; 16550是一种带先进先出(FIFO)功能的8250系列串口芯片; 16550A则是16550的升级版本, 修复了FIFO相关BUG, ... 
随机推荐
- [C#.NET] 使用 X509 數位電子簽章 加解密
			摘自: http://www.dotblogs.com.tw/yc421206/archive/2012/06/30/73150.aspx 上篇提到了使用X509的的憑證使用方式,請參考 [C#.NE ... 
- cocos2d-x游戏开发 跑酷(八) 对象管理 碰撞检測
			对象管理类的原理是这种: ObjectManager类是一个单例类,全局仅仅有一个对象实例存在.初始化的时候创建两个数组CCArray来保存金币和岩石.为什么要保存,由于在地图重载的时候.要销毁看不见 ... 
- -bash: ./xxx.sh: /bin/bash^M: bad interpreter: No such file or directory
			问题的原因是在windows下编辑然后上传到linux系统里执行的..sh文件的格式为dos格式.而linux只能执行格式为unix格式的脚本. 不要使用记事本编辑,使用代码编辑器可以正常执行 
- linux 的命令 -exec 的使用
			linux中的 exec命令,-exec 后面跟的是linux的 command 命令,exec命令以分号结束‘:’, 该分号前面要放 反斜杠转义 . find . -name jquery.js - ... 
- 免费的.net云平台appharbor.com
			支持Mysql,MSSQL2008R2免费的都是20MB 提供codeplex,GitHub,BitBucket部署到appharbor.com 也可以使用Git来部署应用,但不支持FTP 可以一键安 ... 
- jdk1.7下载路径
			http://www.oracle.com/technetwork/java/javase/downloads/jdk7-downloads-1880260.html 
- [Node.js] 4. Modules
			4.2 Missing Exports Notice the two different files: high_five.js on the left side andapp.js on the r ... 
- 如何判断linux用户是否为root用户
			看命令行提示符 ,如果是#,那就是root用户,如下图:[root@localhost /]# 如果是$,那就是普通用户,如下图:[test@localhost ~]$ 
- NET Framework安装失败的麻烦
			本人机子环境是安装了VS2012,即安装了 .NET Framework4.5,现在要安装AutoCAD2013,而安装CAD2013需要安装4.0的Framework,由于本机已有高版本的Frame ... 
- js 给json添加新的字段,或者添加一组数据,在JS数组指定位置删除、插入、替换元素
			JS定义了一个json数据var test={name:"name",age:"12"};需要给test再添加一个字段,需要什么办法,可以让test的值为{na ... 
