字符设备驱动
一、注册设备号
为了让内核知道这个设备是合法的
- 主次设备号
- MKDEV
- register_chrdev_region
驱动部分
00_头文件
#include <linux/fs.h> //for MKDEV register_chrdev_region
01_主次设备号
#define LED_MA 500 //主设备号 用于区分不同种类的设备
//某些主设备号已经静态地分配给了大部分公用设备。见Documentation/devices.txt 。
#define LED_MI 0 //次设备号 用于区分同一类型的多个设备
#define LED_NUM 1 //有多少个设备
02_注册字符设备号
dev_t devno = MKDEV(LED_MA, LED_MI);
int ret;
ret = register_chrdev_region(devno, LED_NUM, "my_led"); /*注册字符设备号(静态分配),为了让内核认可
为一个字符驱动获取一个或多个设备编号
dev_id: 分配的起始设备编号(常常是0)
DEVICE_NUM: 请求的连续设备编号的总数(不能太大,避免别的主设备号冲突)
DEVICE_NAME: 是应当连接到这个编号范围的设备的名字
alloc_chrdev_region 可进行动态分配
*/
if (ret < 0) {
printk("register_chrdev_region\n");
return ret;
}
03_取消注册
dev_t devno = MKDEV(LED_MA, LED_MI);
unregister_chrdev_region(devno, LED_NUM); //取消注册
总程序
//led.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h> //for MKDEV register_chrdev_region
#define LED_MA 500 //主设备号 用于区分不同种类的设备
//某些主设备号已经静态地分配给了大部分公用设备。见Documentation/devices.txt 。
#define LED_MI 0 //次设备号 用于区分同一类型的多个设备
#define LED_NUM 1 //有多少个设备
static int led_init(void)
{
dev_t devno = MKDEV(LED_MA, LED_MI);
int ret;
ret = register_chrdev_region(devno, LED_NUM, "my_led"); /*注册字符设备号(静态分配)
为一个字符驱动获取一个或多个设备编号
dev_id: 分配的起始设备编号(常常是0)
DEVICE_NUM: 请求的连续设备编号的总数(不能太大,避免别的主设备号冲突)
DEVICE_NAME: 是应当连接到这个编号范围的设备的名字
alloc_chrdev_region 可进行动态分配
*/
if (ret < 0) {
printk("register_chrdev_region\n");
return ret;
}
printk("led init\n");
return 0; //返回值 0:成功 负值:失败
}
static void led_exit(void)
{
dev_t devno = MKDEV(LED_MA, LED_MI);
unregister_chrdev_region(devno, LED_NUM); //取消注册
printk("led exit\n");
}
module_init(led_init); //模块加载入口声明
module_exit(led_exit); //模块卸载入口声明
MODULE_LICENSE("GPL"); //模块免费开源声明
验证测试
# insmod led.ko /*加载模块
# rmmod led //卸载模块
二、初始化字符设备
-
file_operations
-
cdev_init
-
cdev_add
驱动部分
00_头文件
#include <linux/cdev.h> //字符设备头文件
01_字符设备初始化
struct file_operations led_fops 这部分全是函数指针
struct cdev cdev; //定义字符设备
static int led_open(struct inode *inode, struct file *file)
{
printk("driver led open\n");
return 0;
}
static int led_release(struct inode *inode, struct file *file)
{
printk("driver led close\n");
return 0;
}
struct file_operations led_fops = {
//文件操作(一切皆文件)
.owner = THIS_MODULE,
.open = led_open,
.release = led_release,
};
cdev_init(&cdev, & led_fops);//字符设备初始化
ret = cdev_add(&cdev, devno, LED_NUM); //添加字符设备到系统中
if (ret < 0) {
printk("cdev_add\n");
return ret;
}
02_字符设备删除
这个删完,再取消注册,相当于把空间中的内容都清掉,再把空间释放
cdev_del(&cdev)
应用部分
交叉编译aarch64-linux-gnu-gcc app.c
//app.c
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/ioctl.h>
int main(int argc, char **argv)
{
int fd;
fd = open("/dev/led", O_RDWR);
if (fd < 0) {
perror("open");
exit(1);
}
printf("open led ok\n"); //注意要加\n 否则打印信息可能没有
return 0;
}
总程序
//led.c
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h> //for MKDEV register_chrdev_region
#include <linux/cdev.h> //字符设备头文件
#define LED_MA 500 //主设备号 用于区分不同种类的设备
//某些主设备号已经静态地分配给了大部分公用设备。见Documentation/devices.txt。
#define LED_MI 0 //次设备号 用于区分同一类型的多个设备
#define LED_NUM 1 //有多少个设备
struct cdev cdev; //定义字符设备
static int led

本文介绍了Linux内核中如何注册和管理字符设备驱动,包括注册设备号、初始化字符设备、实现ioctl自定义命令以及读写文件操作。通过示例代码展示了从设备注册到硬件控制的完整流程,包括异常处理和资源释放。
最低0.47元/天 解锁文章
288

被折叠的 条评论
为什么被折叠?



