1、确定主设备号
static int major = 0;
2、定义一个file_operations结构体
static struct file_operations my_drv = {
.owner = THIS_MODULE,
.open = my_drv_open,
.read = my_drv_read,
.write = my_drv_write,
.release = my_drv_close,
};
其中,file_operations 是Linux内核中用于字符设备驱动程序的结构体,驱动程序注册时,需要将file_operations结构体的指针传递给内核,以便内核在需要时调用相应的操作函数。
这样,用户空间的应用程序就可以通过文件操作接口与设备驱动程序进行交互它定义了字符设备驱动程序的操作函数,这些函数用于处理对字符设备的不同操作,如打开、关闭、读取、写入等。
结构体定义在<linux/fs.h>头文件中,其定义如下:
struct file_operations {
//一个指向拥有该结构体的模块的指针
struct module *owner;
//用于实现文件指针的定位操作(如lseek系统调用)
loff_t (*llseek) (struct file *, loff_t, int);
//用于实现从设备中读取数据的操作
ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
//用于实现向设备中写入数据的操作
ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
//用于打开设备文件时的操作
int (*open) (struct inode *, struct file *);
//用于关闭设备文件时的操作
int (*release) (struct inode *, struct file *);
// 其他函数指针...
};
3、分别实现上述的my_drv_open/read/write/close函数
#define MIN(a, b) (a < b ? a : b)
static ssize_t my_drv_read(struct file *file, char __user *buf, size_t size, loff_t *offeset){
int err;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
/*
unsigned long copy_to_user(void __user *to, const void *from, unsigned long n);
是一个用于将内核空间数据拷贝到用户空间的函数。
它是在Linux内核中提供的一个函数,用于在内核态中将数据从内核空间复制到用户空间。
参数声明:
(1)to :指向用户空间的目标缓冲区的指针
(2)from :指向内核空间源缓冲区的指针
(3)n :要拷贝的字节数
函数返回值:
成功复制的字节数
*/
err = copy_to_user(buf,kernel_buf,MIN(1024,size));
return err;
}
static ssize_t my_drv_write(struct file *file, const char __user *buf, size_t size, loff_t *offset){
int err;
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
/*
unsigned long copy_from_user(void *to, const void __user *from, unsigned long n)
是Linux内核中的一个函数,用于将用户空间的数据复制到内核空间
参数说明:
to :目标缓冲区的指针,表示要将数据复制到的内核空间地址
from :源缓冲区的指针,表示要从用户空间复制数据的地址
n :要复制的字节数
返回值:
返回未复制成功的字节数,如果返回0表示全部复制成功。
*/
err = copy_from_user(kernel_buf,buf,MIN(1024,size));
return err;
}
static int my_drv_open(struct inode *node, struct file *file){
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
static int my_drv_close(struct inode *node, struct file *file){
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
return 0;
}
4、注册驱动程序(驱动入口函数)
static int __init mydrv_init(void)
{
int err;
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
/*
int register_chrdev(unsigned int major, const char *name, const struct file_operations *fops)
该函数是Linux内核中的一个函数,用于注册字符设备驱动程序。
参数说明:
major:主设备号,用于唯一标识一个字符设备驱动程序,
如果传入0,则表示由内核动态分配主设备号。
name:设备名称,用于在/dev目录下创建设备文件。
fops:指向struct file_operations的指针,表示字符设备驱动程序提供的操作函数。
返回值:
返回0表示注册成功,负值表示注册失败
register_chrdev函数用于注册一个字符设备驱动程序,
将其与主设备号、设备名称和操作函数关联起来。
注册成功后,内核会分配一个主设备号,并在/dev目录下创建相应的设备文件(名称由name参数指定)。
当用户打开设备文件并进行读写操作时,内核会调用注册的操作函数来处理相应的操作。
*/
major = register_chrdev(0, "mydrv", &my_drv); /* /dev/mydrv*/
/*
struct class *class_create(struct module *owner, const char *name)
Linux内核中的一个函数,用于创建一个设备类
参数说明:
owner:指向拥有该设备类的内核模块的指针。通常使用THIS_MODULE宏表示当前模块。
name:设备类的名称,用于在/sys/class目录下创建相应的类目录。
返回值:
返回一个指向struct class的指针,表示创建的设备类。如果创建失败,返回NULL。
class_create函数用于在内核中创建一个设备类,设备类是一组具有相似特性的设备的集合。
通过创建设备类,可以将一组设备归类并在/sys/class目录下创建相应的类目录,方便用户空
间和内核空间进行设备管理。
注意,class_create函数通常与device_create函数一起使用,
后者用于在设备类下创建具体的设备对象。
创建设备对象后,可以在/dev目录下看到相应的设备文件,方便用户空间与设备进行交互。
*/
mydrv_class = class_create(THIS_MODULE, "mydrv_class");// /sys/class/mydrv_class
err = PTR_ERR(mydrv_class);
if (IS_ERR(hello_class)) {
printk("%s %s line %d\n", __FILE__, __FUNCTION__, __LINE__);
unregister_chrdev(major, "mydrv");
return err;
}
/*
struct device *device_create(struct class *class, struct device *parent, dev_t devt, void *drvdata, const char *fmt, ...)
该函数用于在指定的设备类下创建一个设备对象
参数说明:
class:指向要创建设备对象的设备类的指针。
parent:指向父设备的指针,如果没有父设备,可以传入NULL。
devt:设备号,表示要创建的设备对象的设备号。
drvdata:设备对象关联的私有数据,可以是任意类型的指针。
fmt:设备对象的名称格式字符串,用于在/sys/class/<class_name>目录下创建相应的设 备目录。
返回值:
返回一个指向struct device的指针,表示创建的设备对象。如果创建失败,返回NULL
*/
device_create(mydrv_class, NULL, MKDEV(major, 0), NULL, "mydrv");
// sys/class/mydrv_class/mydrv
return 0;
}
6、出口函数(卸载驱动程序)
static void __exit mydrv_exit(void){
printk("%s %s line %d\n",__FILE__,__FUNCTION__,__LINE__);
/*
void device_destroy(struct class *class, dev_t devt)
用于销毁一个设备对象;
参数说明:
class:指向设备对象所属的设备类的指针;
devt:设备号,表示要销毁的设备对象的设备号;
device_destroy函数用于销毁指定设备类下的指定设备对象。它会释放设备对象占用的资源,
并从内核中移除该设备对象。
同时,相关的设备文件和/sys/class/<class_name>目录中的设备目录也会被删除;
在调用device_destroy函数之前,通常需要确保设备对象已经停止使用,
并释放与之相关的资源。销毁设备对象应该在不再需要该设备对象的时候进行,
以避免资源泄漏和冲突;
*/
device_destroy(mydrv_class,MKDEV(major,0));
/*
void class_destroy(struct class *cls)
用于销毁字符设备类(struct class)。它的原型在头文件<linux/device.h>中定义。
该函数接收一个指向struct class类型的指针作为参数,该指针指向要销毁的字符设备类。
该函数会释放与字符设备类相关的资源,并将其从系统中移除。
*/
class_destroy(mydrv_class);
/*
void unregister_chrdev(unsigned int major, const char *name)
用于注销已经注册的字符设备驱动程序;
参数说明:
major:要注销的字符设备驱动程序的主设备号;
name:要注销的字符设备驱动程序的设备名称;
*/
unregister_chrdev(major,"mydrv");
}
7、提供设备信息,自动创建设备节点
module_init(mydrv_init);
module_exit(mydrv_exit);