/****************************************************************************************** * 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\n"); /** * 接下来需要跟踪以下三个方向: * 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文件 里添加设备节点的权限.