完善资料让更多小伙伴认识你,还能领取20积分哦, 立即完善>
Linux 下的UART 驱动框架53.1.1uart_driver 结构体在Linux中uart和I2C、SPI一样,提供了串口驱动框架,只需要按照提供的串口框架函数编译驱动即可。一般来说串口驱动都已经实现好了,我们需要做的就是在设备树文件中,添加相应的设备节点。当设备和驱动匹配成功后,串口就能够正常工作。在Linux中,用uart_driver结构体来描述串口,uart_driver定义在include/linux/serial_core.h文件中,内容如下:
295structuart_driver{ 296structmodule*owner;/*模块所属者*/ 297constchar*driver_name;/*驱动名字*/ 298constchar*dev_name;/*设备名字*/ 299intmajor;/*主设备号*/ 300intminor;/*次设备号*/ 301intnr;/*设备数*/ 302structconsole*cons;/*控制台*/ 303 304/* 305*theseareprivate;thelowleveldrivershouldnot 306*touchthese;theyshouldbeinitialisedtoNULL 307*/ 308structuart_state*state; 309structtty_driver*tty_driver; 310}; 一般在开发板上有几个串口,每个串口驱动都需要定义一个uart_driver结构体来表示。同其他设备一样,当uart_driver结构体创建好后,然后注册到内核中去。使用uart_register_driver函数来完成注册行为,函数原型如下:intuart_register_driver(structuart_driver*drv)参数drv就是创建好要注册的uart_driver结构体,返回0,表示成功,失败返回负值。既然有注册函数,同样的也有注销函数uart_unregister_driver,函数原型如下:voiduart_unregister_driver(structuart_driver*drv)参数drv是要注销的uart_driver结构体,没有返回值。 |
|
|
|
uart_port 结构体uart_port用于描述一个UART端口(直接对应于一个串口)的I/O端口或I/O内存地址、FIFO大小、端口类型等信息。uart_port定义在include/linux/serial_core.h文件,部分内容如下:
117structuart_port{ 118spinlock_tlock;/*portlock*/ 119unsignedlongiobase;/*in/out[bwl]*/ 120unsignedchar__iomem*membase;/*read/write[bwl]* 235conststructuart_ops*ops; 236unsignedintcustom_divisor; 237unsignedintline;/*portindex*/ 238unsignedintminor; 239resource_size_tmapbase;/*forioremap*/ 240resource_size_tmapsize; 241structdevice*dev;/*parentdevice*/ 250}; 在uart_port结构体中主要关注ops成员,ops成员包含了串口的具体驱动函数,后面具体了解。每个UART都有一个uart_port结构体,那么uart_port和uart_driver是如何结合起来的,要用到uart_add_one_port函数,函数原型如下:intuart_add_one_port(structuart_driver*drv,structuart_port*uport)drv:与uart_port对应的uart_driver结构体,uport:要添加到uart_driver结构体中的uart_port结构体。返回值:0,表示成功,负值,表示失败。卸载UART驱动时,也需要将uart_port从相应的uart_driver中移除,使用uart_remove_one_port函数来实现,函数原型如下:intuart_remove_one_port(structuart_driver*drv,structuart_port*uport)drv:要卸载的uart_port对应的uart_driver。uport:要卸载的uart_port。返回值:0,表示成功,负值,表示失败。 |
|
|
|
uart_ops 结构体uart_ops结构体中包含了UART框架中具体的驱动函数,Linux系统收发数据最终调用的都是ops中的函数。ops是uart_ops类型的结构体指针变量,uart_ops定义在include/linux/serial_core.h文件中,内容如下:
49structuart_ops{ 50unsignedint(*tx_empty)(structuart_port*); 51void(*set_mctrl)(structuart_port*,unsignedintmctrl); 52unsignedint(*get_mctrl)(structuart_port*); 53void(*stop_tx)(structuart_port*); 54void(*start_tx)(structuart_port*); 55void(*throttle)(structuart_port*); 56void(*unthrottle)(structuart_port*); 57void(*send_xchar)(structuart_port*,charch); 58void(*stop_rx)(structuart_port*); 59void(*enable_ms)(structuart_port*); 60void(*break_ctl)(structuart_port*,intctl); 61int(*startup)(structuart_port*); 62void(*shutdown)(structuart_port*); 63void(*flush_buffer)(structuart_port*); 64void(*set_termios)(structuart_port*,structktermios*new, 65structktermios*old); 66void(*set_ldisc)(structuart_port*,structktermios*); 67void(*pm)(structuart_port*,unsignedintstate, 68unsignedintoldstate); 69 70/* 71*Returnastringdescribingthetypeoftheport 72*/ 73constchar*(*type)(structuart_port*); 74 75/* 76*ReleaseIOandmemoryresourcesusedbytheport. 77*Thisincludesiounmapifnecessary. 78*/ 79void(*release_port)(structuart_port*); 80 81/* 82*RequestIOandmemoryresourcesusedbytheport. 83*Thisincludesiomappingtheportifnecessary. 84*/ 85int(*request_port)(structuart_port*); 86void(*config_port)(structuart_port*,int); 87int(*verify_port)(structuart_port*,structserial_struct*); 88int(*ioctl)(structuart_port*,unsignedint,unsignedlong); 89#ifdefCONFIG_CONSOLE_POLL 90int(*poll_init)(structuart_port*); 91void(*poll_put_char)(structuart_port*,unsignedchar); 92int(*poll_get_char)(structuart_port*); 93#endif 94}; UART驱动编写人员需要实现uart_ops,因为uart_ops是最底层的UART驱动接口,是实实在在的和UART寄存器打交道的。关于uart_ops结构体中的这些函数的具体含义请参考Documentation/serial/driver这个文档。 |
|
|
|
MX6ULUART 驱动分析53.2.1uart的的platform 驱动框架首先看一下在设备树文件imx6ull.dtsi中,串口UART3对应的设备节点,内容如下:
1uart3:serial@021ec000{ 2compatible=“fsl,imx6ul-uart”, 3“fsl,imx6q-uart”,“fsl,imx21-uart”; 4reg=《0x021ec0000x4000》; 5interrupts= ; 6clocks=《&clksIMX6UL_CLK_UART3_IPG》, 7《&clksIMX6UL_CLK_UART3_SERIAL》; 8clock-names=“ipg”,“per”; 9dmas=《&sdma2940》,《&sdma3040》; 10dma-names=“rx”,“tx”; 11status=“disabled”; 12}; 其中,根据compatible属性值:“fsl,imx6ul-uart”、“fsl,imx6q-uar”和“fsl,imx21-uart”。在内核源码中搜索这三个值即可找到对应的UART驱动文件,此文件为drivers/tty/serial/imx.c,在此文件中可以找到如下内容: 267staticstructplatform_device_idimx_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}; 281MODULE_DEVICE_TABLE(platform,imx_uart_devtype); 282 283staticconststructof_device_idimx_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}; 。..。.. 2071staticstructplatform_driverserial_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 2084staticint__initimx_serial_init(void) 2085{ 2086intret=uart_register_driver(&imx_reg); 2087 2088if(ret) 2089returnret; 2090 2091ret=platform_driver_register(&serial_imx_driver); 2092if(ret!=0) 2093uart_unregister_driver(&imx_reg); 2094 2095returnret; 2096} 2097 2098staticvoid__exitimx_serial_exit(void) 2099{ 2100platform_driver_unregister(&serial_imx_driver); 2101uart_unregister_driver(&imx_reg); 2102} 2103 2104module_init(imx_serial_init); 2105module_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。 |
|
|
|
uart_driver 初始化在imx_serial_init函数中向Linux内核注册了imx_reg,imx_reg就是uart_driver类型的结构体变量,imx_reg定义如下:
1836staticstructuart_driverimx_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}; 53.2.3uart_port 初始化和注册当UART设备和驱动匹配成功以后serial_imx_probe函数就会执行,此函数的重点工作就是初始化uart_port,然后将其添加到对应的uart_driver中。在看serial_imx_probe函数之前先来看一下imx_port结构体,imx_port是 NXP 为I.MX系列SOC定义的一个设备结构体,此结构体内部就包含了uart_port成员变量,imx_port结构体内容如下所示(有缩减): 216structimx_port{ 217structuart_portport; 218structtimer_listtimer; 219unsignedintold_status; 220unsignedinthave_rtscts:1; 221unsignedintdte_mode:1; 222unsignedintirda_inv_rx:1; 223unsignedintirda_inv_tx:1; 224unsignedshorttrcv_delay;/*transceiverdelay*/ 243unsignedlongflags; 245}; 第217行,uart_port成员变量port。接下来看一下serial_imx_probe函数,函数内容如下: 1969staticintserial_imx_probe(structplatform_device*pdev) 1970{ 1971structimx_port*sport; 1972void__iomem*base; 1973intret=0; 1974structresource*res; 1975inttxirq,rxirq,rtsirq; 1976 1977sport=devm_kzalloc(&pdev-》dev,sizeof(*sport),GFP_KERNEL); 1978if(!sport) 1979return-ENOMEM; 1980 1981ret=serial_imx_probe_dt(sport,pdev); 1982if(ret》0) 1983serial_imx_probe_pdata(sport,pdev); 1984elseif(ret《0) 1985returnret; 1986 1987res=platform_get_resource(pdev,IORESOURCE_MEM,0); 1988base=devm_ioremap_resource(&pdev-》dev,res); 1989if(IS_ERR(base)) 1990returnPTR_ERR(base); 1991 1992rxirq=platform_get_irq(pdev,0); 1993txirq=platform_get_irq(pdev,1); 1994rtsirq=platform_get_irq(pdev,2); 1995 1996sport-》port.dev=&pdev-》dev; 1997sport-》port.mapbase=res-》start; 1998sport-》port.membase=base; 1999sport-》port.type=PORT_IMX, 2000sport-》port.iotype=UPIO_MEM; 2001sport-》port.irq=rxirq; 2002sport-》port.fifosize=32; 2003sport-》port.ops=&imx_pops; 2004sport-》port.rs485_config=imx_rs485_config; 2005sport-》port.rs485.flags= 2006SER_RS485_RTS_ON_SEND|SER_RS485_RX_DURING_TX; 2007sport-》port.flags=UPF_BOOT_AUTOCONF; 2008init_timer(&sport-》timer); 2009sport-》timer.function=imx_timeout; 2010sport-》timer.data=(unsignedlong)sport; 2011 2012sport-》clk_ipg=devm_clk_get(&pdev-》dev,“ipg”); 2013if(IS_ERR(sport-》clk_ipg)){ 2014ret=PTR_ERR(sport-》clk_ipg); 2015dev_err(&pdev-》dev,“failedtogetipgclk:%dn”,ret); 2016returnret; 2017} 2018 2019sport-》clk_per=devm_clk_get(&pdev-》dev,“per”); 2020if(IS_ERR(sport-》clk_per)){ 2021ret=PTR_ERR(sport-》clk_per); 2022dev_err(&pdev-》dev,“failedtogetperclk:%dn”,ret); 2023returnret; 2024} 2025 2026sport-》port.uartclk=clk_get_rate(sport-》clk_per); 2027if(sport-》port.uartclk》IMX_MODULE_MAX_CLK_RATE){ 2028ret=clk_set_rate(sport-》clk_per,IMX_MODULE_MAX_CLK_RATE); 2029if(ret《0){ 2030dev_err(&pdev-》dev,“clk_set_rate()failedn”); 2031returnret; 2032} 2033} 2034sport-》port.uartclk=clk_get_rate(sport-》clk_per); 2035 2036/* 2037*AllocatetheIRQ(s)i.MX1hasthreeinterruptswhereaslater 2038*chipsonlyhaveoneinterrupt. 2039*/ 2040if(txirq》0){ 2041ret=devm_request_irq(&pdev-》dev,rxirq,imx_rxint,0, 2042dev_name(&pdev-》dev),sport); 2043if(ret) 2044returnret; 2045 2046ret=devm_request_irq(&pdev-》dev,txirq,imx_txint,0, 2047dev_name(&pdev-》dev),sport); 2048if(ret) 2049returnret; 2050}else{ 2051ret=devm_request_irq(&pdev-》dev,rxirq,imx_int,0, 2052dev_name(&pdev-》dev),sport); 2053if(ret) 2054returnret; 2055} 2056 2057imx_ports[sport-》port.line]=sport; 2058 2059platform_set_drvdata(pdev,sport); 2060 2061returnuart_add_one_port(&imx_reg,&sport-》port); 2062} 第1971行,定义一个imx_port类型的结构体指针变量sport。第1977行,为sport申请内存。第1987~1988行,从设备树中获取I.MX系列SOCUART外设寄存器首地址,对于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定义如下: 1611staticstructuart_opsimx_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#ifdefined(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};
|
|
|
|
只有小组成员才能发言,加入小组>>
778 浏览 0 评论
1149 浏览 1 评论
2527 浏览 5 评论
2859 浏览 9 评论
移植了freeRTOS到STMf103之后显示没有定义的原因?
2709 浏览 6 评论
keil5中manage run-time environment怎么是灰色,不可以操作吗?
1067浏览 3评论
190浏览 2评论
453浏览 2评论
368浏览 2评论
M0518 PWM的电压输出只有2V左右,没有3.3V是怎么回事?
453浏览 1评论
小黑屋| 手机版| Archiver| 电子发烧友 ( 湘ICP备2023018690号 )
GMT+8, 2024-12-20 17:14 , Processed in 1.237913 second(s), Total 89, Slave 71 queries .
Powered by 电子发烧友网
© 2015 bbs.elecfans.com
关注我们的微信
下载发烧友APP
电子发烧友观察
版权所有 © 湖南华秋数字科技有限公司
电子发烧友 (电路图) 湘公网安备 43011202000918 号 电信与信息服务业务经营许可证:合字B2-20210191 工商网监 湘ICP备2023018690号