lddbus.c:这里包括了设备模型包的一些定义。
static int __init ldd_bus_init(void) { int ret;
ret = bus_register(&ldd_bus_type); //注册总线,在/sys/bus/下会创建文件夹,其名字为ldd_bus_type.name,即ldd
if (ret) return ret; if (bus_create_file(&ldd_bus_type, &bus_attr_version)) //在sys/bus/ldd/下创建文件version,它用来显示版本号
printk(KERN_NOTICE "Unable to create version attribute\n"); ret = device_register(&ldd_bus); //注册设备
if (ret) printk(KERN_NOTICE "Unable to register ldd0\n"); return ret; }
|
总线
ldd_bus_type定义:
/* * Respond to hotplug events. */ static int ldd_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size) { envp[0] = buffer; if (snprintf(buffer, buffer_size, "LDDBUS_VERSION=%s", Version) >= buffer_size) return -ENOMEM; envp[1] = NULL; return 0; }
/* * Match LDD devices to drivers. Just do a simple name test. */ static int ldd_match(struct device *dev, struct device_driver *driver) { return !strncmp(dev->bus_id, driver->name, strlen(driver->name)); //这里比较的长度为什么是driver->name的长度,而不是dev->bus_id的长度,是因为,一般一个驱动可以驱动多个设备,而一个设备只能有一个驱动,比如这里的驱动名为sculld,而设备有sculld0,sculld1等。 }
/* * And the bus type. */ struct bus_type ldd_bus_type = { .name = "ldd", .match = ldd_match, .hotplug = ldd_hotplug, };
|
总线的一些属性bus_attr_version定义:
/* * Export a simple attribute. */ static ssize_t show_bus_version(struct bus_type *bus, char *buf) { return snprintf(buf, PAGE_SIZE, "%s\n", Version); }
static BUS_ATTR(version, S_IRUGO, show_bus_version, NULL);
|
设备
ldd_bus定义:
/* * The LDD bus device. */ static void ldd_bus_release(struct device *dev) { printk(KERN_DEBUG "lddbus release\n"); } struct device ldd_bus = { .bus_id = "ldd0", .release = ldd_bus_release };
|
现在我们要利用上面的模型包了,分析驱动sculld驱动
int sculld_init(void) { int result, i; dev_t dev = MKDEV(sculld_major, 0); /* * Register your major, and accept a dynamic number. */ if (sculld_major) result = register_chrdev_region(dev, sculld_devs, "sculld"); else { result = alloc_chrdev_region(&dev, 0, sculld_devs, "sculld"); sculld_major = MAJOR(dev); } if (result < 0) return result;
/* * Register with the driver core. */ register_ldd_driver(&sculld_driver); /* * allocate the devices -- we can't have them static, as the number * can be specified at load time */ sculld_devices = kmalloc(sculld_devs*sizeof (struct sculld_dev), GFP_KERNEL); if (!sculld_devices) { result = -ENOMEM; goto fail_malloc; } memset(sculld_devices, 0, sculld_devs*sizeof (struct sculld_dev)); for (i = 0; i < sculld_devs; i++) { sculld_devices[i].order = sculld_order; sculld_devices[i].qset = sculld_qset; sema_init (&sculld_devices[i].sem, 1); sculld_setup_cdev(sculld_devices + i, i); sculld_register_dev(sculld_devices + i, i); }
#ifdef SCULLD_USE_PROC /* only when available */ create_proc_read_entry("sculldmem", 0, NULL, sculld_read_procmem, NULL); #endif return 0; /* succeed */
fail_malloc: unregister_chrdev_region(dev, sculld_devs); return result; }
|
sculld_driver定义:
/* * The LDD driver type. */ struct ldd_driver { char *version; struct module *module; struct device_driver driver; struct driver_attribute version_attr; };
/* Device model stuff */
static struct ldd_driver sculld_driver = { .version = "$Revision: 1.21 $", .module = THIS_MODULE, .driver = { .name = "sculld", }, };
|
register_ldd_driver(&sculld_driver)注册驱动,看一下它的定义:
int register_ldd_driver(struct ldd_driver *driver) { int ret; driver->driver.bus = &ldd_bus_type; //驱动的总线是ldd_bus_type ret = driver_register(&driver->driver); //向系统注册驱动 if (ret) return ret; driver->version_attr.attr.name = "version"; //属性文件名 driver->version_attr.attr.owner = driver->module; //属性属主 driver->version_attr.attr.mode = S_IRUGO; //属性文件的读写权限 driver->version_attr.show = show_version; //属性文件读 driver->version_attr.store = NULL; return driver_create_file(&driver->driver, &driver->version_attr); //在这个设备的文件夹下,创建一个属性文件,由driver->version_attr描述 }
|
从上面的分析可知,这个函数功能:1,确定使用的总线;2,注册驱动,3,创建属性文件,方便用户查询一些信息。
现在继续往下分析:sculld_setup_cdev(sculld_devices + i, i);
static void sculld_setup_cdev(struct sculld_dev *dev, int index) { int err, devno = MKDEV(sculld_major, index); cdev_init(&dev->cdev, &sculld_fops); //初始化字符设备,这个字符设备的操作方法就是sculld_fops,比如应用层open打开这个文件时就会调用这个结构体的open函数。 dev->cdev.owner = THIS_MODULE; dev->cdev.ops = &sculld_fops; err = cdev_add (&dev->cdev, devno, 1); //字符设备和设备的设备号相关,并向系统注册这个设备。至于设备名无所谓的,对于内核而言它认的是设备号,所以在创建设备节点的时候,可以任意取名,但是设备号必须一致。 /* Fail gracefully if need be */ if (err) printk(KERN_NOTICE "Error %d adding scull%d", err, index); }
|
通过上面的分析,我们就明白了其实它就是像系统注册一个字符设备。
接着分析:sculld_register_dev(sculld_devices + i, i);
struct ldd_device { char *name; struct ldd_driver *driver; struct device dev; };
struct sculld_dev { void **data; struct sculld_dev *next; /* next listitem */ int vmas; /* active mappings */ int order; /* the current allocation order */ int qset; /* the current array size */ size_t size; /* 32-bit will suffice */ struct semaphore sem; /* Mutual exclusion */ struct cdev cdev; char devname[20]; struct ldd_device ldev; };
static ssize_t sculld_show_dev(struct device *ddev, char *buf) { struct sculld_dev *dev = ddev->driver_data;
return print_dev_t(buf, dev->cdev.dev); }
static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
static void sculld_register_dev(struct sculld_dev *dev, int index) { sprintf(dev->devname, "sculld%d", index); dev->ldev.name = dev->devname; dev->ldev.driver = &sculld_driver; dev->ldev.dev.driver_data = dev; register_ldd_device(&dev->ldev); device_create_file(&dev->ldev.dev, &dev_attr_dev); }
|
可以看到上面这个函数前面部分主要是对dev->ldev成员赋值。然后通过调用register_ldd_device(&dev->ldev);进行设备注册,这是我猜的,但答案应该是对的,看源代码:
int register_ldd_device(struct ldd_device *ldddev) { ldddev->dev.bus = &ldd_bus_type; //设备的总线 ldddev->dev.parent = &ldd_bus; //父设备是ldd_bus ldddev->dev.release = ldd_dev_release; strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE); //设备名赋值 return device_register(&ldddev->dev); //向系统注册这个设备 }
|
设备的注册,必须是struct device,所以上面我们可以看到,对设备的ldd_device里面的dev成员进行了一些初始化。注意,这里使用的总线也是ldd_bus_type。其实,device_register函数向系统注册设备,它会查询挂在这个设备的总线上有哪些驱动device_driver,通过回调总线驱动的match函数进行匹配,一般在match函数中会比较device和device_driver的名字是否相同,如果相同,那么就说明匹配。也就是说设备和驱动挂接上了,这时会回调总线驱动的probe函数,最终会device_driver的probe函数,进行驱动加载。 (由于ldd3驱动使用的内核比较久了,所以,上面说的有点出入,但整体可以这么理解)
device_create_file(&dev->ldev.dev, &dev_attr_dev);在设备文件夹下,创建一个设备属性文件。dev_attr_dev定义,在这个设备属性文件中显示了设备的设备号,udev就可以通过它来创建设备节点。:
static ssize_t sculld_show_dev(struct device *ddev, char *buf) { struct sculld_dev *dev = ddev->driver_data;
return print_dev_t(buf, dev->cdev.dev); }
static DEVICE_ATTR(dev, S_IRUGO, sculld_show_dev, NULL);
|
设备模型,这里可以发现一个是通过总线串起设备和设备驱动,设备里面又包含了子父设备的关系。在sys文件系统就构成了相应的目录树结构。
阅读(2018) | 评论(0) | 转发(0) |