/****************************************************************************************** * 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文件 里添加設備節點的權限.