- 论坛徽章:
- 0
|
driver_register
platform_add_devices
misc_registerlinux驱动程序常用的几个设备注册函数,详细解析待续.driver_register:
int driver_register(struct device_driver * drv){ if ((drv->bus->probe && drv->probe) ||//drv和drv所属的bus之中只要1个提供该函数即可,否则也只能调用bus的函数,而不理会drv的 (drv->bus->remove && drv->remove) || (drv->bus->shutdown && drv->shutdown)) { printk(KERN_WARNING "Driver '%s' needs updating - please use bus_type methods\n", drv->name); } klist_init(&drv->klist_devices, NULL, NULL);//将drv驱动上的设备链表清空 return bus_add_driver(drv);//将本drv驱动注册登记到drv->bus所在的总线上}
void klist_init(struct klist * k, void (*get)(struct klist_node *), void (*put)(struct klist_node *)){ INIT_LIST_HEAD(&k->k_list);//链表初始化 spin_lock_init(&k->k_lock);//锁初始化 k->get = get;//引用计数操作自定义函数 k->put = put;}
int bus_add_driver(struct device_driver *drv){ struct bus_type * bus = get_bus(drv->bus); int error = 0; if (!bus) return -EINVAL; pr_debug("bus %s: add driver %s\n", bus->name, drv->name); //kboj->name[KOBJ_NAME_LEN],如果KOBJ_NAME_LEN长度不够,会调用kmalloc申请 //之后kobj->k_name指针或者指向kboj->name或者指向kmalloc返回地址 error = kobject_set_name(&drv->kobj, "%s", drv->name); if (error) goto out_put_bus; //bus->drivers为kset集合类型,也正是管理本drv->kobj的kset集合 drv->kobj.kset = &bus->drivers; //gliethttp_20071025 kobject_register()简单理解 //把drv的kobj大张旗鼓的登记到管理它的kset集合上去,同时再根据层级关系创建相应的目录文件 //gliethttp_20071026 //注册登记该kobj,如果该kobj属于某个kset,那么将自己的entry节点挂接到该kset的list链表上, //以示自己需要该kset的滋润,同时kobj->parent=&kset->kobj,parent指向kset用来管理自己的kobj //如果该kobj不属于kset,而属于parent,那么简单的将parent的引用计数加1 //对于kobj属于某个kset的情况,可以实现kset向下查找kobj,也可以实现kobj向上查找kset //对于kobj属于某个parent的情况,查找只能是单向的,只能kobj找到parent,parent不能查找 //该parent挂接的kobj们 //parent是用来明显建立亲子关系图的标志性变量,当然在kset也能若隐若现的显露出这种关系, //但总不如parent正宗和高效 //之后调用create_dir()创建该kobj在sysfs中的目录文件 //最后调用kobject_uevent()将KOBJ_ADD事件通知到用户空间的守护进程 error = kobject_register(&drv->kobj); if (error) goto out_put_bus; if (drv->bus->drivers_autoprobe) { //gliethttp_20071025 //driver提供自动匹配函数,那么现在就遍历所有设备 //尝试将本driver匹配到相应设备上去 error = driver_attach(drv); if (error) goto out_unregister; } //将本driver链接到bus总线上的klist_drivers的klist链表结尾处 klist_add_tail(&drv->knode_bus, &bus->klist_drivers); module_add_driver(drv->owner, drv);//gliethttp_20071026//所以一个驱动需要维持住1个klist链条和一个kobj层次结构--驱动drv->kobj对象,内核一方面使用该kobj在sysfs中建立//统一的与该kobj对应的目录对象供用户空间访问,另一方面使用该kobj的引用计数来获悉该kobj设备的繁忙与空闲情况,//当本kobj对象的引用计数到达0时,只要其他条件允许,那么说明集成本kobj的结构体对象不再使用,内核得知这个情况很重要,//因为这对内核进行进一步的决策提供了详细的证据资料,进而对物理设备进行细致的电源管理成了可能,//如:当hub1上的所有端口设备都被拔掉之后,hub1就可以安全的进入省电模式了,而这个功能在2.4内核中是找不到的.//如果从面向对象的角度来看待kset、kobj和driver的话,并不能清晰的说明问题,因为面向对象本身提供的封装、继承和多态//并不能很好的说明kset、kobj和driver之间存在的实际关系,多少都有一些出入,因为linux毕竟不是用c++写的,不像eCos那样,//虽然大家都努力借鉴面向对象的光辉思想来设计自己的程序,但是面向对象固有的若干弊端因素也是我们必须要剔除的,//所以剥丝抽茧之后,呈现出来的东西,大多将处于中间态,当然不排除走极端的少数,所以我觉得使用单纯的面向对象思想来理解//kset、kobj和driver这3者,最终都会带来理解上的麻烦,因为渗透在他们3者之间的设计思想与封装、继承、多态、类、//虚函数、属性、方法、类属性和类方法等只是相似,而且"仅仅相似,有些影子,但并不是!"//因此对于正常理解,最好抛弃单纯的面向对象方式(开始我就使用单纯的面向对象方式来理解,结果在若干单元上卡了壳儿,//现在想来很是可笑,因为那些单元根本就不是面向对象所具有的,硬是要用面向对象来解决,那完全是在牵强)//而是采用面向对象和c数据结构相结合的方式,//而且在结合过程中,为了减少麻烦,最好偏向c数据结构多一点(gliethttp_20071026小感). //创建属性目录文件 error = driver_add_attrs(bus, drv); if (error) { /* How the hell do we get out of this pickle? Give up */ printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", __FUNCTION__, drv->name); } error = add_bind_files(drv); if (error) { /* Ditto */ printk(KERN_ERR "%s: add_bind_files(%s) failed\n", __FUNCTION__, drv->name); } return error;out_unregister: kobject_unregister(&drv->kobj);out_put_bus: put_bus(bus); return error;}
platform_add_devices:
/**
* platform_add_devices - add a numbers of platform devices
* @devs: array of platform devices to add
* @num: number of platform devices in array
*/
int platform_add_devices(struct platform_device **devs, int num)
{
int i, ret = 0;
for (i = 0; i ret = platform_device_register(devs);
if (ret) {
while (--i >= 0)
platform_device_unregister(devs);
break;
}
}
return ret;
}/**
* platform_device_register - add a platform-level device
* @pdev: platform device we're adding
*
*/
int platform_device_register(struct platform_device * pdev)
{
int i, ret = 0;
if (!pdev)
return -EINVAL;
if (!pdev->dev.parent)
pdev->dev.parent = &platform_bus;
pdev->dev.bus = &platform_bus_type;
if (pdev->id != -1)
snprintf(pdev->dev.bus_id, BUS_ID_SIZE, "%s.%u", pdev->name, pdev->id);
else
strlcpy(pdev->dev.bus_id, pdev->name, BUS_ID_SIZE);
for (i = 0; i num_resources; i++) {
struct resource *p, *r = &pdev->resource;
if (r->name == NULL)
r->name = pdev->dev.bus_id;
p = r->parent;
if (!p) {
if (r->flags & IORESOURCE_MEM)
p = &iomem_resource;
else if (r->flags & IORESOURCE_IO)
p = &ioport_resource;
}
if (p && request_resource(p, r)) {
printk(KERN_ERR
"%s: failed to claim resource %d\n",
pdev->dev.bus_id, i);
ret = -EBUSY;
goto failed;
}
}
pr_debug("Registering platform device '%s'. Parent at %s\n",
pdev->dev.bus_id, pdev->dev.parent->bus_id);
ret = device_register(&pdev->dev);
if (ret == 0)
return ret;
failed:
while (--i >= 0)
if (pdev->resource.flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(&pdev->resource);
return ret;
}
/**
* platform_device_unregister - remove a platform-level device
* @pdev: platform device we're removing
*
* Note that this function will also release all memory- and port-based
* resources owned by the device (@dev->resource).
*/
void platform_device_unregister(struct platform_device * pdev)
{
int i;
if (pdev) {
for (i = 0; i num_resources; i++) {
struct resource *r = &pdev->resource;
if (r->flags & (IORESOURCE_MEM|IORESOURCE_IO))
release_resource(r);
}
device_unregister(&pdev->dev);
}
}
misc_register:
/**
* misc_register - register a miscellaneous device
* @misc: device structure
*
* Register a miscellaneous device with the kernel. If the minor
* number is set to %MISC_DYNAMIC_MINOR a minor number is assigned
* and placed in the minor field of the structure. For other cases
* the minor number requested is used.
*
* The structure passed is linked into the kernel and may not be
* destroyed until it has been unregistered.
*
* A zero is returned on success and a negative errno code for
* failure.
*/
int misc_register(struct miscdevice * misc)
{
static devfs_handle_t devfs_handle, dir;
struct miscdevice *c;
if (misc->next || misc->prev)
return -EBUSY;
down(&misc_sem);
c = misc_list.next;
while ((c != &misc_list) && (c->minor != misc->minor))
c = c->next;
if (c != &misc_list) {
up(&misc_sem);
return -EBUSY;
}
if (misc->minor == MISC_DYNAMIC_MINOR) {
int i = DYNAMIC_MINORS;
while (--i >= 0)
if ( (misc_minors[i>>3] & (1 7))) == 0)
break;
if (i0)
{
up(&misc_sem);
return -EBUSY;
}
misc->minor = i;
}
if (misc->minor misc_minors[misc->minor >> 3] |= 1 minor & 7);
if (!devfs_handle)
devfs_handle = devfs_mk_dir (NULL, "misc", NULL);
dir = strchr (misc->name, '/') ? NULL : devfs_handle;
misc->devfs_handle =
devfs_register (dir, misc->name, DEVFS_FL_NONE,
MISC_MAJOR, misc->minor,
S_IFCHR | S_IRUSR | S_IWUSR | S_IRGRP,
misc->fops, NULL);
/*
* Add it to the front, so that later devices can "override"
* earlier defaults
*/
misc->prev = &misc_list;
misc->next = misc_list.next;
misc->prev->next = misc;
misc->next->prev = misc;
up(&misc_sem);
return 0;
}/**
* misc_deregister - unregister a miscellaneous device
* @misc: device to unregister
*
* Unregister a miscellaneous device that was previously
* successfully registered with misc_register(). Success
* is indicated by a zero return, a negative errno code
* indicates an error.
*/
int misc_deregister(struct miscdevice * misc)
{
int i = misc->minor;
if (!misc->next || !misc->prev)
return -EINVAL;
down(&misc_sem);
misc->prev->next = misc->next;
misc->next->prev = misc->prev;
misc->next = NULL;
misc->prev = NULL;
devfs_unregister (misc->devfs_handle);
if (i 0) {
misc_minors[i>>3] &= ~(1 minor & 7));
}
up(&misc_sem);
return 0;
}
/**
* devfs_register - Register a device entry.
* @dir: The handle to the parent devfs directory entry. If this is %NULL the
* new name is relative to the root of the devfs.
* @name: The name of the entry.
* @flags: A set of bitwise-ORed flags (DEVFS_FL_*).
* @major: The major number. Not needed for regular files.
* @minor: The minor number. Not needed for regular files.
* @mode: The default file mode.
* @ops: The &file_operations or &block_device_operations structure.
* This must not be externally deallocated.
* @info: An arbitrary pointer which will be written to the @private_data
* field of the &file structure passed to the device driver. You can set
* this to whatever you like, and change it once the file is opened (the next
* file opened will not see this change).
*
* Returns a handle which may later be used in a call to devfs_unregister().
* On failure %NULL is returned.
*/
devfs_handle_t devfs_register (devfs_handle_t dir, const char *name,
unsigned int flags,
unsigned int major, unsigned int minor,
umode_t mode, void *ops, void *info)
{
char devtype = S_ISCHR (mode) ? DEVFS_SPECIAL_CHR : DEVFS_SPECIAL_BLK;
int err;
kdev_t devnum = NODEV;
struct devfs_entry *de;
if (name == NULL)
{
PRINTK ("(): NULL name pointer\n");
return NULL;
}
if (ops == NULL)
{
if ( S_ISBLK (mode) ) ops = (void *) get_blkfops (major);
if (ops == NULL)
{
PRINTK ("(%s): NULL ops pointer\n", name);
return NULL;
}
PRINTK ("(%s): NULL ops, got %p from major table\n", name, ops);
}
if ( S_ISDIR (mode) )
{
PRINTK ("(%s): creating directories is not allowed\n", name);
return NULL;
}
if ( S_ISLNK (mode) )
{
PRINTK ("(%s): creating symlinks is not allowed\n", name);
return NULL;
}
if ( ( S_ISCHR (mode) || S_ISBLK (mode) ) &&
(flags & DEVFS_FL_AUTO_DEVNUM) )
{
if ( kdev_none ( devnum = devfs_alloc_devnum (devtype) ) )
{
PRINTK ("(%s): exhausted %s device numbers\n",
name, S_ISCHR (mode) ? "char" : "block");
return NULL;
}
major = major (devnum);
minor = minor (devnum);
}
if ( ( de = _devfs_prepare_leaf (&dir, name, mode) ) == NULL )
{
PRINTK ("(%s): could not prepare leaf\n", name);
if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
return NULL;
}
if ( S_ISCHR (mode) || S_ISBLK (mode) )
{
de->u.fcb.u.device.major = major;
de->u.fcb.u.device.minor = minor;
de->u.fcb.autogen = kdev_none (devnum) ? FALSE : TRUE;
}
else if ( !S_ISREG (mode) )
{
PRINTK ("(%s): illegal mode: %x\n", name, mode);
devfs_put (de);
devfs_put (dir);
return (NULL);
}
de->info = info;
if (flags & DEVFS_FL_CURRENT_OWNER)
{
de->inode.uid = current->uid;
de->inode.gid = current->gid;
}
else
{
de->inode.uid = 0;
de->inode.gid = 0;
}
de->u.fcb.ops = ops;
de->u.fcb.auto_owner = (flags & DEVFS_FL_AUTO_OWNER) ? TRUE : FALSE;
de->u.fcb.aopen_notify = (flags & DEVFS_FL_AOPEN_NOTIFY) ? TRUE : FALSE;
de->hide = (flags & DEVFS_FL_HIDE) ? TRUE : FALSE;
if (flags & DEVFS_FL_REMOVABLE) de->u.fcb.removable = TRUE;
if ( ( err = _devfs_append_entry (dir, de, de->u.fcb.removable, NULL) )
!= 0 )
{
PRINTK ("(%s): could not append to parent, err: %d\n", name, err);
devfs_put (dir);
if ( !kdev_none (devnum) ) devfs_dealloc_devnum (devtype, devnum);
return NULL;
}
DPRINTK (DEBUG_REGISTER, "(%s): de: %p dir: %p \"%s\" pp: %p\n",
name, de, dir, dir->name, dir->parent);
devfsd_notify (de, DEVFSD_NOTIFY_REGISTERED, flags & DEVFS_FL_WAIT);
devfs_put (dir);
return de;
} /* End Function devfs_register */
本文来自ChinaUnix博客,如果查看原文请点:http://blog.chinaunix.net/u1/54524/showart_1982570.html |
|