Linux驱动开发2:字符设备驱动

Linux驱动开发2:字符设备驱动

字符设备驱动开发流程

字符设备是 Linux 驱动中最基本的一类设备驱动,字符设备就是一个一个字节,按照字节流进行读写操作的设备,读写数据是分先后顺序的。比如最常见的点灯、按键、 IIC、 SPI, LCD 等等都是字符设备,这些设备的驱动就叫做字符设备驱动。

驱动就是获取外设、或者传感器数据,控制外设。数据会提交给应用程序。Linux驱动编译既要编写一个驱动,还要我们编写一个简单的测试应用程序,APP,Linux下驱动和应用是完全分开的。

字符设备的注册与注销

注册字符设备使用register_chrdev函数

函数原型:static inline int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)

major : 主设备号, Linux 下每个设备都有一个设备号,设备号分为主设备号和次设备号两部分
name :设备名字,指向一串字符串。
fops: 结构体 file_operations 类型指针,指向设备的操作函数集合变量。

注销字符设备使用 unregister_chrdev函数

函数原型:static inline void unregister_chrdev(unsigned int major, const char *name);
major : 要注销的设备对应的主设备号。
name: 要注销的设备对应的设备名。

设备号

Linux 中每个设备都有一个设备号,设备号由主设备号和次设备号两部分组成,主设备号表示某一个具体的驱动,次设备号表示使用这个驱动的各个设备

Linux 提供了一个名为 dev_t 的数据类型表示设备号

dev_t 其实就是 unsigned int 类型,是一个 32 位的数据类型,其中高 12 位为主设备号, 低 20 位为次设备号。

可以通过cat /proc/devices命令查看当前设备中已被使用的主设备号

字符驱动编写

字符设备驱动的编写主要就是驱动对应的open/close/read/write函数的编写,本质上就是对file_operations结构体的成员变量的实现。

在此给出结构体函数定义

完善实现file_operations 结构体的成员变量open/close/read/write函数的实现后进行编译

编写应用程序进行测试

加载驱动后移植应用代码进行测试

输入命令手动创建驱动节点

输入命令测试chrdevbase 驱动

最后给出一般流程下的完整字符设备驱动框架

c 复制代码
字符驱动开发总流程
一、设备树定义
1、在设备树文件中定义节点
	示例:
	gpioled {
		#address-cells = <1>;
		#size-cells = <1>;
		compatible = "alientek,gpioled";
		pinctrl-names = "default";
		pinctrl-0 = <&pinctrl_gpioled>;
		led-gpios = <&gpio1 3 GPIO_ACTIVE_LOW>;
		status = "okay";
	};

	pinctrl_gpioled: gpiogrp {
			fsl,pins = <
				MX6UL_PAD_GPIO1_IO03__GPIO1_IO03	0x10b0	/* gpioled */	
			>;
	};

二、驱动代码开发
1、module_init和module_exit
2、声明模块相关信息
	1-作者:MODULE_AUTHOR(author);
	2-描述:MODULE_DESCRIPTION(description);
	3-版本:MODULE_VERSION(version_string);
	4-设备表:MODULE_DEVICE_TABLE(table_info);
	5-别名:MODULE_ALIAS(alternate_name);
	6-开源协议:MODULE_LICENSE("GPL");
3、定义字符设备结构体
4、定义设备操作函数 file_operations
5、实现init函数流程
	1-注册字符设备,判断major是否已被指定
		Y:register_chrdev_region(dev_t from, unsigned count, const char *name)
		N:alloc_chrdev_region(dev_t *dev, unsigned baseminor, unsigned count, const char *name)	-> MAJOR,MINOR
	2-添加cdev字符设备
		cdev_init(struct cdev *cdev, const struct file_operations *fops) -> cdev_add(struct cdev *p, dev_t dev, unsigned count)
	3-创建class设备类	
		class_create(owner, const char *name)
	4-创建device设备
		device_create(struct class *class, NULL , dev_t devt, NULL, const char *name)
	5-获取设备树节点信息
		of_find_node_by_path(const char *path)
		5.1-获取对应的GPIO
			of_get_named_gpio(struct device_node *np, const char *propname, int index)
		5.2-申请IO
			gpio_request(unsigned gpio, const char *label)
		5.3-设置GPIO口输入输出模式并配置默认输出模式
			gpio_direction_output(unsigned gpio, int value)
		5.4-设置指定GPIO口电平值
			gpio_set_value(unsigned int gpio, int value)
	6-获取设备树属性信息
		of_property_read_string-字符串
		of_property_count_elems_of_size-获取数组大小
		of_property_read_u32_array-从数组中获取每个元素
		等
	7-实现write操作函数-从应用程序用户空间拷贝数据
		copy_from_user(void *to, const void __user *from, unsigned long n)
	//7-实现地址映射
	//	ioremap 或 of_iomap
6、实现exit函数流程
	1-释放GPIO
		gpio_free(unsigned gpio)
	//2-取消地址映射
	//	iounmap
	3-删除device设备
		device_destroy(struct class *class, dev_t devt)
	4-删除class设备类
		class_destroy(struct class *cls)
	5-注销cdev字符设备
		cdev_del(struct cdev *p)
	6-释放设备号
		unregister_chrdev_region(dev_t from, unsigned count)
相关推荐
xiaoye-duck37 分钟前
《Linux 网络编程》深入理解 IP 协议(二):网段划分、私有 IP 与 NAT 地址转换
linux·网络·ip
zx_741484811 小时前
【Linux入门】Shell 函数、正则表达式与文本处理:cut 与 awk
linux·运维·正则表达式
Zenova EdgeOS2 小时前
Linux dmesg 工业边缘实战:内核日志过滤、持久化与故障定位
linux·运维·边缘计算·工业边缘·dmesg·内核日志
金士顿2 小时前
System.Text.Json Source Generator 深度解析:为什么 Native AOT 不喜欢反射
linux·asp.net core·arm64·net·native aot
wdfk_prog3 小时前
ROS教程08:从 TransportTCP::connect() 追到 TCPROS Connection Header、序列化与 Socket 数据传输
运维·缓存·docker·容器·ros
做运维的阿瑞3 小时前
Python 标准库汇总:分类速览与常用模块清单
linux·运维·python
百万蹄蹄向前冲3 小时前
密码都对Node.js却连不上Linux数据库
linux·mysql
彧azz3 小时前
Linux 网络编程学习总结
linux·网络·笔记·学习·面试
日常筹谋记4 小时前
自动化仓储安全防护工况评估:明治传感器AS-33C技术适配性分析
大数据·运维·创业创新·业界资讯
susplus4 小时前
【ARM 裸机开发 (IMX6ULL-mini)】GNU工具、Makefile 工程构建、链接脚本详解|C 语言点灯 + 蜂鸣器驱动
linux·arm·makefile·imx6ull