static int __init chardev_init(void)
{
//int value = 0;
//value = register_chrdev(200,"chard",&chardev_fops);
//分配
if(newchar_dev.major){
newchar_dev.devid = MKDEV(newchar_dev.major, 0);
register_chrdev_region(newchar_dev.devid,NEWCHARDEV_CNT,NEWCHARDEV_NAME);
}else{
alloc_chrdev_region(&newchar_dev.devid,0, NEWCHARDEV_CNT,NEWCHARDEV_NAME);
newchar_dev.major = MAJOR(newchar_dev.devid);
newchar_dev.minor = MINOR(newchar_dev.devid);
}
printk("--%s--%d,newchadev.major %d\n",__func__,__LINE__,newchar_dev.major);
//初始化 注册cdev
newchar_dev.cdev.owner = THIS_MODULE;
cdev_init(&newchar_dev.cdev, &chardev_fops);
cdev_add(&newchar_dev.cdev,newchar_dev.devid,NEWCHARDEV_CNT);
//创建类
newchar_dev.class = class_create(THIS_MODULE, NEWCHARDEV_NAME);
newchar_dev.device = device_create(newchar_dev.class,NULL, newchar_dev.devid,NULL,NEWCHARDEV_NAME);
printk("--%s--%d,init sucess \n",__func__,__LINE__);
return 0;
}
alloc_chrdev_region()实质上申请了初始化了char_device_struct的结构体。
static struct char_device_struct {
struct char_device_struct *next;
unsigned int major;
unsigned int baseminor;
int minorct;
char name[64];
struct cdev *cdev; /* will die */
} *chrdevs[CHRDEV_MAJOR_HASH_SIZE];
在字符设备驱动开发的入门教程中,最常见的就是用device_create()函数来创建设备节点了,但是在之后阅读内核源码的过程中却很少见device_create()的踪影了,取而代之的是device_register()与device_add(),将device_create()函数展开不难发现:其实device_create()只是device_register()的封装,而device_register()则是device_add()的封装。
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
......
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
......
return dev;
}
struct device *device_create_vargs(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt,
va_list args)
{
......
dev->devt = devt;
dev->class = class;
dev->parent = parent;
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata);
......
retval = device_register(dev);
......
}
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
device_add()会在/sys目录对应设备目录下创建uevent属性节点,应用层的udev则会根据uevent来创建/dev目录下的设备节点,这里关于udev的部分不再赘述,我们继续分析device_create()、device_register()、device_add()三个函数在实际运用中的区别。
以一个简单的led设备字符设备驱动为例,下面分别用device_create()、device_register()、device_add()三个函数来创建设备节点“/dev/led”:
class_create() 和 device_create()有什么关系呢??
在刚开始写Linux设备驱动程序的时候,很多时候都是利用mknod命令手动创建设备节点,实际上Linux内核为我们提供了一组函数,可以用来在模块加载的时候自动在/dev目录下创建相应设备节点,并在卸载模块时删除该节点,当然前提条件是用户空间移植了udev。什么事udev??请查看“ 收集 3个介绍uedv 网址资料 ”
内核中定义了struct class结构体,顾名思义,一个struct class结构体类型变量对应一个类,内核同时提供了class_create(…)函数,可以用它来创建一个类,这个类存放于sysfs下面,一旦创建好了这个类,再调用device_create(…)函数来在/dev目录下创建相应的设备节点。这样,加载模块的时候,用户空间中的udev会自动响应device_create(…)函数,去/sysfs下寻找对应的类从而创建设备节点。
参考链接:https://blog.csdn.net/zifehng/article/details/73844845