/dev下面的设备节点一般由两种方式创建的,一者是mknod手动创建,另外就是驱动中创建新设备的时候,会往udev类的用户程序上报一个DEVICE_ADD的事件,udev接收到设备添加事件后就会在/dev下创建设备节点了
##/dev目录生成,/dev目录下的节点生成???
#devt这个是major和minor设备组合出来的值
fmt字符串就是在/dev下面生成的文件
并且在class对应的目录下生成相应的文件并建立symlink
比如在
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt);
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(vargs);
return dev;
}
#################################################################################################
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
MODULE_AUTHOR("Zaiguang Hong");
MODULE_DESCRIPTION("A gentle Hello World module");
MODULE_LICENSE("GPL");
static struct miscdevice test_miscdev={
.minor = 239,
.name = "test_miscdev",
.fops = NULL,
};
static int __init task_init(void)
{
int error;
printk(KERN_DEBUG "Hello world!\n");
error=misc_register(&test_miscdev);
if(error)
pr_info("test_miscdev register failed");
return 0;
}
static void __exit task_cleanup(void)
{
printk(KERN_DEBUG "Goodbye World!\n");
}
module_init(task_init);
module_exit(task_cleanup);
Makefile如下:
obj-m += test.o
KERNEL ?= /lib/modules/$(shell uname -r)/build
all:
make -C $(KERNEL) M=$(PWD) modules
clean:
make -C $(KERNEL) M=$(PWD) clean
像上面这一段程序编译成.ko文件之后,insmod test.ko之后会生成如下文件
misc_register(&test_miscdev);
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
//devices目录下的virtual生成可以参考get_device_parent()->virtual_device_parent
//misc_class = class_create(THIS_MODULE, “misc”);这个会在/sys/class下面生成misc文件夹
下面解释一下misc_register()是怎么一步一步完成上面的过程的
int misc_register(struct miscdevice * misc){
....
int misc_register(struct miscdevice * misc);
//misc->parent == NULL,
dev = MKDEV(MISC_MAJOR, misc->minor); //dev_t dev类型,表示主设备号和minor设备号的数据类型
misc->this_device = device_create(misc_class, misc->parent, dev,
misc, "%s", misc->name);
...
}
struct device *device_create(struct class *class, struct device *parent,
dev_t devt, void *drvdata, const char *fmt, ...)
{
va_list vargs;
struct device *dev;
va_start(vargs, fmt); //这个后续再解释
dev = device_create_vargs(class, parent, devt, drvdata, fmt, vargs);
va_end(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)
{
struct device *dev = NULL;
int retval = -ENODEV;
if (class == NULL || IS_ERR(class))
goto error;
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) {
retval = -ENOMEM;
goto error;
}
dev->devt = devt;//保存设备号
dev->class = class;//保存设备class
dev->parent = parent;//parent
dev->release = device_create_release;
dev_set_drvdata(dev, drvdata); //dev->p->driver_data里边把保存task_miscdev
retval = kobject_set_name_vargs(&dev->kobj, fmt, args);//这里把task_miscdev设备的名字赋值给dev->kobj->name,而不是赋值给dev->name ?? 为什么?
//而且kobj_set_name_vargs()函数里边使用了kvasprintf()函数,需要整理一下
if (retval)
goto error;
retval = device_register(dev);//应该就是这里做了后续所有的事情!!
if (retval)
goto error;
return dev;
error:
put_device(dev);
return ERR_PTR(retval);
}
int device_register(struct device *dev)
{
device_initialize(dev);//初始化dev里边的spinlock等,但里边有比较重要的两个变量赋值
//dev->kobj.kset = devices_kset;
//kobject_init(&dev->kobj, &device_ktype);
return device_add(dev);
}
在进入真正做事的device_add()函数之前,先解释一下device_initialize()函数中
devices_kset和device_ktype
void device_initialize(struct device *dev)
{
dev->kobj.kset = devices_kset;
kobject_init(&dev->kobj, &device_ktype);
.....
}
devices_kset的解释:
/* /sys/devices/ */
struct kset *devices_kset;
devices_kset = kset_create_and_add(“devices”, &device_uevent_ops, NULL);//创建一个kset,kset->kobj->name设置为devices , kset->uevent_ops=device_uevent_ops,
kset->kobj.ktype=&kset_ktype
//kset->uevent_ops赋值为device_uevent_ops,这个就是在每个/sys/devices/目录下面的文件夹中,读取uevent的时候通过show_uevent()函数进行调用的!!!
static struct kobj_type kset_ktype= {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
};
const struct sysfs_ops kobj_sysfs_ops= {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
device_ktype的解释:
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
.namespace = device_namespace,
};
static const struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
kobject_init(&dev->kobj, &device_ktype); //这句只是初始化dev->kobj然后把device_ktype赋值给dev->kobj->ktype,
在读特定文件的时候,可以看到通过fill_read_buffer里边会调用sysfs_ops->show.
也即调用dev_sysfs_ops标量的show函数.dev_sysfs_ops函数里边还会取出来device_attribute来调用show!!所以dev_sysfs_ops只是一个媒介!!
所以这里实现device_attribute的show等函数,就可以显示需要的内容,之后再讲!
下面介绍实际的工作者:device_add()函数!!
int device_add(struct device *dev)
{
dev = get_device(dev);//这个做什么?
if (!dev->p) {
error = device_private_init(dev);
if (error)
goto done;
}
if (dev->init_name) {
dev_set_name(dev, "%s", dev->init_name);
dev->init_name = NULL;
}
/* subsystems can specify simple device enumeration */
if (!dev_name(dev) && dev->bus && dev->bus->dev_name)
dev_set_name(dev, "%s%u", dev->bus->dev_name, dev->id);
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
pr_debug("device: '%s': %s\n", dev_name(dev), __func__);
parent = get_device(dev->parent);
kobj = get_device_parent(dev, parent);
if (kobj)
dev->kobj.parent = kobj;
/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
/* first, register with generic layer. */
/* we require the name to be set before, and pass NULL */
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL); //调用create_dir(kobj)创建kobj名字的文件夹,当然这里都提前设置了parent,所以放在/sys/devices/virtual/misc文件夹下面。
if (error)
goto Error;
/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);
error = device_create_file(dev, &uevent_attr);//在test_miscdevice文件夹下面创建uevent文件,并传入uevent_attr为device_attribute,这样在读取uevent文件的时候,就会调用uevent_attr的show函数
if (error)
goto attrError;
if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr); //在test_miscdev文件夹下面生成dev文件,并传入devt_attr,所以在cat dev的时候就会调用devt_attr里边的show函数
if (error)
goto ueventattrError;
error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;
devtmpfs_create_node(dev);
}
error = device_add_class_symlinks(dev);//做一个symlink到class里边对应的文件夹!!
if (error)
goto SymlinkError;
error = device_add_attrs(dev); //根据class->attr添加attr到device目录下??
if (error)
goto AttrsError;
error = bus_add_device(dev);
if (error)
goto BusError;
error = dpm_sysfs_add(dev);
if (error)
goto DPMError;
device_pm_add(dev);
/* Notify clients of device addition. This call must come * after dpm_sysfs_add() and before kobject_uevent(). */
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
BUS_NOTIFY_ADD_DEVICE, dev);
kobject_uevent(&dev->kobj, KOBJ_ADD);
bus_probe_device(dev);
if (parent)
klist_add_tail(&dev->p->knode_parent,
&parent->p->klist_children);
if (dev->class) {
mutex_lock(&dev->class->p->mutex);
/* tie the class to the device */
klist_add_tail(&dev->knode_class,
&dev->class->p->klist_devices);
/* notify any interfaces that the device is here */
list_for_each_entry(class_intf,
&dev->class->p->interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->mutex);
}
}
static int device_add_attrs(struct device *dev)
{
if (class) {
error = device_add_attributes(dev, class->dev_attrs);
.....
}
if (type) {
error = device_add_groups(dev, type->groups);
.........
}
error = device_add_groups(dev, dev->groups);
return 0;
}
1) 传输的数据大小比较小的时候一般用device_create_file(),使用方法如下:
static DEVICE_ATTR(_name, _mode, _show, _store);
extern int __must_check device_create_file(struct device *device,
const struct device_attribute *entry);
extern void device_remove_file(struct device *dev,
const struct device_attribute *attr);
使用DEVICE_ATTR()生成相关的device_attribute数据结构,然后调用device_create_file()生成sysfs文件,如果需要删除就用device_remove_file()函数删掉相应的sysfs。
_name : device的名字,后面会根据这个名字生成dev_attr_[name]
_mode : 文件读写模式
_show와 _store : 读写相关的函数指针
读写函数一般写法如下:
static ssize_t xxxxx_store(struct device *, struct device_attribute *,
const char *buf, size_t count)
{
u8 data;
scanf(buf, fmt, &data);
return count;
}
static ssize_t xxxxx_show(struct device *, struct device_attribute *,
char *buf)
{
int count;
u8 data;
count += sprintf(buf, fmt, data);
return count;
}
这两个函数都需要返回count,但xxx_store不管是不是用count只要把原来的值返回回去就可以。但xxx_show函数需要在使用count之后,需要返回加上使用的count的值。如果不遵守返回count的规则,内部使用的data buffer会出问题。
1) 传输的数据大小比较大的时候一般用device_create_bin_file,使用方法如下:
static struct bin_attribute xxxx_attr = {
.attr = {
.name = "xxxx",
.mode = S_IRWXUGO,
},
.size = MAX_BUF,
.read = xxx_sysfs_read,
.write = xxx_sysfs_write,
.mmap = xxx_sysfs_mmap,
}
extern int __must_check device_create_bin_file(struct device *dev,
const struct bin_attribute *attr);
extern void device_remove_bin_file(struct device *dev,
const struct bin_attribute *attr);
传输过来的数据会根据MAX_BUF的大小(数据大小/MAX_BUF+1),分成多个部分传给.read, .write, .mmap函数。这个几个函数会根据(数据大小/MAX_BUF+1)的值被调相应的次数。所以在.read, .write, .mmap函数中需要操作buffer的方式不太一样。
static ssize_t xxxx_sysfs_read(struct kobject *, struct bin_attribute *,
char *buf, loff_t off, size_t size)
{
int count;
u8 data;
if (off >= MAX_BUF)
return 0;
if (off + size > PAGE_SIZE)
return 0;
count += sprintf(buf, fmt, data);
return count;
}
static ssize_t xxxx_sysfs_write(struct kobject *, struct bin_attribute *,
char *buf, loff_t off, size_t size)
{
if (off == 0) {
/* set the values, for the first time */
}
return size;
}
static ssize_t xxxx_sysfs_mmap(struct kobject *, struct bin_attribute *,
struct vm_area_struct *vma)
{
/* ? */
}