嵌入式板卡USART RTS引脚控制与lirc_serial内核模块移植问题
解决BayTrail平台上基于MMIO串口的红外发射内核模块移植问题
先给你梳理下核心问题的原因和可行的简便方案:
1. request_mem_region失败&ioremap数据异常的本质原因
你遇到的问题核心是资源冲突:dw-apb-uart驱动是内核内置模块,所以lsmod看不到,已经抢占了0x90a0c000这个MMIO区域。内核的资源管理机制会阻止重复申请同一硬件资源,所以request_mem_region必然失败;而直接跳过申请步骤做ioremap,会和正在运行的uart驱动产生寄存器读写冲突,导致数据输出混乱,这种操作非常危险,绝对不能继续。
2. ioremap_nocache与request_mem_region的正确调用顺序
正确的流程是:
- 先调用
request_mem_region申请目标内存区域,确认内核未将其分配给其他驱动; - 申请成功后,再调用
ioremap_nocache完成物理地址到虚拟地址的映射; - 使用完毕后,先执行
iounmap释放映射,再调用release_mem_region归还资源。
你之前的调用顺序完全颠倒了,再加上区域已被占用,第一步就失败了,后续操作自然无效。
3. 不卸载现有驱动,控制RTS引脚的简便方案
既然dw-apb-uart已经接管了串口,我们完全可以复用它的硬件控制接口,不需要重新操作寄存器。针对4.9内核,推荐两种实用方式:
方式一:通过tty设备接口直接控制RTS
内核提供了tty设备的标准化控制接口,可以直接设置RTS电平,步骤如下:
// 打开ttyS0设备 struct tty_struct *tty = tty_open_by_path("/dev/ttyS0", O_RDWR | O_NONBLOCK); if (IS_ERR(tty)) { // 处理错误逻辑 return PTR_ERR(tty); } tty_lock(tty); // 加锁避免并发冲突 // 获取对应的uart_port结构体(4.9内核中tty的driver_data指向uart_port) struct uart_port *port = tty->driver_data; // 设置RTS为高电平 uart_set_mctrl(port, TIOCM_RTS); udelay(500); // 红外脉冲延时,根据协议调整时长 // 设置RTS为低电平 uart_set_mctrl(port, 0); tty_unlock(tty); tty_kref_put(tty); // 关闭设备
方式二:直接操作8250寄存器(基于现有uart_port)
如果你需要更底层的控制,可以直接调用8250驱动的寄存器读写函数,同样基于已有的uart_port:
// 读取Modem控制寄存器(MCR) unsigned char mcr = port->serial_in(port, UART_MCR); // 置位RTS(MCR的第1位) mcr |= UART_MCR_RTS; port->serial_out(port, UART_MCR, mcr); udelay(500); // 清除RTS位 mcr &= ~UART_MCR_RTS; port->serial_out(port, UART_MCR, mcr);
这种方式和方式一本质相同,但更直接,前提是你对8250的寄存器定义有一定了解。
4. 红外发射时序实现注意事项
红外发射需要精准的电平脉冲时序,内核中可以用udelay(微秒级)或者hrtimer(更高精度)来实现:
- 避免在中断上下文中使用长延时,优先用
hrtimer调度时序; - 操作RTS的频率不要超过硬件支持范围,防止硬件异常。
总结
最简便的方案就是复用现有dw-apb-uart驱动的控制接口,不需要重新映射寄存器或者卸载驱动,既安全又节省开发时间。完全没必要移植serdev或者实现复杂的线路规程,直接通过tty设备接口控制RTS就能满足红外发射的需求。
内容的提问来源于stack exchange,提问作者Danny
相关产品推荐
相关产品推荐

