前面介绍的设备中的模型:bus,device,driver都是有比较明确的定义,bus代表总线,device代表实际的设备和接口,driver代表驱动
class是设备类,它是一个抽象的概念,没有对应的实体,它是提供给用户接口相似的一类设备的集合。常见的有输入子系统input、usb、串口tty、块设备block等
misc杂项设备class这个部分,系统对这部分进行了打包
使用ls /sys/class查看设备类
函数class_create创建class类文件
#define class_create(owner, name)
参数1:一般是THIS_MODULE
参数2:设备名称
返回一个设备类,用于设备节点文件的创建
返回一个class结构体变量
class结构体变量
class是设备驱动模型中通用的设备类结构
在include/linux/device.h中
struct class {
const char *name;
struct module *owner;
struct class_attribute *class_attrs;
struct device_attribute *dev_attrs;
struct bin_attribute *dev_bin_attrs;
struct kobject *dev_kobj;
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
char *(*devnode)(struct device *dev, mode_t *mode);
void (*class_release)(struct class *class);
void (*dev_release)(struct device *dev);
int (*suspend)(struct device *dev, pm_message_t state);
int (*resume)(struct device *dev);
const struct kobj_ns_type_operations *ns_type;
const void *(*namespace)(struct device *dev);
const struct dev_pm_ops *pm;
struct subsys_private *p;
};
#include
#include
/*定义module_param module_param_array的头文件*/
#include
/*定义module_param module_param_array中的参数perm的头文件*/
#include
/*三个字符设备注册函数*/
#include
/*宏定义MKDEV的头文件,MKDEV转换设备号数据类型*/
#include
/*定义字符设备的结构体*/
#include
/*分配内存空间函数头文件*/
#include
/*包含结构体class以及相关函数的头文件*/
#include
#define DEVICE_NEME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GYY");
int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;
/*输入主设备号*/
module_param(numdev_major,int,S_IRUSR);
/*输入次设备号*/
module_param(numdev_minor,int,S_IRUSR);
static struct class *myclass ;
struct reg_dev
{
char *data;
unsigned long size;
struct cdev cdev;
};
struct reg_dev *my_devices;
struct file_operations my_fops = {
.owner = THIS_MODULE
};
/*设备注册到系统*/
static void reg_init_cdev(struct reg_dev *dev,int index)
{
int err;
int devnum = MKDEV(numdev_major,numdev_minor+index);
/*数据初始化*/
cdev_init(&dev->cdev,&my_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_fops;
/*注册到系统*/
err = cdev_add(&dev->cdev,devnum,1);
/*打印信息*/
if(err)
{
printk(KERN_EMERG "cdev_add is failed ,err : %d ,index : %d \n",err,index);
}
else
{
printk(KERN_EMERG "cdev_add is success ,minor : %d \n",numdev_minor+index);
}
}
static int chardev_init(void)
{
int ret,i;
dev_t num_dev;
printk(KERN_EMERG "chardev_init enter!\n");
printk(KERN_EMERG "numdev_major is %d \n",numdev_major);
printk(KERN_EMERG "numdev_minor is %d \n",numdev_minor);
/*如果传入参数的话静态分配设备号*/
if(numdev_major)
{
num_dev = MKDEV(numdev_major,numdev_minor);//转换为内核的设备号格式(高12位为主设备号,低20位为次设备号)
/*静态分配设备号*/
ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NEME);
}
/*不传入参数的话动态分配设备号*/
else
{
/*动态分配设备号*/
ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NEME);
numdev_major = MAJOR(num_dev);//使用MAJOR宏定义获取主设备号
printk(KERN_EMERG "alloc_chrdev_region req %d\n",numdev_major);//打印主设备号
}
/*如果分配设备号失败*/
if(ret<0)
{
printk(KERN_EMERG "alloc_chrdev_region req %d is failed",numdev_major);
return 0;
}
/**/
myclass = class_create(THIS_MODULE,DEVICE_NEME);
/*给my_devices结构体分配内存*/
my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev),GFP_KERNEL);
/*如果内存分配失败,返回错误*/
if(!my_devices)
{
ret = -ENOMEM;
goto fail;
}
/*清空分配到的内存空间的数据*/
memset(my_devices,0,DEVICE_MINOR_NUM*sizeof(struct reg_dev));
/*设备初始化,一个一个初始化*/
for(i=0;i<DEVICE_MINOR_NUM;i++)
{
/*分配内存空间*/
my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
/*清空分配到的内存空间的数据*/
memset(my_devices[i].data,0,REGDEV_SIZE);
/*设备注册到系统*/
reg_init_cdev(&my_devices[i],i);
/*生成设备节点*/
device_create(myclass,NULL, MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NEME"%d",i);
}
/*打印初始化函数信息*/
printk(KERN_EMERG "ascdev_init\n");
return 0;
fail:
/*注销设备号*/
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
printk(KERN_EMERG "kmalloc is failed!\n");
return ret;
}
static void chardev_exit(void)
{
int i;
printk(KERN_EMERG "chardev World exit!\n");
/*卸载字符设备(一个一个卸载)*/
for(i=0;i<DEVICE_MINOR_NUM;i++)
{
/*卸载字符设备*/
cdev_del(&my_devices[i].cdev);
/*摧毁设备节点*/
device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
}
/*释放内存*/
kfree(my_devices);
/*释放设备class*/
class_destroy(myclass);
/*注销设备号*/
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
}
module_init(chardev_init);
module_exit(chardev_exit);
这个代码是在前面注册设备的代码的基础上修改的,修改的地方并不多,只是添加了类的注册与释放以及在初始化时生成设备节点,在最后卸载模块的时候摧毁设备节点
在使用循环对设备进行初始化时,每注册一个设备就生成一个一个设备节点
/*生成设备节点*/
device_create(myclass,NULL, MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NEME"%d",i);
myclass是我们先前注册的字符设备类
父设备写NULL
dev_t设备号我们使用MKDEV宏定义来生成(高12为主设备号,低20位为次设备号)
数据写NULL
设备节点名称我们使用DEVICE_NEME加数字后缀,生成的应当是chardevnode0、chardevnode1…
在我们卸载模块的时候我们释放了申请的内存空间、释放了设备类、摧毁了生成的设备节点
首先我们查看一下设备类和设备节点
加载模块
查看设备类
生成了chardevnode设备类
查看设备节点
生成了我们想要的两个设备节点
卸载模块
编写字符驱动与我们前面所做的编写杂项设备驱动的工作相同,就是给文件操作系统调用open、close、ioctl实现底层的操作,也就是实现file_operations中的部分函数
如果需要不同的设备节点有不同的功能,只需要在注册设备的时候添
加不同的file_operations结构体即可
驱动代码
#include
#include
/*定义module_param module_param_array的头文件*/
#include
/*定义module_param module_param_array中的参数perm的头文件*/
#include
/*三个字符设备注册函数*/
#include
/*宏定义MKDEV的头文件,MKDEV转换设备号数据类型*/
#include
/*定义字符设备的结构体*/
#include
/*分配内存空间函数头文件*/
#include
/*包含结构体class以及相关函数的头文件*/
#include
#define DEVICE_NEME "chardevnode"
#define DEVICE_MINOR_NUM 2
#define DEV_MAJOR 0
#define DEV_MINOR 0
#define REGDEV_SIZE 3000
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("GYY");
int numdev_major = DEV_MAJOR;
int numdev_minor = DEV_MINOR;
/*输入主设备号*/
module_param(numdev_major,int,S_IRUSR);
/*输入次设备号*/
module_param(numdev_minor,int,S_IRUSR);
static struct class *myclass ;
struct reg_dev
{
char *data;
unsigned long size;
struct cdev cdev;
};
struct reg_dev *my_devices;
/*file_operations中的函数*/
/*打开操作*/
static int chardevnode_open (struct inode * pinode, struct file *pfile)
{
printk(KERN_EMERG "chardevnode_open is success! \n");
return 0;
}
/*关闭操作*/
static int chardevnode_release (struct inode *pinode, struct file *pfile)
{
printk(KERN_EMERG "chardevnode_release is success! \n");
return 0;
}
/*IO操作*/
static long chardevnode_ioctl (struct file *pfile, unsigned int cmd, unsigned long arg)
{
printk(KERN_EMERG "chardevnode_ioctl is success! cmd is %d ,arg is %d \n",cmd,arg);
return 0;
}
/*读操作*/
static ssize_t chardevnode_read (struct file *pfile, char __user *buf, size_t count, loff_t *ops)
{
return 0;
}
/*写操作*/
static ssize_t chardevnode_write (struct file *pfile, const char __user *buf, size_t count, loff_t *ops)
{
return 0;
}
/*定位操作*/
static loff_t chardevnode_llseek (struct file *pfile, loff_t offset, int n)
{
return 0;
}
struct file_operations my_fops = {
.owner = THIS_MODULE,
.open = chardevnode_open,
.release = chardevnode_release,
.unlocked_ioctl = chardevnode_ioctl,
.read = chardevnode_read,
.write = chardevnode_write,
.llseek = chardevnode_llseek
};
/*设备注册到系统*/
static void reg_init_cdev(struct reg_dev *dev,int index)
{
int err;
int devnum = MKDEV(numdev_major,numdev_minor+index);
/*数据初始化*/
cdev_init(&dev->cdev,&my_fops);
dev->cdev.owner = THIS_MODULE;
dev->cdev.ops = &my_fops;
/*注册到系统*/
err = cdev_add(&dev->cdev,devnum,1);
/*打印信息*/
if(err)
{
printk(KERN_EMERG "cdev_add is failed ,err : %d ,index : %d \n",err,index);
}
else
{
printk(KERN_EMERG "cdev_add is success ,minor : %d \n",numdev_minor+index);
}
}
static int chardev_init(void)
{
int ret,i;
dev_t num_dev;
printk(KERN_EMERG "chardev_init enter!\n");
printk(KERN_EMERG "numdev_major is %d \n",numdev_major);
printk(KERN_EMERG "numdev_minor is %d \n",numdev_minor);
/*如果传入参数的话静态分配设备号*/
if(numdev_major)
{
num_dev = MKDEV(numdev_major,numdev_minor);//转换为内核的设备号格式(高12位为主设备号,低20位为次设备号)
/*静态分配设备号*/
ret = register_chrdev_region(num_dev,DEVICE_MINOR_NUM,DEVICE_NEME);
}
/*不传入参数的话动态分配设备号*/
else
{
/*动态分配设备号*/
ret = alloc_chrdev_region(&num_dev,numdev_minor,DEVICE_MINOR_NUM,DEVICE_NEME);
numdev_major = MAJOR(num_dev);//使用MAJOR宏定义获取主设备号
printk(KERN_EMERG "alloc_chrdev_region req %d\n",numdev_major);//打印主设备号
}
/*如果分配设备号失败*/
if(ret<0)
{
printk(KERN_EMERG "alloc_chrdev_region req %d is failed",numdev_major);
return 0;
}
/**/
myclass = class_create(THIS_MODULE,DEVICE_NEME);
/*给my_devices结构体分配内存*/
my_devices = kmalloc(DEVICE_MINOR_NUM*sizeof(struct reg_dev),GFP_KERNEL);
/*如果内存分配失败,返回错误*/
if(!my_devices)
{
ret = -ENOMEM;
goto fail;
}
/*清空分配到的内存空间的数据*/
memset(my_devices,0,DEVICE_MINOR_NUM*sizeof(struct reg_dev));
/*设备初始化,一个一个初始化*/
for(i=0;i<DEVICE_MINOR_NUM;i++)
{
/*分配内存空间*/
my_devices[i].data = kmalloc(REGDEV_SIZE,GFP_KERNEL);
/*清空分配到的内存空间的数据*/
memset(my_devices[i].data,0,REGDEV_SIZE);
/*设备注册到系统*/
reg_init_cdev(&my_devices[i],i);
/*生成设备节点*/
device_create(myclass,NULL, MKDEV(numdev_major,numdev_minor+i),NULL,DEVICE_NEME"%d",i);
}
/*打印初始化函数信息*/
printk(KERN_EMERG "ascdev_init\n");
return 0;
fail:
/*注销设备号*/
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
printk(KERN_EMERG "kmalloc is failed!\n");
return ret;
}
static void chardev_exit(void)
{
int i;
printk(KERN_EMERG "chardev World exit!\n");
/*卸载字符设备(一个一个卸载)*/
for(i=0;i<DEVICE_MINOR_NUM;i++)
{
/*卸载字符设备*/
cdev_del(&my_devices[i].cdev);
/*摧毁设备节点*/
device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
//device_destroy(myclass,MKDEV(numdev_major,numdev_minor+i));
}
/*释放内存*/
kfree(my_devices);
/*释放设备class*/
class_destroy(myclass);
/*注销设备号*/
unregister_chrdev_region(MKDEV(numdev_major,numdev_minor),DEVICE_MINOR_NUM);
}
module_init(chardev_init);
module_exit(chardev_exit);
应用程序代码
#include
#include
#include
#include
#include
#include
int main()
{
int fd;
char *hello_node0 = "/dev/chardevnode0";
char *hello_node1 = "/dev/chardevnode1";
/*O_RDWR只读打开,O_NDELAY非阻塞方式打开*/
if((fd = open(hello_node0,O_RDWR|O_NDELAY)) < 0)
{
printf("APP open %s failed\n",hello_node0);
}
else
{
printf("APP open %s success\n",hello_node0);
ioctl(fd,1,6);
}
close(fd);
/*O_RDWR只读打开,O_NDELAY非阻塞方式打开*/
if((fd = open(hello_node1,O_RDWR|O_NDELAY)) < 0)
{
printf("APP open %s failed\n",hello_node1);
}
else
{
printf("APP open %s success\n",hello_node1);
ioctl(fd,1,6);
}
close(fd);
}