新字符驱动设备
首先看看分配设备号的函数(动态分配和静态分配):
int register_chrdev_region(dev_t, unsigned, const char *);
// 静态分配:通过已知未使用的主设备号像内核注册设备号
// 缺点:设备号可能重复导致注册失败
// 参数1:申请的设备号
// 参数2:申请设备号个数(主设备号不变,次设备号增加)
// 参数3:设备名(体现在/proc/devices)
// 返回值: 成功返回0,失败返回负数
int alloc_chrdev_region(dev_t *, unsigned, unsigned, const char *);
// 动态分配设备号
// 参数1:系统分配的设备号将保存在该变量中
// 参数2:起始次设备号
// 参数3:申请的设备号个数
// 参数4:设备名(体现在/proc/devices)
// 返回值: 成功返回0,失败返回负数
void unregister_chrdev_region(dev_t, unsigned);
// 注销申请的设备号
// 参数1:需要注销的设备号
// 参数2:注销设备号的个数
// 返回值:无
向内核注册字符设备
void cdev_init(struct cdev *, const struct file_operations *);
// 先通过这个函数初始化字符设备结构体cdev并指定了fops
// 参数1:需要初始化的字符设备结构体
// 参数2:文件操作集合
// 返回值:无
int cdev_add(struct cdev *, dev_t, unsigned);
// 将该字符设备注册进内核
// 参数1:需要注册的字符设备结构体
// 参数2:设备号
// 参数3:需要注册的设备个数
// 返回值:成功为0,失败为负数
void cdev_del(struct cdev *);
// 删除字符设备结构体
// 参数1:需要删除的字符设备结构体
// 返回值:无
总结一下新字符设备驱动程序的流程:
- 静态或者动态的申请设备号
- 初始化并注册字符设备结构体cdev到内核
- 在/sys/class目录下创建类
- 自动生成设备节点
- 其他操作
代码如下:
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/cdev.h>
#include <linux/device.h>
#define DEV_NUM (1)
#define DEV_NAME "test_dev"
#define TEST_DESCRIPTION "test description"
static struct test
{
dev_t dev;
int major;
int minor;
struct cdev cdev;
struct class *pclass;
}test;
static struct file_operations fops =
{
.owner = THIS_MODULE,
};
static int __init test_init(void)
{
int ret = -1;
test.major = 0;
test.minor = 0;
test.dev = MKDEV(test.major,test.minor);
if(test.major)
ret = register_chrdev_region(test.dev,DEV_NUM,DEV_NAME);
else
{
ret = alloc_chrdev_region(&test.dev,0,DEV_NUM,DEV_NAME);
test.major = MAJOR(test.dev);
test.minor = MINOR(test.dev);
}
if(ret < 0)
goto fail_get_chrdev;
cdev_init(&test.cdev,&fops);
test.cdev.owner = THIS_MODULE;
ret = cdev_add(&test.cdev,test.dev,DEV_NUM);
if(ret < 0 )
goto fail_cdev_add;
test.pclass = class_create(THIS_MODULE,"test_class");
if(IS_ERR(test.pclass))
{
ret = PTR_ERR(test.pclass);
goto fail_class_create;
}
for(int i =0;i<DEV_NUM;i++)
{
struct device *temp_dev = device_create(test.pclass,NULL,MKDEV(test.major,i),NULL,"test_dev%d",i);
ret = PTR_ERR_OR_ZERO(temp_dev);
if(ret < 0)
goto fail_device_create;
}
return 0;
fail_device_create:
for(int i=0;i<DEV_NUM;i++)
device_destroy(test.pclass,MKDEV(test.major,i));
fail_class_create:
class_destroy(test.pclass);
fail_cdev_add:
cdev_del(&test.cdev);
fail_get_chrdev:
unregister_chrdev_region(test.dev,DEV_NUM);
return -1;
}
static void __exit test_exit(void)
{
for(int i=0;i<DEV_NUM;i++)
device_destroy(test.pclass,MKDEV(test.major,i));
class_destroy(test.pclass);
cdev_del(&test.cdev);
unregister_chrdev_region(test.dev,DEV_NUM);
}
module_init(test_init);
module_exit(test_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("ChenGuo");
MODULE_DESCRIPTION(TEST_DESCRIPTION);
Makefile文件:
KERNEL_DIR := /home/chen/alientek/imx6ull/kernel/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_DIR := $(shell pwd)
obj-m := test.o
ccflags-y := -std=gnu99 -Wno-declaration-after-statement
kernel_module:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules
.PHONY:clean
clean:
$(MAKE) -C $(KERNEL_DIR) M=$(CURRENT_DIR) ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- clean