/******************************************************************************************
* 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文件
里添加设备节点的权限.
I.MX6_Linux_UART_device&driver_hacking
最新推荐文章于 2023-11-06 11:35:31 发布