Linux字符设备驱动开发教程:详解CDD驱动示例

本文将以一个名为CDD的字符设备驱动为例,详细讲解Linux字符设备驱动的开发流程。

代码示例

#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
dev_t dev;
struct cdev cdev;
struct class *cdd_class;//设备类指针
struct device *cdd_device;//设备文件指针

int cdd_open(struct inode *inode,struct file *filp)
{
	printk("enter cdd_open\n");
}

ssize_t  cdd_read(struct file *filp,char __user *buf,size_t size ,loff_t  loff)
{
	printk("cdd_read\n");
}

ssize_t  cdd_write(struct file *filp,const char __user *buf,size_t size ,loff_t  loff)
{
	printk("cdd_write\n");
}


long cdd_ioctl (struct file *filp,unsigned int cmd,unsigned long data)
{
	printk("cdd_ioctrl\n");
}

struct file_operations cdd_fops={
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
unlocked_ioctl=cdd_ioctl,//oictl接口
.release=cdd_release,
}

int myinit(void)
{
// 1 申请设备号
int ret;
ret=alloc_chrdev_region(&dev,CDD_MINOR,CDD_COUNT,"cdd_demo");
if(ret<0) {printk("device init error.\n");return ret;}
//初始化设备结构
cdev_init(&cdd_cdev,&cdd_fops);
// 2 添加设备到内核
ret=cdev_add(&cdd_cdev,dev,CDD_COUNT);
if(ret<0) {printk("device init error.\n");unregister_chrdev_region(dev,CDD_COUNT);return ret;}
printk("succeed.\n");
// 3 创建设备类
cdd_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(cdd_class)){
	cdev_del(&cdd_cdev);
	unregister_chrdev_region(dev,CDD_COUNT);
	printk("class_create failed.\n");
	ret=PTR_ERR(cdd_class);
	return ret;}
// 4创建设备文件
cdd_device=device_create(cdd_class,NULL,dev,NULL,"cdd");
if(IS_ERR(cdd_device)){
	class_destroy(cdd_class,dev);
	cdev_del(&cdd_cdev);
	unregister_chrdev_region(dev,CDD_COUNT);
	printk("class_create faile.\n");
	ret=PTR_ERR(cdd_device);
	return ret;}
return 0;	
}
void myExit(void)
{
	device_destroy(cdd_device);// 4
	class_destroy(cdd_class,dev);// 3
	cdev_del(&cdd_cdev);// 2
	unregister_chrdev_region(dev,CDD_COUNT);// 1
}
	module_init(myinit);
	module_exit(myExit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxxx");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("A symbol TEST");

代码详解

  1. 定义模块的主设备号、次设备号和设备数量
#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
  • CDD_MAJOR 定义了主设备号,通常需要在系统中保持唯一性。
  • CDD_MINOR 定义了次设备号,可以根据需要分配多个次设备号。
  • CDD_COUNT 定义了设备数量,通常为 1,表示只创建了一个设备。
  1. 定义设备结构体和设备类指针、设备文件指针
dev_t dev;
struct cdev cdev;
struct class *cdd_class;//设备类指针
struct device *cdd_device;//设备文件指针
  • dev 用于存储分配的设备号。
  • cdev 是设备结构体,用来保存设备信息。
  • cdd_class 指向设备类,用来组织设备。
  • cdd_device 指向设备文件,用来访问设备。
  1. 实现设备的打开、读、写、ioctl等操作函数
int cdd_open(struct inode *inode,struct file *filp)
{
	printk("enter cdd_open\n");
}

ssize_t  cdd_read(struct file *filp,char __user *buf,size_t size ,loff_t  loff)
{
	printk("cdd_read\n");
}

ssize_t  cdd_write(struct file *filp,const char __user *buf,size_t size ,loff_t  loff)
{
	printk("cdd_write\n");
}


long cdd_ioctl (struct file *filp,unsigned int cmd,unsigned long data)
{
	printk("cdd_ioctrl\n");
}
  • cdd_open 在设备打开时被调用。
  • cdd_read 在设备读取时被调用。
  • cdd_write 在设备写入时被调用。
  • cdd_ioctl 在设备执行 ioctl 操作时被调用。
  1. 定义文件操作函数表
struct file_operations cdd_fops={
.owner=THIS_MODULE,
.open=cdd_open,
.read=cdd_read,
.write=cdd_write,
unlocked_ioctl=cdd_ioctl,//oictl接口
.release=cdd_release,
}
  • cdd_fops 结构体保存了设备的各种操作函数,供内核调用。
  1. 在模块初始化函数中,申请设备号、初始化设备结构、添加设备到内核、创建设备类和创建设备文件
int myinit(void)
{
// 1 申请设备号
int ret;
ret=alloc_chrdev_region(&dev,CDD_MINOR,CDD_COUNT,"cdd_demo");
if(ret<0) {printk("device init error.\n");return ret;}
//初始化设备结构
cdev_init(&cdd_cdev,&cdd_fops);
// 2 添加设备到内核
ret=cdev_add(&cdd_cdev,dev,CDD_COUNT);
if(ret<0) {printk("device init error.\n");unregister_chrdev_region(dev,CDD_COUNT);return ret;}
printk("succeed.\n");
// 3 创建设备类
cdd_class=class_create(THIS_MODULE,"cdd_class");
if(IS_ERR(cdd_class)){
	cdev_del(&cdd_cdev);
	unregister_chrdev_region(dev,CDD_COUNT);
	printk("class_create failed.\n");
	ret=PTR_ERR(cdd_class);
	return ret;}
// 4创建设备文件
cdd_device=device_create(cdd_class,NULL,dev,NULL,"cdd");
if(IS_ERR(cdd_device)){
	class_destroy(cdd_class,dev);
	cdev_del(&cdd_cdev);
	unregister_chrdev_region(dev,CDD_COUNT);
	printk("class_create faile.\n");
	ret=PTR_ERR(cdd_device);
	return ret;}
return 0;	
}
  1. 在模块退出函数中,销毁设备文件、销毁设备类、删除设备、释放设备号
void myExit(void)
{
	device_destroy(cdd_device);// 4
	class_destroy(cdd_class,dev);// 3
	cdev_del(&cdd_cdev);// 2
	unregister_chrdev_region(dev,CDD_COUNT);// 1
}
  1. 定义模块的基本信息,如许可证、作者、版本号和描述等
MODULE_LICENSE("GPL");
MODULE_AUTHOR("xxxxx");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("A symbol TEST");

总结

本文通过CDD驱动示例,讲解了Linux字符设备驱动的开发流程,并详细解释了代码中的关键部分。希望本文能帮助你理解Linux字符设备驱动开发的原理和方法,并能够独立完成自己的字符设备驱动开发。

注意事项

  • 设备号申请需要保证在系统中唯一性,避免和其他设备冲突。
  • 设备类创建可以根据需要进行,但建议创建设备类以更好地组织设备。
  • 设备文件创建后,用户可以通过 /dev/cdd 访问设备。
  • 代码中提供的 cdd_opencdd_readcdd_writecdd_ioctl 函数仅用于示例,实际应用中需要根据具体需求实现。
Linux字符设备驱动开发教程:详解CDD驱动示例

原文地址: https://www.cveoy.top/t/topic/oUwW 著作权归作者所有。请勿转载和采集!

免费AI点我,无需注册和登录