完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
|
|
相关推荐
5个回答
|
|
Linux 下的 UART 驱动框架
53.1.1 uart_driver 结构体 在 Linux 中 uart 和 I2C、SPI 一样,提供了串口驱动框架,只需要按照提供的串口框架函数编译驱动即可。一般来说串口驱动都已经实现好了,我们需要做的就是在设备树文件中,添加相应的设备节点。当设备和驱动匹配成功后,串口就能够正常工作。 在 Linux 中,用 uart_driver 结构体来描述串口,uart_driver 定义在 include/linux/serial_core.h 文件中,内容如下: 295 struct uart_driver { 296 struct module *owner; /* 模块所属者 */ 297 const char *driver_name; /* 驱动名字 */ 298 const char *dev_name; /* 设备名字 */ 299 int major; /* 主设备号 */ 300 int minor; /* 次设备号 */ 301 int nr; /* 设备数 */ 302 struct console *cons; /* 控制台 */ 303 304 /* 305 * these are private; the low level driver should not 306 * touch these; they should be initialised to NULL 307 */ 308 struct uart_state *state; 309 struct tty_driver *tty_driver; 310 }; 一般在开发板上有几个串口,每个串口驱动都需要定义一个 uart_driver 结构体来表示。 同其他设备一样,当 uart_driver 结构体创建好后,然后注册到内核中去。使用 uart_register_driver 函数来完成注册行为,函数原型如下: int uart_register_driver(struct uart_driver *drv) 参数 drv 就是创建好要注册的 uart_driver 结构体,返回 0,表示成功,失败返回负值。 既然有注册函数,同样的也有注销函数 uart_unregister_driver,函数原型如下: void uart_unregister_driver(struct uart_driver *drv) 参数 drv 是要注销的 uart_driver 结构体,没有返回值。 53.1.2 uart_port 结构体 uart_port 用于描述一个 UART 端口(直接对应于一个串口)的 I/O 端口或 I/O 内存地址、FIFO 大小、端口类型等信息。 uart_port 定义在 include/linux/serial_core.h 文件,部分内容如下: 117 struct uart_port { 118 spinlock_t lock; /* port lock */ 119 unsigned long iobase; /* in/out[bwl] */ 120 unsigned char __iomem *membase; /* read/write[bwl] */ ...... 235 const struct uart_ops *ops; 236 unsigned int custom_divisor; 237 unsigned int line; /* port index */ 238 unsigned int minor; 239 resource_size_t mapbase; /* for ioremap */ 240 resource_size_t mapsize; 241 struct device *dev; /* parent device */ ...... 250 }; 在 uart_port 结构体中主要关注 ops 成员,ops 成员包含了串口的具体驱动函数,后面具体了解。 每个 UART 都有一个 uart_port 结构体,那么 uart_port 和 uart_driver 是如何结合起来的,要用到 uart_add_one_port 函数,函数原型如下: int uart_add_one_port(struct uart_driver *drv, struct uart_port *uport) drv:与 uart_port 对应的 uart_driver 结构体, uport:要添加到 uart_driver 结构体中的 uart_port 结构体。 返回值:0,表示成功,负值,表示失败。 卸载 UART 驱动时,也需要将 uart_port 从相应的 uart_driver 中移除,使用 uart_remove_one_port 函数来实现,函数原型如下: int uart_remove_one_port(struct uart_driver *drv, struct uart_port *uport) drv:要卸载的 uart_port 对应的 uart_driver。 uport:要卸载的 uart_port。 返回值:0,表示成功,负值,表示失败。 53.1.3 uart_ops 结构体 uart_ops 结构体中包含了 UART 框架中具体的驱动函数,Linux 系统收发数据最终调用的都是 ops 中的函数。ops 是 uart_ops 类型的结构体指针变量,uart_ops 定义在 include/linux/serial_core.h 文件中,内容如下: 49 struct uart_ops { 50 unsigned int (*tx_empty)(struct uart_port *); 51 void (*set_mctrl)(struct uart_port *, unsigned int mctrl); 52 unsigned int (*get_mctrl)(struct uart_port *); 53 void (*stop_tx)(struct uart_port *); 54 void (*start_tx)(struct uart_port *); 55 void (*throttle)(struct uart_port *); 56 void (*unthrottle)(struct uart_port *); 57 void (*send_xchar)(struct uart_port *, char ch); 58 void (*stop_rx)(struct uart_port *); 59 void (*enable_ms)(struct uart_port *); 60 void (*break_ctl)(struct uart_port *, int ctl); 61 int (*startup)(struct uart_port *); 62 void (*shutdown)(struct uart_port *); 63 void (*flush_buffer)(struct uart_port *); 64 void (*set_termios)(struct uart_port *, struct ktermios *new, 65 struct ktermios *old); 66 void (*set_ldisc)(struct uart_port *, struct ktermios *); 67 void (*pm)(struct uart_port *, unsigned int state, 68 unsigned int oldstate); 69 70 /* 71 * Return a string describing the type of the port 72 */ 73 const char *(*type)(struct uart_port *); 74 75 /* 76 * Release IO and memory resources used by the port. 77 * This includes iounmap if necessary. 78 */ 79 void (*release_port)(struct uart_port *); 80 81 /* 82 * Request IO and memory resources used by the port. 83 * This includes iomapping the port if necessary. 84 */ 85 int (*request_port)(struct uart_port *); 86 void (*config_port)(struct uart_port *, int); 87 int (*verify_port)(struct uart_port *, struct serial_struct *); 88 int (*ioctl)(struct uart_port *, unsigned int, unsigned long); 89 #ifdef CONFIG_CONSOLE_POLL 90 int (*poll_init)(struct uart_port *); 91 void (*poll_put_char)(struct uart_port *, unsigned char); 92 int (*poll_get_char)(struct uart_port *); 93 #endif 94 }; UART 驱动编写人员需要实现 uart_ops,因为 uart_ops 是最底层的 UART 驱动接口,是实实在在的和UART 寄存器打交道的。关于 uart_ops 结构体中的这些函数的具体含义请参考 Documentation/serial/driver这个文档。 |
|
|
|
2 i.MX6UL UART 驱动分析
53.2.1 uart 的 的 platform 驱动框架 首先看一下在设备树文件 imx6ull.dtsi 中,串口 UART3 对应的设备节点,内容如下: 1 uart3: serial@021ec000 { 2 compatible = "fsl,imx6ul-uart", 3 "fsl,imx6q-uart", "fsl,imx21-uart"; 4 reg = <0x021ec000 0x4000>; 5 interrupts = ; 6 clocks = , 7 ; 8 clock-names = "ipg", "per"; 9 dmas = , ; 10 dma-names = "rx", "tx"; 11 status = "disabled"; 12 }; 其中,根据 compatible 属性值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在内核源码中搜索这三个值即可找到对应的 UART 驱动文件,此文件为 drivers/tty/serial/imx.c,在此文件中可以找到如下内容: 267 static struct platform_device_id imx_uart_devtype[] = { 268 { 269 .name = "imx1-uart", 270 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX1_UART], 271 }, { 272 .name = "imx21-uart", 273 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX21_UART], 274 }, { 275 .name = "imx6q-uart", 276 .driver_data = (kernel_ulong_t) &imx_uart_devdata[IMX6Q_UART], 277 }, { 278 /* sentinel */ 279 } 280 }; 281 MODULE_DEVICE_TABLE(platform, imx_uart_devtype); 282 283 static const struct of_device_id imx_uart_dt_ids[] = { 284 { .compatible = "fsl,imx6q-uart", .data = &imx_uart_devdata[IMX6Q_UART], }, 285 { .compatible = "fsl,imx1-uart", .data = &imx_uart_devdata[IMX1_UART], }, 286 { .compatible = "fsl,imx21-uart", .data = &imx_uart_devdata[IMX21_UART], }, 287 { /* sentinel */ } 288 }; ...... 2071 static struct platform_driver serial_imx_driver = { 2072 .probe = serial_imx_probe, 2073 .remove = serial_imx_remove, 2074 2075 .suspend = serial_imx_suspend, 2076 .resume = serial_imx_resume, 2077 .id_table = imx_uart_devtype, 2078 .driver = { 2079 .name = "imx-uart", 2080 .of_match_table = imx_uart_dt_ids, 2081 }, 2082 }; 2083 2084 static int __init imx_serial_init(void) 2085 { 2086 int ret = uart_register_driver(&imx_reg); 2087 2088 if (ret) 2089 return ret; 2090 2091 ret = platform_driver_register(&serial_imx_driver); 2092 if (ret != 0) 2093 uart_unregister_driver(&imx_reg); 2094 2095 return ret; 2096 } 2097 2098 static void __exit imx_serial_exit(void) 2099 { 2100 platform_driver_unregister(&serial_imx_driver); 2101 uart_unregister_driver(&imx_reg); 2102 } 2103 2104 module_init(imx_serial_init); 2105 module_exit(imx_serial_exit); 从上述代码可以看出,uart 驱动文件使用了 platform_driver 结构体,本质上是一个 platform 驱动。 第 267~280 行,imx_uart_devtype 为传统匹配表。 第 283~288 行,设备树所使用的匹配表,第 284 行的 compatible 属性值为“fsl,imx6q-uart”。 第 2071~2082 行,platform 驱动框架结构体 serial_imx_driver。 第 2084~2096 行,驱动入口函数,第 2086 行调用 uart_register_driver 函数向 Linux 内核注册uart_driver,在这里就是 imx_reg。 第 2098~2102 行,驱动出口函数,第 2101 行调用 uart_unregister_driver 函数注销掉前面注册的uart_driver,也就是 imx_reg。 |
|
|
|
2.2 uart_driver 初始化
在 imx_serial_init 函数中向 Linux 内核注册了 imx_reg,imx_reg 就是 uart_driver 类型的结构体变量,imx_reg 定义如下: 1836 static struct uart_driver imx_reg = { 1837 .owner = THIS_MODULE, 1838 .driver_name = DRIVER_NAME, 1839 .dev_name = DEV_NAME, 1840 .major = SERIAL_IMX_MAJOR, 1841 .minor = MINOR_START, 1842 .nr = ARRAY_SIZE(imx_ports), 1843 .cons = IMX_CONSOLE, 1844 }; |
|
|
|
uart_port 初始化和注册
当 UART 设备和驱动匹配成功以后 serial_imx_probe 函数就会执行,此函数的重点工作就是初始化uart_port,然后将其添加到对应的 uart_driver 中。在看 serial_imx_probe 函数之前先来看一下 imx_port 结构体,imx_port 是 NXP 为 I.MX 系列 SOC 定义的一个设备结构体,此结构体内部就包含了 uart_port 成 员变量,imx_port 结构体内容如下所示(有缩减): 216 struct imx_port { 217 struct uart_port port; 218 struct timer_list timer; 219 unsigned int old_status; 220 unsigned int have_rtscts:1; 221 unsigned int dte_mode:1; 222 unsigned int irda_inv_rx:1; 223 unsigned int irda_inv_tx:1; 224 unsigned short trcv_delay; /* transceiver delay */ ...... 243 unsigned long flags; 245 }; 第 217 行,uart_port 成员变量 port。 接下来看一下 serial_imx_probe 函数,函数内容如下: 1969 static int serial_imx_probe(struct platform_device *pdev) 1970 { 1971 struct imx_port *sport; 1972 void __iomem *base; 1973 int ret = 0; 1974 struct resource *res; 1975 int txirq, rxirq, rtsirq; 1976 1977 sport = devm_kzalloc(&pdev->dev, sizeof(*sport), GFP_KERNEL); 1978 if (!sport) 1979 return -ENOMEM; 1980 1981 ret = serial_imx_probe_dt(sport, pdev); 1982 if (ret > 0) 1983 serial_imx_probe_pdata(sport, pdev); 1984 else if (ret < 0) 1985 return ret; 1986 1987 res = platform_get_resource(pdev, IORESOURCE_MEM, 0); 1988 base = devm_ioremap_resource(&pdev->dev, res); 1989 if (IS_ERR(base)) 1990 return PTR_ERR(base); 1991 1992 rxirq = platform_get_irq(pdev, 0); 1993 txirq = platform_get_irq(pdev, 1); 1994 rtsirq = platform_get_irq(pdev, 2); 1995 1996 sport->port.dev = &pdev->dev; 1997 sport->port.mapbase = res->start; 1998 sport->port.membase = base; 1999 sport->port.type = PORT_IMX, 2000 sport->port.iotype = UPIO_MEM; 2001 sport->port.irq = rxirq; 2002 sport->port.fifosize = 32; 2003 sport->port.ops = &imx_pops; 2004 sport->port.rs485_config = imx_rs485_config; 2005 sport->port.rs485.flags = 2006 SER_RS485_RTS_ON_SEND | SER_RS485_RX_DURING_TX; 2007 sport->port.flags = UPF_BOOT_AUTOCONF; 2008 init_timer(&sport->timer); 2009 sport->timer.function = imx_timeout; 2010 sport->timer.data = (unsigned long)sport; 2011 2012 sport->clk_ipg = devm_clk_get(&pdev->dev, "ipg"); 2013 if (IS_ERR(sport->clk_ipg)) { 2014 ret = PTR_ERR(sport->clk_ipg); 2015 dev_err(&pdev->dev, "failed to get ipg clk: %d/n", ret); 2016 return ret; 2017 } 2018 2019 sport->clk_per = devm_clk_get(&pdev->dev, "per"); 2020 if (IS_ERR(sport->clk_per)) { 2021 ret = PTR_ERR(sport->clk_per); 2022 dev_err(&pdev->dev, "failed to get per clk: %d/n", ret); 2023 return ret; 2024 } 2025 2026 sport->port.uartclk = clk_get_rate(sport->clk_per); 2027 if (sport->port.uartclk > IMX_MODULE_MAX_CLK_RATE) { 2028 ret = clk_set_rate(sport->clk_per, IMX_MODULE_MAX_CLK_RATE); 2029 if (ret < 0) { 2030 dev_err(&pdev->dev, "clk_set_rate() failed/n"); 2031 return ret; 2032 } 2033 } 2034 sport->port.uartclk = clk_get_rate(sport->clk_per); 2035 2036 /* 2037 * Allocate the IRQ(s) i.MX1 has three interrupts whereas later 2038 * chips only have one interrupt. 2039 */ 2040 if (txirq > 0) { 2041 ret = devm_request_irq(&pdev->dev, rxirq, imx_rxint, 0, 2042 dev_name(&pdev->dev), sport); 2043 if (ret) 2044 return ret; 2045 2046 ret = devm_request_irq(&pdev->dev, txirq, imx_txint, 0, 2047 dev_name(&pdev->dev), sport); 2048 if (ret) 2049 return ret; 2050 } else { 2051 ret = devm_request_irq(&pdev->dev, rxirq, imx_int, 0, 2052 dev_name(&pdev->dev), sport); 2053 if (ret) 2054 return ret; 2055 } 2056 2057 imx_ports[sport->port.line] = sport; 2058 2059 platform_set_drvdata(pdev, sport); 2060 2061 return uart_add_one_port(&imx_reg, &sport->port); 2062 } 第 1971 行,定义一个 imx_port 类型的结构体指针变量 sport。 第 1977 行,为 sport 申请内存。 第 1987~1988 行,从设备树中获取 I.MX 系列 SOC UART 外设寄存器首地址,对于I.MX6ULL 的 UART3 来说就是 0X021EC000。得到寄存器首地址以后对其进行内存映射,得到对应的虚拟地址。 第 1992~1994 行,获取中断信息。 第 1996~2034 行,初始化 sport,我们重点关注的就是第 2003 行初始化 sport 的 port 成员变量,也就是设置 uart_ops 为 imx_pops,imx_pops 就是 I.MX6ULL 最底层的驱动函数集合,稍后再来看。 第 2040~2055 行,申请中断。 第 2061 行,使用 uart_add_one_port 向 uart_driver 添加 uart_port,在这里就是向 imx_reg 添加sport->port。 |
|
|
|
imx_pops 结构体
imx_pops 就是 uart_ops 类型的结构体变量,保存了 I.MX6ULL 串口最底层的操作函数,imx_pops 定义如下: 1611 static struct uart_ops imx_pops = { 1612 .tx_empty = imx_tx_empty, 1613 .set_mctrl = imx_set_mctrl, 1614 .get_mctrl = imx_get_mctrl, 1615 .stop_tx = imx_stop_tx, 1616 .start_tx = imx_start_tx, 1617 .stop_rx = imx_stop_rx, 1618 .enable_ms = imx_enable_ms, 1619 .break_ctl = imx_break_ctl, 1620 .startup = imx_startup, 1621 .shutdown = imx_shutdown, 1622 .flush_buffer = imx_flush_buffer, 1623 .set_termios = imx_set_termios, 1624 .type = imx_type, 1625 .config_port = imx_config_port, 1626 .verify_port = imx_verify_port, 1627 #if defined(CONFIG_CONSOLE_POLL) 1628 .poll_init = imx_poll_init, 1629 .poll_get_char = imx_poll_get_char, 1630 .poll_put_char = imx_poll_put_char, 1631 #endif 1632 }; imx_pops 中的函数基本都是和 I.MX6ULL 的 UART 寄存器打交道的,这里就不去详细的分析了。 |
|
|
|
只有小组成员才能发言,加入小组>>
798 浏览 0 评论
1155 浏览 1 评论
2531 浏览 5 评论
2863 浏览 9 评论
移植了freeRTOS到STMf103之后显示没有定义的原因?
2713 浏览 6 评论
keil5中manage run-time environment怎么是灰色,不可以操作吗?
1086浏览 3评论
195浏览 2评论
461浏览 2评论
374浏览 2评论
M0518 PWM的电压输出只有2V左右,没有3.3V是怎么回事?
455浏览 1评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-24 07:43 , Processed in 1.255868 second(s), Total 86, Slave 67 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号