现在的位置: 首页 > 综合 > 正文

Linux设备驱动模型浅析2– 设备及总线的注册

2014年01月31日 ⁄ 综合 ⁄ 共 10103字 ⁄ 字号 评论关闭

linux 设备驱动体系结构中,Kset和Kobject结构体,都是用来表示 sys下的目录结构的,用来形成完整的sys文件系统结构,仅此而已。在驱动体系中,最重要的三个要素无非是设备, 驱动 ,总线,三者紧密的联系在了一起,分别用device,device_driver,bus_type三个数据结构来形容,在这三个数据结构中,分别嵌套了kobject或者是kset,在我们注册设备,驱动,总线的过程中,通过这些kobject,kset形成了我们可视的sys文件系统,而在device,
device_driver,bus_type中,使用其他的数据元素比如klist,knode,进一步构成了三个数据结构之间的关系。

下面来讲驱动中封装这些结构的容器。

1总线bus
struct bus_type {
     const char        *name;       //总线的名称,这个名字理论上并不是sys/bus/下的那些目录的目录名。那些目录的目录名应该是在下面变量  subsys_private p.sbusys的name变量中。但是往往那个name是由这个name赋值的,所以就一样的。但这里要明白的是(还是上面的老生常谈),表示目录是由Kset.Kobject这个东西来表示的。
     struct bus_attribute    *bus_attrs;  //根据后面的bus_add_attrs函数分析,这些个属性可能是数组
     struct device_attribute    *dev_attrs;
     struct driver_attribute    *drv_attrs;  //bus device driver的属性,一些操作导入导出的属性,等后面再分析
     int (*match)(struct device *dev, struct device_driver *drv);  //插入总线上的设备匹配方法
     int (*uevent)(struct device *dev, struct kobj_uevent_env *env);//发往用户态空间热插拔消息的方法
     int (*probe)(struct device *dev);//对总线上的设备进行探测和初始化的方法
     int (*remove)(struct device *dev);//移除总线上设备的方法
     void (*shutdown)(struct device *dev);//关闭总线上设备的方法
     int (*suspend)(struct device *dev, pm_message_t state);//暂停总线上设备的方法
     int (*resume)(struct device *dev);       //总线上设备的恢复的操作
     const struct dev_pm_ops *pm;           //power manage 的operations
     struct subsys_private *p;  //记录总线的一些私有信息,
 };

struct subsys_private {  
 struct kset subsys;  //代表该总线类型在在/sys/bus/目录下所对应的目录项    
 struct kset *devices_kset; //指向在该总线类型目录下面的devices目录项
 struct kset *drivers_kset;//指向在该总线类型目录下面的drivers目录项
 struct klist klist_devices; //下挂属于该bus的设备
 struct klist klist_drivers;//下挂属于该bus的设备驱动
 struct blocking_notifier_head bus_notifier;
 unsigned int drivers_autoprobe:1;
 struct bus_type *bus; //指回总线类型
 struct list_head class_interfaces;
 struct kset glue_dirs;
 struct mutex class_mutex;
 struct class *class;
 };
    这个结构体用来描述比如:/sys/bus/pci pci总线,/sys/bus/platform platform总线等。
另外:从这个结构体分析下来,整个bus的目录结构都很清楚了。
eg:
    找到总线下的设备目录: bus_type bus ---> subsys_private p---->Kset devices_kset
    找到总线下的设备驱动目录: bus_type bus ---> subsys_private p---->Kset driver_kset
另外,找到的也只是目录,因为找到的仅仅是Kset结构。

2设备device

    首先明白,device这个结构并不是直接挂在bus下的,可以到/sys/bus/platform/device下随便看一下,发现里面的都是link文件,link到/sys/device/下。所以真正的device结构体的在/sys/device下的。
struct device {
struct device
*parent; //指向父设备
struct device_private*p;//device的私有数据,里面包含了该设备子设备链表的头部,以及挂入bus, driver结构设备链表的节点,类似于listhead
struct kobject kobj; //通过该kobj,device将被挂入到sys文件系统,形成一个目录
const char
*init_name; //设备名
struct device_type*type;  //设备类型
struct mutex
mutex; /* mutex to synchronize calls to
* its driver.
*/
struct bus_type*bus;//指向设备的bus type
struct device_driver *driver;//指向设备所用的driver
void *platform_data;/* Platform specific data, device
  core doesn't touch it */
struct dev_pm_infopower;
u64 *dma_mask;/* dma mask (if dma'able device) */
u64 coherent_dma_mask;/* Like dma_mask, but for
    alloc_coherent mappings as
    not all hardware supports
    64 bit addresses for consistent
    allocations such descriptors. */
struct device_dma_parameters *dma_parms;
struct list_headdma_pools;/* dma pools (if dma'ble) */
struct dma_coherent_mem*dma_mem; /* internal for coherent mem
    override */
/* arch specific additions */
struct dev_archdataarchdata;
dev_t devt;/* dev_t, creates the sysfs "dev" */
spinlock_t
devres_lock;
struct list_headdevres_head;
struct klist_nodeknode_class;  //通过该klist_node挂入对应的class设备链表
struct class
*class;//指向该klist_node所属的class
const struct attribute_group **groups;/* optional groups */
void (*release)(struct device *dev);
};

3设备driver

struct device_driver {
const char
*name;//驱动名称
struct bus_type*bus;//驱动所属总线类型
struct module
*owner;
const char
*mod_name; /* used for built-in modules */
bool suppress_bind_attrs;/* disables bind/unbind via sysfs */
#if defined(CONFIG_OF)
const struct of_device_id*of_match_table;
#endif
int (*probe) (struct device *dev);//探测及初始化设备的方法
int (*remove) (struct device *dev);//设备移除的方法
void (*shutdown) (struct device *dev);//设备关闭的方法
int (*suspend) (struct device *dev, pm_message_t state);//设备暂停的方法
int (*resume) (struct device *dev);//设备恢复的方法
const struct attribute_group **groups;
const struct dev_pm_ops *pm;
struct driver_private *p;//私有数据结构,里面存放了属于该driver的设备的链表头,
     //并提供了可以将driver挂入bus_type driver链表的klist_node节点
};

   下面讲述总线和设备的注册:
1总线的注册:
       用户可以自己注册一个总线,然后将自己喜欢的设备和驱动挂载到下面。但是linux 2.6中,有个默认的总线,platform总线。我们就分析一下这个总线。
int __init platform_bus_init(void)
{
int error;
early_platform_cleanup();
    
error = device_register(&platform_bus);
//设备注册。哦,linux将platform也当成了一个设备,他在/sys/device目录下。当然,以后会在platform这个设备下再建立其他的设备,回顾刚才介绍device结构体时候有个parent变量,应该就是用在这里的。具体device_register这个函数,后面再介绍  
if (error)
return error;
    //总线注册
error =  bus_register(&platform_bus_type);
if (error)
device_unregister(&platform_bus);
return error;
}

int bus_register(struct bus_type *bus)
{
int retval;
struct subsys_private *priv;
//分配bus_type的私有数据
priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);
if (!priv)
return -ENOMEM;
//建立私有数据和bus_type的关联
priv->bus = bus;
bus->p = priv;
BLOCKING_INIT_NOTIFIER_HEAD(&priv->bus_notifier);

//设置kset的name ,即总线目录的名字
retval = kobject_set_name(&priv->subsys.kobj, "%s", bus->name);
if (retval)
goto out;

//设置该总线类型目录的父目录
priv->subsys.kobj.kset = bus_kset;
//设置该总线类型目录的类型
priv->subsys.kobj.ktype = &bus_ktype;
//当设备添加时,自动检测匹配的驱动
priv->drivers_autoprobe = 1;

//将该bus_type对应的kset注册到sys文件系统中
retval = kset_register(&priv->subsys);
if (retval)
goto out;

//创建总线类型目录的属性文件
retval = bus_create_file(bus, &bus_attr_uevent);
if (retval)
goto bus_uevent_fail;

//创建属于该总线类型的devices kset,并将device kset
//
priv->devices_kset = kset_create_and_add("devices", NULL,
&priv->subsys.kobj);
if (!priv->devices_kset) {
retval = -ENOMEM;
goto bus_devices_fail;
}

//创建属于该总线类型的driver kset
priv->drivers_kset = kset_create_and_add("drivers", NULL,
&priv->subsys.kobj);
if (!priv->drivers_kset) {
retval = -ENOMEM;
goto bus_drivers_fail;
}

klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);
klist_init(&priv->klist_drivers, NULL, NULL);
//在bus的目录下添加bus_attr_drivers_probe 和 bus_attr_drivers_autoprobe文件。应该是probe的时候使用。
retval = add_probe_files(bus);
if (retval)
goto bus_probe_files_fail;
//将bus的属性都建成一个文件
retval = bus_add_attrs(bus);
if (retval)
goto bus_attrs_fail;

pr_debug("bus: '%s': registered\n", bus->name);
return 0;

bus_attrs_fail:
remove_probe_files(bus);
bus_probe_files_fail:
kset_unregister(bus->p->drivers_kset);
bus_drivers_fail:
kset_unregister(bus->p->devices_kset);
bus_devices_fail:
bus_remove_file(bus, &bus_attr_uevent);
bus_uevent_fail:
kset_unregister(&bus->p->subsys);
out:
kfree(bus->p);
bus->p = NULL;
return retval;
}
到此,bus_register解释完成。

2设备的注册
int device_register(struct device *dev)
{
device_initialize(dev);
return device_add(dev);
}
int device_add(struct device *dev)
{
struct device *parent = NULL;
struct class_interface *class_intf;
int error = -EINVAL;

dev = get_device(dev);
if (!dev)
goto done;

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;
}
if (!dev_name(dev)) {
error = -EINVAL;
goto name_error;
}
parent = get_device(dev->parent);//找到该设备的parent,该操作主要是
      //是增加父设备的引用计数
setup_parent(dev, parent);//对dev的obj中的parent赋值,赋值为父设备的
//obj,以便后续通过obj进行组织

/* use parent numa_node */
if (parent)
set_dev_node(dev, dev_to_node(parent));
//将该设备通过kobj嵌入到了vsys系统中
error = kobject_add(&dev->kobj, dev->kobj.parent, NULL);
if (error)
goto Error;

/* notify platform of device entry */
if (platform_notify)
platform_notify(dev);

//创建设备的属性文件
error = device_create_file(dev, &uevent_attr);
if (error)
goto attrError;

if (MAJOR(dev->devt)) {
error = device_create_file(dev, &devt_attr);
if (error)
goto ueventattrError;

error = device_create_sys_dev_entry(dev);
if (error)
goto devtattrError;

devtmpfs_create_node(dev);
}

//在其他文件夹 建立link文件,这就是为什么在class目录下也能看到device的目录和文件了  
error = device_add_class_symlinks(dev);
if (error)
goto SymlinkError;
error = device_add_attrs(dev);
if (error)
goto AttrsError;
//在bus目录下 建立link文件,所以在/sys/bus/platform/device下回看到n多个link文件
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_sysf_add() and before kobject_uevent().
*/
if (dev->bus)
blocking_notifier_call_chain(&dev->bus->p->bus_notifier,
    BUS_NOTIFY_ADD_DEVICE, dev);
//产生object热插拔事件
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->class_mutex);
/* tie the class to the device */
//将dev加入到/sys/class/xxx对应的设备链表上
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->class_interfaces, node)
if (class_intf->add_dev)
class_intf->add_dev(dev, class_intf);
mutex_unlock(&dev->class->p->class_mutex);
}
done:
put_device(dev);
return error;
 DPMError:
bus_remove_device(dev);
 BusError:
device_remove_attrs(dev);
 AttrsError:
device_remove_class_symlinks(dev);
 SymlinkError:
if (MAJOR(dev->devt))
devtmpfs_delete_node(dev);
if (MAJOR(dev->devt))
device_remove_sys_dev_entry(dev);
 devtattrError:
if (MAJOR(dev->devt))
device_remove_file(dev, &devt_attr);
 ueventattrError:
device_remove_file(dev, &uevent_attr);
 attrError:
kobject_uevent(&dev->kobj, KOBJ_REMOVE);
kobject_del(&dev->kobj);
 Error:
cleanup_device_parent(dev);
if (parent)
put_device(parent);
name_error:
kfree(dev->p);
dev->p = NULL;
goto done;
}
static int device_add_class_symlinks(struct device *dev)
{
int error;

if (!dev->class)
return 0;
//在/sys/device/XXX的目录下,创建一个符号链接,链接到
//该device所属的class,for example,subsystem -> ../../../../class/net
error = sysfs_create_link(&dev->kobj,
 &dev->class->p->subsys.kobj,
 "subsystem");
if (error)
goto out;
//在/sys/device/XXX的目录下,创建一个符号链接,指向其父设备目录,for example,oot@localhost:/sys/devices/pci0000:00/0000:00:11.0/0000:02:01.0/net/eth0> ls -l device
//lrwxrwxrwx 1 root root 0 Jul  5 02:11 device -> ../../../0000:02:01.0
//0000:02:01:0其实应该是其真正的设备ID
if (dev->parent && device_is_not_partition(dev)) {
error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
 "device");
if (error)
goto out_subsys;
}

#ifdef CONFIG_BLOCK
/* /sys/block has directories and does not need symlinks */
if (sysfs_deprecated && dev->class == &block_class)
return 0;
#endif
/* link in the class directory pointing to the device */
//在/sys/class/XXX的目录下,创建一个符号链接,指向该device
error = sysfs_create_link(&dev->class->p->subsys.kobj,
 &dev->kobj, dev_name(dev));
if (error)
goto out_device;
return 0;
out_device:
sysfs_remove_link(&dev->kobj, "device");
out_subsys:
sysfs_remove_link(&dev->kobj, "subsystem");
out:
return error;
}

    当然还有 drive_register的函数,其实和device_register差不多,另外,driver_register也会在最后进行probe,看有没有相应的设备。driver_register会先check这个drvier所在的bus上有没有probe函数,如果有就运行这个函数进行probe,如果没有,就运行自己的probe进行probe,这就是我们在驱动中经常看到的probe函数。
    所以,在驱动中,先运行drive_register和先运行device_register都是一样的。

【上篇】
【下篇】

抱歉!评论已关闭.