zoukankan      html  css  js  c++  java
  • I.MX6_Linux_UART_device&driver_hacking

    /******************************************************************************************
     *                        I.MX6_Linux_UART_device&driver_hacking
     * 声明:
     *    1. 目录脚本生成: 
     *        grep -v '^s' I.MX6_UART_hacking.txt | grep '.'
     *    2. 该文章是在vim中编辑,请尽量是用vim来阅读,这样就不会出现缩进不对齐的问题;
     *    3. 本人阅读源码使用了ctags,强烈建议您使用类似高效的工具进行内核源代码阅读;
     *    3. 文章中的(device)表示的是串口设备侧的代码跟踪部分;
     *    4. 文章中的(driver)表示的是串口驱动侧的代码跟踪部分;
     *    5. 内核中的有些宏写成了函数的形式,从使用者的角度来说,个人感觉称作宏也行,
     *        叫函数也行,不过本人强烈希望你理解宏与函数的区别;   :)
     *    6. device跟踪架构如下:
     *      .( arch/arm/mach-mx6/board-mx6q_sabresd.c )
     *       -- mx6_sabresd_board_init()
     *          |
     *          | -- static iomux_v3_cfg_t mx6dl_sabresd_pads[] 
     *          |    -- #define MX6DL_PAD_CSI0_DAT10__UART1_TXD
     *          |        -- #define IOMUX_PAD(_pad_ctrl_ofs, _mux_ctrl_ofs,...)
     *          |            -- typedef u64 iomux_v3_cfg_t
     *          |
     *          | -- mxc_iomux_v3_setup_multiple_pads()
     *          |    -- int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
     *          |        -- static inline void __raw_writel(u32 b, volatile void __iomem *addr)
     *          |
     *           -- mx6q_sabresd_init_uart()
     *               -- #define imx6q_add_imx_uart(id, pdata)
     *                  | -- const struct imx_imx_uart_1irq_data imx6q_imx_uart_data[] __initconst
     *                  |    -- #define imx6q_imx_uart_data_entry(_id, _hwid)
     *                  |        -- #define imx_imx_uart_1irq_data_entry(soc, _id, _hwid, _size)
     *                  |           | -- #define SZ_4K      0x00001000
     *                  |           | -- #define MX6Q_UART3_BASE_ADDR  UART3_BASE_ADDR
     *                  |            -- #define MXC_INT_UART3_ANDED        60
     *                  |
     *                   -- struct platform_device *__init imx_add_imx_uart_1irq(...)
     *                       -- static inline struct platform_device *imx_add_platform_device(...)
     *                           -- struct platform_device *__init imx_add_platform_device_dmamask(...)
     *                   
     *    7. driver跟踪架构如下:
     *      .( drivers/tty/serial/imx.c )
     *       -- module_init(imx_serial_init);
     *           -- static int __init imx_serial_init(void)
     *              | -- int uart_register_driver(struct uart_driver *drv)
     *              | -- static struct uart_driver imx_reg
     *              |   | -- struct uart_driver
     *              |    -- #define DEV_NAME   "ttymxc"
     *              |
     *               -- static struct platform_driver serial_imx_driver
     *                   -- static int serial_imx_probe(struct platform_device *pdev)
     *                       -- int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
     *                           -- struct device *tty_register_device(...)
     *                               -- static void tty_line_name(...)
     *
     *
     *                                  2015-4-12 周日 晴 深圳 南山 西丽平山村 曾剑锋
     *****************************************************************************************/
    
               \\\\\\\\\\\\\--*目录*--/////////////////////////
               |  一. 需求说明:                                             |
               |  二. 参考文章:                                             |
               |  三. (device)从板级文件开始跟踪代码:                       |
               |  四. (device)跟踪mx6dl_sabresd_pads参数:                   |
               |  五. (device)跟踪mxc_iomux_v3_setup_multiple_pads()函数:   |
               |  六. (device)跟踪mx6q_sabresd_init_uart()函数:             |
               |  七. (device)跟踪imx6q_imx_uart_data参数:                  |
               |  八. (device)跟踪imx_add_imx_uart_1irq()函数:              |
               |  九. (driver)串口驱动跟踪:                                 |
               |  十. (driver)跟踪imx_reg参数:                              |
               |  十一. (driver)跟踪uart_register_driver()函数:             |
               |  十二. (driver)跟踪serial_imx_driver参数:                  |
               |  十三. Android下打开串口节点可能出现权限问题:              |
               \\\\\\\\\\\\\\\///////////////////////////////
    
    一. 需求说明:
        1. 了解I.MX6 Linux内核是如何在板级文件中注册UART设备(device);
        2. 了解I.MX6 Linux内核是如何加载UART驱动(driver);
        3. 了解I.MX6 Linux内核串口设备节点为什么有ttymxc这个前缀;
    
    二. 参考文章:
        1. MarS Board i.MX6 IOMUX解析:
            url: http://www.embest-tech.cn/community/thread-7822-1-1.html
        2. MarS Board-I.MX6 串口注册过程解析:
            url: http://www.embest-tech.cn/community/forum.php?mod=viewthread&tid=7825
        3. datasheet: IMX6DQRM_revC.pdf
        4. 内核源代码: myzr_android4_2_2_1_1_0.tar.bz2中的Linux内核
    
    三. (device)从板级文件开始跟踪代码:
        1. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            /*
             * initialize __mach_desc_MX6Q_SABRESD data structure.
             */
            MACHINE_START(MX6Q_SABRESD, "Freescale i.MX 6Quad/DualLite/Solo Sabre-SD Board")
                /* Maintainer: Freescale Semiconductor, Inc. */
                .boot_params = MX6_PHYS_OFFSET + 0x100,
                .fixup = fixup_mxc_board,
                .map_io = mx6_map_io,
                .init_irq = mx6_init_irq,
                .init_machine = mx6_sabresd_board_init,  //跟踪这个板级初始化函数
                .timer = &mx6_sabresd_timer,
                .reserve = mx6q_sabresd_reserve,
            MACHINE_END
        
        2. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            /**
             * Board specific initialization.
             */
            static void __init mx6_sabresd_board_init(void)
            {
                ......
                /**
                 * 1. 这部分有3个方向需要跟踪:
                 *     1. mx6dl_sabresd_pads : 我们需要知道它为什么存在
                 *     2. mxc_iomux_v3_setup_multiple_pads() : 同上
                 *     3. mx6q_sabresd_init_uart() : 同上       
                 * 2. 综上所述,我们下面分别对以上3种情况进行分析.
                 */
                if (cpu_is_mx6q())
                    mxc_iomux_v3_setup_multiple_pads(mx6q_sabresd_pads,
                        ARRAY_SIZE(mx6q_sabresd_pads));
                else if (cpu_is_mx6dl()) {
                    //跟踪mxc_iomux_v3_setup_multiple_pads函数及mx6dl_sabresd_pads参数
                    mxc_iomux_v3_setup_multiple_pads(mx6dl_sabresd_pads,
                        ARRAY_SIZE(mx6dl_sabresd_pads)); 
                }
                ......
                mx6q_sabresd_init_uart();  //跟踪uart初始化函数
                ......
            }
            ......
    
    四. (device)跟踪mx6dl_sabresd_pads参数:
        1. cat arch/arm/mach-mx6/board-mx6dl_sabresd.h
            ......
            static iomux_v3_cfg_t mx6dl_sabresd_pads[] = {
                ......
                /* UART1 for debug */
                MX6DL_PAD_CSI0_DAT10__UART1_TXD, //跟踪这个宏
                MX6DL_PAD_CSI0_DAT11__UART1_RXD,
    
                /* UART2*/
                MX6DL_PAD_EIM_D26__UART2_TXD,
                MX6DL_PAD_EIM_D27__UART2_RXD,
    
                /* UART3 for gps */
                MX6DL_PAD_EIM_D24__UART3_TXD,
                MX6DL_PAD_EIM_D25__UART3_RXD,
    
                /* UART4*/
                MX6DL_PAD_KEY_COL0__UART4_TXD,
                MX6DL_PAD_KEY_ROW0__UART4_RXD,
    
                /* UART5*/
                MX6DL_PAD_KEY_COL1__UART5_TXD,
                MX6DL_PAD_KEY_ROW1__UART5_RXD,
                ......
            }
            ......
    
        2. cat arch/arm/plat-mxc/include/mach/iomux-mx6dl.h
            ......
            #define MX6DL_PAD_CSI0_DAT10__UART1_TXD                             
                    IOMUX_PAD(0x0360, 0x004C, 3, 0x0000, 0, MX6DL_UART_PAD_CTRL) 跟踪这个函数,或者说宏
            ......
    
        3. cat arch/arm/plat-mxc/include/mach/iomux-v3.h
            ......
            /**
             * 跟踪iomux_v3_cfg_t数据结构.
             * 这里相当于数据合成存放在一个64位的数据中,包含了应该有的所有的数据
             */
            #define IOMUX_PAD(_pad_ctrl_ofs, _mux_ctrl_ofs, _mux_mode, _sel_input_ofs, 
                    _sel_input, _pad_ctrl)                    
                (((iomux_v3_cfg_t)(_mux_ctrl_ofs) << MUX_CTRL_OFS_SHIFT) |    
                    ((iomux_v3_cfg_t)(_mux_mode) << MUX_MODE_SHIFT) |    
                    ((iomux_v3_cfg_t)(_pad_ctrl_ofs) << MUX_PAD_CTRL_OFS_SHIFT) | 
                    ((iomux_v3_cfg_t)(_pad_ctrl) << MUX_PAD_CTRL_SHIFT) |    
                    ((iomux_v3_cfg_t)(_sel_input_ofs) << MUX_SEL_INPUT_OFS_SHIFT) | 
                    ((iomux_v3_cfg_t)(_sel_input) << MUX_SEL_INPUT_SHIFT))
            ......
    
        4. cat arch/arm/plat-mxc/include/mach/iomux-v3.h
            ......
            typedef u64 iomux_v3_cfg_t;
            ......
    
    五. (device)跟踪mxc_iomux_v3_setup_multiple_pads()函数:
        1. cat arch/arm/plat-mxc/iomux-v3.c
            ......
            int mxc_iomux_v3_setup_multiple_pads(iomux_v3_cfg_t *pad_list, unsigned count)
            {
                iomux_v3_cfg_t *p = pad_list;
                int i;
                int ret;
    
                for (i = 0; i < count; i++) {
                    ret = mxc_iomux_v3_setup_pad(*p); //跟踪该函数
                    if (ret)
                        return ret;
                    p++;
                }
                return 0;
            }
            ......
    
        2. cat arch/arm/plat-mxc/iomux-v3.c  
            ......
            /*
             * configures a single pad in the iomuxer
             */
            int mxc_iomux_v3_setup_pad(iomux_v3_cfg_t pad)
            {
                u32 mux_ctrl_ofs = (pad & MUX_CTRL_OFS_MASK) >> MUX_CTRL_OFS_SHIFT;
                u32 mux_mode = (pad & MUX_MODE_MASK) >> MUX_MODE_SHIFT;
                u32 sel_input_ofs = (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT;
                u32 sel_input = (pad & MUX_SEL_INPUT_MASK) >> MUX_SEL_INPUT_SHIFT;
                u32 pad_ctrl_ofs = (pad & MUX_PAD_CTRL_OFS_MASK) >> MUX_PAD_CTRL_OFS_SHIFT;
                u32 pad_ctrl = (pad & MUX_PAD_CTRL_MASK) >> MUX_PAD_CTRL_SHIFT;
    
                if (mux_ctrl_ofs)
                    __raw_writel(mux_mode, base + mux_ctrl_ofs);  //跟踪这个函数
    
                if (sel_input_ofs)
                    __raw_writel(sel_input, base + sel_input_ofs);
    
                if (!(pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs)
                    __raw_writel(pad_ctrl, base + pad_ctrl_ofs);
    
                return 0;
            }
            ......
    
        3. cat include/asm-generic/io.h  (找了个好理解的例子 :) )
            ......
            #ifndef __raw_writel
            static inline void __raw_writel(u32 b, volatile void __iomem *addr)
            {
                *(volatile u32 __force *) addr = b;
            }
            #endif
            ......
    
    六. (device)跟踪mx6q_sabresd_init_uart()函数:
        1. cat arch/arm/mach-mx6/board-mx6q_sabresd.c
            ......
            static inline void mx6q_sabresd_init_uart(void)
            {
                imx6q_add_imx_uart(0, NULL); //跟踪imx6q_add_imx_uart宏,你也可以说是函数
                imx6q_add_imx_uart(1, NULL);
                imx6q_add_imx_uart(2, NULL);
                imx6q_add_imx_uart(3, NULL);
                imx6q_add_imx_uart(4, NULL);
            }
            ......
        
        2. cat arch/arm/mach-mx6/devices-imx6q.h
            ......
            extern const struct imx_imx_uart_1irq_data imx6q_imx_uart_data[] __initconst;
            /**
             * 1. 这里需要分别跟踪两个方向:
             *     1. imx6q_imx_uart_data : 我们需要知道它为什么存在
             *     2. imx_add_imx_uart_1irq() : 同上
             * 2. 综上所述,下面分别对跟踪上面的两种情况.
             */
            #define imx6q_add_imx_uart(id, pdata)    
                imx_add_imx_uart_1irq(&imx6q_imx_uart_data[id], pdata)
            ......
    
    七. (device)跟踪imx6q_imx_uart_data参数:
        1. cat arch/arm/plat-mxc/devices/platform-imx-uart.c
            ......
            #ifdef CONFIG_SOC_IMX6Q
            const struct imx_imx_uart_1irq_data imx6q_imx_uart_data[] __initconst = {
            /** 
             * 1. 分别跟踪以下内容:
             *     1. imx_imx_uart_1irq_data_entry : 跟踪这个宏,也可以说是函数
             *     2. SZ_4K : 跟踪这个宏
             * 2. 综上所述,下面分别对跟踪上面的两种情况,但由于内容比较简单,
             *     所以就不单独分出一小节来跟踪代码.   :)
             */
            #define imx6q_imx_uart_data_entry(_id, _hwid)                
                imx_imx_uart_1irq_data_entry(MX6Q, _id, _hwid, SZ_4K) 
                imx6q_imx_uart_data_entry(0, 1),  //跟踪这个宏,也可以说是函数
                imx6q_imx_uart_data_entry(1, 2),
                imx6q_imx_uart_data_entry(2, 3),
                imx6q_imx_uart_data_entry(3, 4),
                imx6q_imx_uart_data_entry(4, 5),
            };
            #endif /* ifdef CONFIG_SOC_IMX6Q */
            ......
    
        2. cat arch/arm/plat-mxc/devices/platform-imx-uart.c
            ......
            /**
             * 1. 如果传入的参数如下:
             *      1. soc : MX6Q
             *      2. _id : 2
             *      3. _hwid : 3
             *      4. _size : SZ_4k
             * 2. 那么:
             *      1. .iobase = soc ## _UART ## _hwid ## _BASE_ADDR 合成结果:
             *          .iobase = MX6Q_UART3_BASE_ADDR = 0x21EC000
             *      2. .irq = soc ## _INT_UART ## _hwid 合成结果:
             *          .rq = MX6Q_INT_UART3
             *
             * 3. 如下是IMX6DQRM_revC.pdf第219页UART3的基地址:
             *   ------------------------------------------------------------
             *   | Start Address | End Address | Region | Allocation | Size |
             *   +---------------+-------------+--------+------------+------+
             *   |   021E_C000   |  021E_FFFF  | AIPS-2 |   UART3    | 16KB |
             *   ------------------------------------------------------------
             * 4. 如3表格中所示,SZ_4k和表中Size(16KB)不一致,目前不知道是为何.
             * 5. 有可能你不知道怎么去找到宏MX6Q_UART3_BASE_ADDR和MX6Q_INT_UART3,
             *      你可以在内核源码根目录下执行shell命令知道宏在哪个文件中:
             *      grep MX6Q_UART3_BASE_ADDR * -R
             */
            #define imx_imx_uart_1irq_data_entry(soc, _id, _hwid, _size) 
                [_id] = {                            
                    .id = _id,                        
                    .iobase = soc ## _UART ## _hwid ## _BASE_ADDR,   //跟踪合成的宏,可以得到基地址
                    .iosize = _size,                    
                    .irq = soc ## _INT_UART ## _hwid,        //跟踪合成的宏,可以得到irq
                }
            ......
        
        3. cat include/asm-generic/sizes.h
            ......
            #define SZ_4K      0x00001000
            ......
        
        4. cat arch/arm/plat-mxc/include/mach/mx6.h
            ......
            /**
             * 1. 如下主要是为了获取MX6Q芯片UART3的基地址制,推算过程如下:
             *     MX6Q_UART3_BASE_ADDR = UART3_BASE_ADDR
             *     MX6Q_UART3_BASE_ADDR = (AIPS2_OFF_BASE_ADDR + 0x6C000)
             *     MX6Q_UART3_BASE_ADDR = ((ATZ2_BASE_ADDR + 0x80000) + 0x6C000)
             *     MX6Q_UART3_BASE_ADDR = ((AIPS2_ARB_BASE_ADDR + 0x80000) + 0x6C000)
             *     MX6Q_UART3_BASE_ADDR = ((0x02100000 + 0x80000) + 0x6C000)
             *     MX6Q_UART3_BASE_ADDR = ((0x02100000 + 0x80000) + 0x6C000)
             *     MX6Q_UART3_BASE_ADDR = 0x21EC000
             * 2. 如1中演算所得,符合IMX6DQRM_revC.pdf第219页UART3的基地址:
             */
            #define MX6Q_UART3_BASE_ADDR        UART3_BASE_ADDR
            ......
            #define UART3_BASE_ADDR             (AIPS2_OFF_BASE_ADDR + 0x6C000)
            ......
            /* ATZ#2- Off Platform */
            #define AIPS2_OFF_BASE_ADDR         (ATZ2_BASE_ADDR + 0x80000)
            ......
            #define ATZ2_BASE_ADDR              AIPS2_ARB_BASE_ADDR
            ......
            #define AIPS2_ARB_BASE_ADDR         0x02100000
            ......
        
        5. cat arch/arm/plat-mxc/include/mach/mx6.h
            ......
            #define MX6Q_INT_UART3            MXC_INT_UART3_ANDED
            ......
            /*
             * 如下是IMX6DQRM_revC.pdf第226页UART3的中断号:
             *     -------------------------------------------------------------
             *     | RQ | Interrupt | Interrupt Description                    |
             *     |    |  Source   |                                          |
             *     +----+-----------+------------------------------------------+
             *     | 60 |   UART3   | Logical OR of UART3 interrupt requests.  |
             *     -------------------------------------------------------------
             */
            #define MXC_INT_UART3_ANDED        60
            ......
        
    八. (device)跟踪imx_add_imx_uart_1irq()函数:
        1. cat arch/arm/plat-mxc/devices/platform-imx-uart.c    
            ......
            struct platform_device *__init imx_add_imx_uart_1irq(
                    const struct imx_imx_uart_1irq_data *data,
                    const struct imxuart_platform_data *pdata)
            {
                /**
                 * 构造平台设备的基地址和中断资源数组
                 */
                struct resource res[] = {
                    {
                        .start = data->iobase,
                        .end = data->iobase + data->iosize - 1,
                        .flags = IORESOURCE_MEM,
                    }, {
                        .start = data->irq,
                        .end = data->irq,
                        .flags = IORESOURCE_IRQ,
                    },
                };
            
                return imx_add_platform_device("imx-uart", data->id, res, ARRAY_SIZE(res),
                        pdata, sizeof(*pdata));  //跟踪这个函数
            }
            ......
    
        2. cat arch/arm/plat-mxc/include/mach/devices-common.h
            ......
            static inline struct platform_device *imx_add_platform_device(
                    const char *name, int id,
                    const struct resource *res, unsigned int num_resources,
                    const void *data, size_t size_data)
            {
                return imx_add_platform_device_dmamask(
                        name, id, res, num_resources, data, size_data, 0); //跟踪这个函数
            }
            ......
    
        3. cat arch/arm/plat-mxc/devices.c
            ......
            struct platform_device *__init imx_add_platform_device_dmamask(
                    const char *name, int id,
                    const struct resource *res, unsigned int num_resources,
                    const void *data, size_t size_data, u64 dmamask)
            {
                int ret = -ENOMEM;
                struct platform_device *pdev;
            
                //分配一个平台设备,名字叫做"imx-uart"
                pdev = platform_device_alloc(name, id);
                if (!pdev)
                    goto err;
            
                //传入的参数是0,不用关心
                if (dmamask) {
                    /*
                     * This memory isn't freed when the device is put,
                     * I don't have a nice idea for that though.  Conceptually
                     * dma_mask in struct device should not be a pointer.
                     * See http://thread.gmane.org/gmane.linux.kernel.pci/9081
                     */
                    pdev->dev.dma_mask =
                        kmalloc(sizeof(*pdev->dev.dma_mask), GFP_KERNEL);
                    if (!pdev->dev.dma_mask)
                        /* ret is still -ENOMEM; */
                        goto err;
            
                    *pdev->dev.dma_mask = dmamask;
                    pdev->dev.coherent_dma_mask = dmamask;
                }
                //往平台设备中添加平台设备资源,有利于设备驱动获取平台设备数据
                if (res) {
                    ret = platform_device_add_resources(pdev, res, num_resources);
                    if (ret)
                        goto err;
                }
            
                //传入的参数是NULL,不用关心
                if (data) {
                    ret = platform_device_add_data(pdev, data, size_data);
                    if (ret)
                        goto err;
                }
            
                /** 
                 * 其实到这里可以结束了,后面是平台设备总线的实现方式,这里就不做跟踪了
                 * 有兴趣的,可以自己去跟踪  :)
                 */
                ret = platform_device_add(pdev);  
                if (ret) {
            err:
                    if (dmamask)
                        kfree(pdev->dev.dma_mask);
                    platform_device_put(pdev);
                    return ERR_PTR(ret);
                }
            
                return pdev;
            }
            ......
    
    九. (driver)串口驱动跟踪:
        1. cat drivers/tty/serial/imx.c
            ......
            module_init(imx_serial_init); //跟踪初始化函数
            module_exit(imx_serial_exit);
            
            MODULE_AUTHOR("Sascha Hauer");
            MODULE_DESCRIPTION("IMX generic serial port driver");
            MODULE_LICENSE("GPL");
            MODULE_ALIAS("platform:imx-uart");
            ......
    
        2. cat drivers/tty/serial/imx.c
            ......
            static int __init imx_serial_init(void)
            {
                int ret;
    
                printk(KERN_INFO "Serial: IMX driver
    ");
    
                /**
                 * 接下来需要跟踪以下三个方向:
                 *     1. imx_reg : 我们需要知道它为什么存在
                 *     2. serial_imx_driver: 理由同上
                 *     3. uart_register_driver(): 理由同上
                 */
                ret = uart_register_driver(&imx_reg);  //跟踪这个函数,参数,参数类型
                if (ret)
                    return ret;
    
                ret = platform_driver_register(&serial_imx_driver); //跟踪serial_imx_driver参数
                if (ret != 0)
                    uart_unregister_driver(&imx_reg);
    
                return 0;
            }
            ......
    
    十. (driver)跟踪imx_reg参数:
        1. cat drivers/tty/serial/imx.c
            ......
            static struct uart_driver imx_reg = {           //跟踪结构体
                    .owner          = THIS_MODULE,
                    .driver_name    = DRIVER_NAME,
                    /**
                     * 如果你想知道设备节点为什么有ttymxc前缀,那么你需要注意这个地方,
                     * 将来创建设备节点的时候,会用到这个名字来连接字符串,这个字符串
                     * 的值: ttymxc
                     */
                    .dev_name       = DEV_NAME,            //跟踪DEV_NAME
                    .major          = SERIAL_IMX_MAJOR,
                    .minor          = MINOR_START,
                    .nr             = ARRAY_SIZE(imx_ports),
                    .cons           = IMX_CONSOLE,
                };
            ......
    
        2. cat include/linux/serial_core.h
            ......
            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;
            };
            ......
    
        3. cat drivers/tty/serial/imx.c
            ......
            #define DEV_NAME        "ttymxc"
            ......
    
    十一. (driver)跟踪uart_register_driver()函数:
        cat drivers/tty/serial/imx.c
            /**
             * 串口驱动仅跟踪到这个函数,目前不做进行更进一步的跟踪
             */
            ......
            int uart_register_driver(struct uart_driver *drv)
            {
                ......
                /**
                 * 非常关键的一行代码,后面serial_imx_probe中要生成的设备
                 * 节点,需要用到normal->name,因为这里面报存了drv->dev_name,
                 * 也就是: ttymxc
                 */
                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;
    
                //初始波特率是9600,8,n,1
                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);
                ......
            }
    
    十二. (driver)跟踪serial_imx_driver参数:
        1. cat drivers/tty/serial/imx.c
            ......
            static struct platform_driver serial_imx_driver = {
                .probe        = serial_imx_probe,
                .remove        = serial_imx_remove,
                .suspend    = serial_imx_suspend,
                .resume        = serial_imx_resume,
                .driver        = {
                    .name    = "imx-uart", //和前面设备注册的名字一样
                    .owner    = THIS_MODULE,
                },
            };
            ......
    
        2. cat drivers/tty/serial/imx.c
            ......
            static int serial_imx_probe(struct platform_device *pdev)
            {
                ......
                //获取前面的设备(device)资源数据
                res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
                if (!res) {
                    ret = -ENODEV;
                    goto free;
                }
                ......
                sport->port.line = pdev->id;    //后面要用到这个数字,和串口前缀合成节点名字
                ......
                imx_ports[pdev->id] = sport;  
                ......
                /**
                 * 注意这里传入的参数imx_reg,里面有设备节点的字符串前缀
                 */
                ret = uart_add_one_port(&imx_reg, &sport->port); //跟踪这个函数
                ......
            }
            ......
    
        3. cat drivers/tty/serial/serial_core.c
            ......
            int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport)
            {
                ......
                /*
                 * 感觉这里很有用,以后可能会用到
                 * If this port is a console, then the spinlock is already
                 * initialised.
                 */
                if (!(uart_console(uport) && (uport->cons->flags & CON_ENABLED))) {
                    spin_lock_init(&uport->lock);
                    lockdep_set_class(&uport->lock, &port_lock_key);
                }
    
                uart_configure_port(drv, state, uport);
    
                /*
                 * Register the port whether it's detected or not.  This allows
                 * setserial to be used to alter this ports parameters.
                 */
                /**
                 * 1. 初始化函数imx_serial_init(void)已经通过uart_register_driver()初始化好了drv->tty_driver,
                 *     其中drv->tty_driver->name的值: ttymxc
                 * 2. uport->line = pdev->id, 用于和串口前缀合成串口节点名;
                 */
                tty_dev = tty_register_device(drv->tty_driver, uport->line, uport->dev); //跟踪这个函数
                ......
            }
            ......
    
        4. cat drivers/tty/tty_io.c
            ......
            struct device *tty_register_device(struct tty_driver *driver, unsigned index,
                               struct device *device)
            {
                ......
                /**
                 * 查看前面的int uart_register_driver(struct uart_driver *drv)函数中的设置可知:
                 * driver->type  = TTY_DRIVER_TYPE_SERIAL;
                 */
                if (driver->type == TTY_DRIVER_TYPE_PTY)
                    pty_line_name(driver, index, name);
                else
                    tty_line_name(driver, index, name); 跟踪这个函数
    
                return device_create(tty_class, device, dev, NULL, name);   //到头了
            }
            ......
    
        5. cat drivers/tty/tty_io.c
            ......
            static void tty_line_name(struct tty_driver *driver, int index, char *p)
            {
                /**
                 * 到了目的地了,也就是我想知道的设备节点的名字  :)
                 */
                sprintf(p, "%s%d", driver->name, index + driver->name_base);
            }
            ......
    
    十三. Android下打开串口节点可能出现权限问题:
        1. 可能会收到: you do not hava read/write permission to the serial port;
        2. 也可能会收到: 打开串口失败,没有串口读/写权限;
        3. 这里目前提供的解决方法是:
            1. 在串口shell中输入: chmod 777 <你的设备节点>
                例如: chmod 777 /dev/ttymxc2
            2. 上面只是针对本次有效,重启板子都不行,如果想要一直有效,就在init.rc文件
                里添加设备节点的权限.
  • 相关阅读:
    反射自动填充model
    source control tool
    项目管理案例分析
    IOC
    js framework
    WPF 难点内容
    WPF MVVM
    NewSQL
    软件部门员工考核
    JavaScript 中级
  • 原文地址:https://www.cnblogs.com/zengjfgit/p/4422893.html
Copyright © 2011-2022 走看看