您的位置:首页 > 运维架构 > Linux

I.MX6_Linux_UART_device&driver_hacking

2016-09-23 13:32 344 查看
/******************************************************************************************
*                        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文件
里添加设备节点的权限.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  IMX6Q linux linux kernel
相关文章推荐