Linux字符设备驱动开发教程:详解CDD驱动示例
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");
代码详解
- 定义模块的主设备号、次设备号和设备数量
#define CDD_MAJOR 200
#define CDD_MINOR 0
#define CDD_COUNT 1
CDD_MAJOR定义了主设备号,通常需要在系统中保持唯一性。CDD_MINOR定义了次设备号,可以根据需要分配多个次设备号。CDD_COUNT定义了设备数量,通常为 1,表示只创建了一个设备。
- 定义设备结构体和设备类指针、设备文件指针
dev_t dev;
struct cdev cdev;
struct class *cdd_class;//设备类指针
struct device *cdd_device;//设备文件指针
dev用于存储分配的设备号。cdev是设备结构体,用来保存设备信息。cdd_class指向设备类,用来组织设备。cdd_device指向设备文件,用来访问设备。
- 实现设备的打开、读、写、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 操作时被调用。
- 定义文件操作函数表
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结构体保存了设备的各种操作函数,供内核调用。
- 在模块初始化函数中,申请设备号、初始化设备结构、添加设备到内核、创建设备类和创建设备文件
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_LICENSE("GPL");
MODULE_AUTHOR("xxxxx");
MODULE_VERSION("1.0");
MODULE_DESCRIPTION("A symbol TEST");
总结
本文通过CDD驱动示例,讲解了Linux字符设备驱动的开发流程,并详细解释了代码中的关键部分。希望本文能帮助你理解Linux字符设备驱动开发的原理和方法,并能够独立完成自己的字符设备驱动开发。
注意事项
- 设备号申请需要保证在系统中唯一性,避免和其他设备冲突。
- 设备类创建可以根据需要进行,但建议创建设备类以更好地组织设备。
- 设备文件创建后,用户可以通过
/dev/cdd访问设备。 - 代码中提供的
cdd_open、cdd_read、cdd_write和cdd_ioctl函数仅用于示例,实际应用中需要根据具体需求实现。
原文地址: https://www.cveoy.top/t/topic/oUwW 著作权归作者所有。请勿转载和采集!