Linux驱动开发笔记(十一)tty子系统及其驱动

文章目录

  • 前言
  • 一、串口驱动框架
    • 1.1 核心数据结构
    • 1.2 数据处理流程
  • 二、驱动编写
    • 1. 设备树的修改
    • 2. 相关API函数
    • 3. 驱动框架
    • 4. 具体功能的实现
      • 4.1 出入口函数的编写
      • 4.2 读写函数


前言

  之前已经讲过应用层的应用,接下来我们继续进行驱动的学习。其实实际上我们很少主动进行串口的驱动编写,通常情况下只需要进行应用层的应用就可以了,网络上相关的驱动内容介绍也较少,这里仅作了解并简单了解一下架构即可。


一、串口驱动框架

  串口驱动没有什么主机端和设备端之分,就只有一个串口驱动,而且这个驱动也已经由厂商已经编写好了,我们真正要做的就是在设备树中添加所要使用的串口节点信息。当系统启动以后串口驱动和设备匹配成功,相应的串口就会被驱动起来,生成/dev/ttymxcX(X=0….n)文件。
  这部分内容还是比较多的,笔者发现这个文章讲述的还是很详细的,感兴趣可以自行查阅,这里不再赘述:框架详细介绍

1.1 核心数据结构

  对于串口驱动来说,实际上重要的结构体只有uart_drive和uart_port,这里可能根据不同的厂商所采用的名称不大一样,但内容差不多,比如在rk3568中,官方提供的uart驱动程序位8250通用串口程序,其为uart_port结构体提供了扩展后的uart_8250_port,包含8250 UART特有的属性和操作。
  这部分内容保存于内核中的下列文件中,感兴趣可以自行查阅:

  •  serial_core.c:包含UART核心层的实现。
  •  8250.c:包含8250 UART特定操作的实现。
  •  8250_port.c:包含8250 UART端口的具体实现。

  uart_driver结构体用于描述一个UART驱动程序,它通常包含一些基础的信息和操作函数。

struct uart_driver {struct module *owner;               // 设备驱动模块的所有者const char *driver_name;            // 驱动程序的名称const char *dev_name;               // 设备名称int major;                          // 设备主设备号int minor;                          // 设备次设备号int nr;                             // 该驱动程序支持的设备数量struct console *cons;               // 控制台相关信息struct uart_ops *ops;               // UART操作函数集合struct uart_state *state;struct tty_driver *tty_driver;
};

  uart_8250_port结构体用于描述8250 UART端口的具体信息和操作方法。

struct uart_8250_port {struct uart_port port;              // 通用UART端口结构体struct timer_list timer;            // 定时器用于处理超时unsigned int capabilities;          // 硬件能力unsigned int mcr;                   // 调制解调器控制寄存器unsigned int lsr;                   // 线路状态寄存器unsigned int msr;                   // 调制解调器状态寄存器unsigned int icr;                   // 中断控制寄存器unsigned int lcr;                   // 线路控制寄存器unsigned int fcr;                   // FIFO控制寄存器unsigned int ier;                   // 中断使能寄存器unsigned char mcr_mask;             // 调制解调器控制寄存器掩码unsigned char mcr_force;            // 调制解调器控制寄存器强制值unsigned char lsr_break_flag;       // 断开标志unsigned char bugs;                 // 硬件错误标志unsigned int tx_loadsz;             // 发送FIFO加载大小unsigned int acr;                   // 额外控制寄存器unsigned int ier_mask;              // 中断使能寄存器掩码unsigned int ier_force;             // 中断使能寄存器强制值unsigned int lsr_mask;              // 线路状态寄存器掩码unsigned int lsr_break_flag_mask;   // 断开标志掩码
};//通用结构体,但比较鸡肋
struct uart_port {spinlock_t lock; /* port lock */unsigned long iobase; /* in/out[bwl] */unsigned char __iomem *membase; /* read/write[bwl] */
......const struct uart_ops *ops;unsigned int custom_divisor;unsigned int line; /* port index */unsigned int minor;resource_size_t mapbase; /* for ioremap */resource_size_t mapsize;struct device *dev; /* parent device */

  这里补充一下在uart_driver提到的实现控制台打印功能必须要注册的结构体console和将uart_port与对应的circ_buf联系起来的uart_state结构体。

struct console {char name[16];void(*write)(struct console *const char *, unsigined);int (*read)(struct console *, char *, unsigned);struct tty_driver *(struct console *,int*);void (*unblank)(void);int  (*setup)(struct console *, char *);int  (*early_setup)(void);short  flags;short  index; /*用来指定该console使用哪一个uart port (对应的uart_port中的line),如果为-1,kernel会自动选择第一个uart port*/int   cflag;void  *data;struct   console *next;
};/*uart_state有两个成员在底层串口驱动会用到,即xmit和port。
用户空间程序通过串口发送数据时,上层驱动将用户数据保存在xmit;而串口发送中断处理函数就是通过xmit获取到用户数据并将它们发送出去。
串口接收中断处理函数需要通过port将接收到的数据传递给线路规程层。
*/
struct uart_state {struct  tty_port  port;enum uart_pm_state   pm_state;struct circ_buf     xmit;struct uart_port     *uart_port; /*对应于一个串口设备*/
}

  uart_ops结构体几乎涵盖了驱动可对串口的所有操作:

 struct uart_ops {unsigned int    (*tx_empty)(struct uart_port *);void            (*set_mctrl)(struct uart_port *, unsigned int mctrl);unsigned int    (*get_mctrl)(struct uart_port *);void            (*stop_tx)(struct uart_port *);void            (*start_tx)(struct uart_port *);void            (*throttle)(struct uart_port *);void            (*unthrottle)(struct uart_port *);void            (*send_xchar)(struct uart_port *, char ch);void            (*stop_rx)(struct uart_port *);void            (*enable_ms)(struct uart_port *);void            (*break_ctl)(struct uart_port *, int ctl);int             (*startup)(struct uart_port *);void            (*shutdown)(struct uart_port *);void            (*flush_buffer)(struct uart_port *);void            (*set_termios)(struct uart_port *, struct ktermios *new,struct ktermios *old);void            (*set_ldisc)(struct uart_port *, int new);void            (*pm)(struct uart_port *, unsigned int state,unsigned int oldstate);int             (*set_wake)(struct uart_port *, unsigned int state);/** Return a string describing the type of the port*/const char      *(*type)(struct uart_port *);/** Release IO and memory resources used by the port.* This includes iounmap if necessary.*/void            (*release_port)(struct uart_port *);/** Request IO and memory resources used by the port.* This includes iomapping the port if necessary.*/int             (*request_port)(struct uart_port *);void            (*config_port)(struct uart_port *, int);int             (*verify_port)(struct uart_port *, struct serial_struct *);int             (*ioctl)(struct uart_port *, unsigned int, unsigned long);
#ifdef CONFIG_CONSOLE_POLLint             (*poll_init)(struct uart_port *);void            (*poll_put_char)(struct uart_port *, unsigned char);int             (*poll_get_char)(struct uart_port *);
#endif
};

1.2 数据处理流程

  相较于前面学习的spi子系统和iic子系统,uart驱动通常情况不需要根据协议的时序额外编写新的传输函数,这里可以直接提供write函数和read函数实现数据的互传。
在这里插入图片描述
  这部分的内容我们在应用层实验的时候已经详细介绍过了,感兴趣可以回顾一下。

二、驱动编写

  我们这里采用的8250通用驱动,对于不同驱动其函数名称和入口参数可能有所不同,这里我们简单学习梳理一下框架即可,通常来说我们是只需要进行应用层的编写。

1. 设备树的修改

代码如下(示例):

uart3: serial@fe670000 {compatible = "rockchip,rk3568-uart", "snps,dw-apb-uart";reg = <0x0 0xfe670000 0x0 0x100>;interrupts = <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;clocks = <&cru SCLK_UART3>, <&cru PCLK_UART3>;clock-names = "baudclk", "apb_pclk";reg-shift = <2>;reg-io-width = <4>;dmas = <&dmac0 6>, <&dmac0 7>;pinctrl-names = "default";pinctrl-0 = <&uart3m0_xfer>;status = "disabled";};//uart3的复用
uart3 {/omit-if-no-ref/uart3m0_xfer: uart3m0-xfer {rockchip,pins =/* uart3_rxm0 */<1 RK_PA0 2 &pcfg_pull_up>,/* uart3_txm0 */<1 RK_PA1 2 &pcfg_pull_up>;};/omit-if-no-ref/uart3m0_ctsn: uart3m0-ctsn {rockchip,pins =/* uart3m0_ctsn */<1 RK_PA3 2 &pcfg_pull_none>;};/omit-if-no-ref/uart3m0_rtsn: uart3m0-rtsn {rockchip,pins =/* uart3m0_rtsn */<1 RK_PA2 2 &pcfg_pull_none>;};/omit-if-no-ref/uart3m1_xfer: uart3m1-xfer {rockchip,pins =/* uart3_rxm1 */<3 RK_PC0 4 &pcfg_pull_up>,/* uart3_txm1 */<3 RK_PB7 4 &pcfg_pull_up>;};};

  接着根据我们的需要继续编写即可,泰山派这里已经默认将uart3配置好了,这里可以直接调用。

//用户串口3
&uart3 {status = "okay";pinctrl-names = "default";pinctrl-0 = <&uart3m1_xfer>;...};

2. 相关API函数

//注册 uart_driver
int uart_register_driver(struct uart_driver *drv)
  • 参数
    • drv :要注册的 uart_driver
  • 返回值
    • 成功:0
    • 失败:负值
//注销uart_driver
void uart_unregister_driver(struct uart_driver *drv)
  • 参数
    • drv :要注销的 uart_driver
  • 返回值:无
//
int serial8250_register_8250_port(struct uart_8250_port *up);
  • 参数
    • up:指向 uart_8250_port 结构体的指针,该结构体包含了要注册的 8250 UART 端口的所有必要信息。
  • 返回值
    • 成功:端口的线路号(line)
    • 失败:错误码
void serial8250_unregister_port(int line);
  • 参数
    • line:要注销的 UART 端口的线路号(line),通常是在调用 serial8250_register_8250_port 时返回的值。
  • 返回值:无

3. 驱动框架

  实际上,我们可以简单地将uart驱动理解为是一个 platform 驱动在驱动入口函数中调用uart_register_driver 函数向 Linux 内核注册 uart_driver,在驱动出口函数中调用uart_unregister_driver 函数注销掉前面注册的 uart_driver,当设备和驱动匹配成功以后进入probe 函数,此函数的重点工作就是初始化uart_8250_port(uart_port),然后将其添加到对应的uart_driver 中。在初始化uart_port过程中,设置 uart_ops 为对应的 XXX_ops即可。

4. 具体功能的实现

4.1 出入口函数的编写

static int __init my_uart_init(void)
{int ret;// 初始化uart_driver结构体my_uart_driver = (struct uart_driver) {.owner      = THIS_MODULE,.driver_name= DRIVER_NAME,.dev_name   = "ttyMY",.major      = TTY_MAJOR,.minor      = 64,.nr         = 1,};// 注册UART驱动ret = uart_register_driver(&my_uart_driver);if (ret)return ret;// 初始化uart_8250_port结构体my_uart_port.port = (struct uart_port) {.iotype     = UPIO_PORT,.mapbase    = UART_BASE,.irq        = UART_IRQ,.uartclk    = UART_CLOCK,.fifosize   = 16,.ops        = &my_uart_ops,.line       = 0,};// 注册8250 UART端口line = serial8250_register_8250_port(&my_uart_port);if (line)uart_unregister_driver(&my_uart_driver);return line;
}static void __exit my_uart_exit(void)
{serial8250_unregister_port(line);uart_unregister_driver(&my_uart_driver);
}module_init(my_uart_init);
module_exit(my_uart_exit);

4.2 读写函数

static void my_uart_start_tx(struct uart_port *port)
{struct uart_8250_port *up = (struct uart_8250_port *)port;unsigned int ier;spin_lock_irq(&up->port.lock);// 启用发送中断ier = serial_in(up, UART_IER);serial_out(up, UART_IER, ier | UART_IER_THRI);spin_unlock_irq(&up->port.lock);
}static void my_uart_stop_tx(struct uart_port *port)
{struct uart_8250_port *up = (struct uart_8250_port *)port;unsigned int ier;spin_lock_irq(&up->port.lock);// 禁用发送中断ier = serial_in(up, UART_IER);serial_out(up, UART_IER, ier & ~UART_IER_THRI);spin_unlock_irq(&up->port.lock);
}static irqreturn_t my_uart_interrupt(int irq, void *dev_id)
{struct uart_8250_port *up = dev_id;unsigned int iir = serial_in(up, UART_IIR);// 检查中断类型并处理if (!(iir & UART_IIR_NO_INT)) {if (iir & UART_IIR_RDI)serial8250_rx_chars(up);if (iir & UART_IIR_THRI)serial8250_tx_chars(up);return IRQ_HANDLED;}return IRQ_NONE;
}

免责声明:本内容部分参考野火科技及其他相关公开资料,若有侵权或者勘误请联系作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/363491.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Redis四】主从复制、哨兵以及Cluster集群

目录 一.主从复制、哨兵、集群的区别 二.Redis主从复制 1.作用 2.原理 3.流程 三.搭建Redis 主从复制 1.源码编译安装以及配置文件修改 1.1.修改 Redis 配置文件&#xff08;Slave节点操作&#xff09; 2.验证主从复制 2.1.在Master节点上看日志 2.2.在Master节点上…

学习感悟丨在誉天学习数通HCIP怎么样

大家好&#xff0c;我是誉天学员的徐同学&#xff0c;学习的数通HCIP课程。 在学校的时候&#xff0c;听说下半年就要出去实习了&#xff0c;心中坎坷不安&#xff0c;现在我学到的知识远远不够的。然后就想着学点东西充实一下自己的知识面和专业能力&#xff0c;有一次和同学谈…

有没有能用蓝牙的游泳耳机,性能超凡的4大游泳耳机力荐

在现代科技的推动下&#xff0c;越来越多具备蓝牙功能的游泳耳机正在改变游泳爱好者的体验方式。这些创新产品不仅在防水性能上有了显著提升&#xff0c;还能让您在水中享受到高质量的音乐。然而&#xff0c;选择一款优秀的蓝牙游泳耳机并不简单&#xff0c;需要考虑到防水等级…

vite vue3使用axios解决跨域问题

引入依赖 npm install axios 在main.js中全局引入 import { createApp } from vue import App from ./App.vue import axios from axiosconst app createApp(App)// 全局引入axios app.config.globalProperties.$axios axiosapp.mount(#app) 修改vite.config.js的代理配置…

Java | Leetcode Java题解之第189题轮转数组

题目&#xff1a; 题解&#xff1a; class Solution {public void rotate(int[] nums, int k) {k % nums.length;reverse(nums, 0, nums.length - 1);reverse(nums, 0, k - 1);reverse(nums, k, nums.length - 1);}public void reverse(int[] nums, int start, int end) {whil…

搭建企业内网pypi镜像库,让python在内网也能像互联网一样安装pip库

目录 知识点实验1.服务器安装python2.新建一个目录/mirror/pip&#xff0c;用于存储pypi文件&#xff0c;作为仓库目录3.下载python中的所需包放至仓库文件夹/mirror/pip3.1. 新建requirement.py脚本&#xff08;将清华pypi镜像库文件列表粘贴到requirement.txt文件中&#xff…

代码随想录算法训练营第三十七天|01背包问题、分割等和子集

01背包问题 题目链接&#xff1a;46. 携带研究材料 文档讲解&#xff1a;代码随想录 状态&#xff1a;忘了 二维dp 问题1&#xff1a;为啥会想到i代表第几个物品&#xff0c;j代表容量变化&#xff1f; 动态规划中&#xff0c;每次决策都依赖于前一个状态的结果&#xff0c;在…

Radxa 学习摘录

文章目录 1、参考资料2、硬件知识3、shell4、交叉编译工具链5、问题6、DTS 1、参考资料 技术论坛&#xff08;推荐&#xff09; 官方资料下载 wiki资料 u-boot 文档 u-boot 源码 内核文档 内核源码 原理图 radxa-repo radxa-build radxa-pkg radxa-docs 2、硬件知识 Rad…

RabbitMQ(七)Shovel插件对比Federation插件

文章目录 Shovel和Federation的主要区别&#xff08;重点&#xff09;一、启用Shovel插件二、配置Shovel三、测试1、测试计划2、测试效果发布消息源节点目标节点 Shovel和Federation的主要区别&#xff08;重点&#xff09; • Shovel更简洁一些 • Federation更倾向于跨集群使…

国外的Claude3.5 Sonnet Artifacts和国内的CodeFlying孰强孰弱?

在Claude 3.5 Sonnet发布后&#xff0c;最受大家关注的问题应该就是它在编写代码能力上的变化。 要知道在Claude3.0发布以来的这几个月就因为它的编写代码能力而一直受到人们的诟病。 那Anthropic这次终于是不负众望&#xff0c;在Claude 3.5 Sonnet中更新了一个叫做Artifact…

mysql是什么

mysql是什么 是DBMS软件系统&#xff0c;并不是一个数据库&#xff0c;管理数据库 DBMS相当于用户和数据库之间的桥梁&#xff0c;有超过300种不同的dbms系统 mysql是关系型数据库&#xff0c;关系型数据库存储模型很想excel&#xff0c;用行和列组织数据 sql是一门编程语言…

致敬经典:在国产开源操作系统 RT-Thread 重温 UNIX 彩色终端

引言 上篇文章里我们向大家介绍了 RT-Thread v5.1.0 的一些新特性。其中包括了终端环境的进一步完善。终端是人机交互的重要接口。实用的终端工具可以显著地提升系统使用者的幸福指数。举例来说&#xff0c;当我们想要修改一些系统配置&#xff0c;或是编写脚本时&#xff0c;一…

鸿蒙开发设备管理:【@ohos.distributedHardware.deviceManager (设备管理)】

设备管理 本模块提供分布式设备管理能力。 系统应用可调用接口实现如下功能&#xff1a; 注册和解除注册设备上下线变化监听发现周边不可信设备认证和取消认证设备查询可信设备列表查询本地设备信息&#xff0c;包括设备名称&#xff0c;设备类型和设备标识 说明&#xff1a…

什么是ArchiMate?有优缺点和运用场景?

一、什么是ArchiMate? ArchiMate是一种由The Open Group发布的企业级标准&#xff0c;它是一种整合多种架构的可视化业务分析模型语言&#xff0c;也属于架构描述语言&#xff08;ADL&#xff09;。ArchiMate主要从业务、应用和技术三个层次&#xff08;Layer&#xff09;&…

Web渗透:php反序列化漏洞

反序列化漏洞&#xff08;Deserialization Vulnerability&#xff09;是一种在应用程序处理数据的过程中&#xff0c;因不安全的反序列化操作引发的安全漏洞&#xff1b;反序列化是指将序列化的数据&#xff08;通常是字节流或字符串&#xff09;转换回对象的过程&#xff0c;如…

k8s架构设计思想

1.谷歌borg云计算管理平台 一类&#xff1a;infrastucture platform software 另一类&#xff1a;borg为主的非虚拟化技术&#xff0c;调度进程 核心是轻量级作业调度&#xff0c;不是做虚拟化/云平台的 borg本身用了一些容器技术 生产业务product workload要求高可用&#xf…

AI 基础设施要点——AB Periasamy 和 Matthew DeMello 主持的 AI

MinIO 的联合创始人兼首席执行官 AB Periasamy 最近在 AI in Business 播客中亮相&#xff0c;他与 Emerj 的高级编辑 Matthew DeMello 就企业的 AI 基础设施和对象存储进行了丰富的对话。 在这篇博文中&#xff0c;我们将带您了解所讨论内容的删节版本。让我们开始吧。 AB 和…

操作系统之《PV操作》【知识点+详细解题过程】

1、并发进程 &#xff1a; 并发的实质是一个处理器在几个进程之间的多路复用&#xff0c;并发是对有限的物理资源强制行使多用户共享&#xff0c;消除计算机部件之间的互等现象&#xff0c;以提高系统资源利用率。 &#xff08;1&#xff09;并发进程——互斥性&#xff1a; 进…

ingress相关yaml文件报错且相关资源一切正常解决方法

今天在执行ingress相关文件的时候莫名其妙报错了&#xff0c;问了别人得知了这个方法 执行ingress相关文件报错 01.yaml是我自己创建关于ingress的yaml文件 报错信息 且相关资源一切正常 解决方法 kubectl get validatingwebhookconfigurations删除ingress-nginx-admissio…

在Qt中,直接include <moc_xxxxx.cpp> 为什么不会出现符号冲突的错误?

在逛Qt官方社区的时候看到这样一个帖子&#xff1a; https://forum.qt.io/topic/117973/how-does-include-moc_-cpp-work 大概的意思是moc_xxx.cpp如果已经被编译器编译&#xff0c;那么在另一个cpp文件中include同一个moc_xxx.cpp应该出现符号冲突才对&#xff0c;但是Qt却能正…