Linux驱动之设备模型(5)-设备

1. 设备,LDM中用device结构体来描述设备

struct device {
 /* 父设备,通常是某种总线或者是宿主设备 */
         struct device             *parent;  
 /* 私有数据指针:子设备链表,父设备链表节点,驱动程序链表节点,总线链表节点 */
         struct device_private       *p;  
 
         struct kobject kobj;          /* 连接到结构体系中的kobject */
         const char                  *init_name; /* initial name of the device */
         const struct device_type *type;  /* 设备类型 */
 
         struct bus_type        *bus;    /* 依附的总线 */
         struct device_driver *driver;   /* 关联的驱动程序 */
         void           *platform_data;      /* Platform specific data*/
 
         dev_t                           devt;         /* dev_t, creates the sysfs"dev" */
 
         struct klist_node      knode_class;  /*  */
         struct class                *class;       /* 设备所属的类 */
         const struct attribute_group **groups; /* optional groups */
 
         void  (*release)(structdevice *dev);  /* 当引用计数(kobject->kref)减为0时调用 */
};

我们在注册一个设备时,通常要初始化parent,name,bus_id和bus这四个成员。


2. 设备的注册和注销

    int device_register(struct device *dev)
    void device_unregister(struct device *dev)

    注册函数分析,注册函数分为两个部分device_initialize和device_add,具体分析一下device_add

int device_add(structdevice *dev)
{
         struct device *parent = NULL;
         struct class_interface *class_intf;
         int error = -EINVAL;
        
         /* 引用计数加1 */
         dev = get_device(dev);
 
         /*
          * for statically allocated devices, whichshould all be converted
          * some day, we need to initialize the name. We prevent reading back
          * the name, and force the use of dev_name()
          */
         if (dev->init_name) {
                   dev_set_name(dev,"%s", dev->init_name);
                   dev->init_name = NULL;
         }
 
         /* 增加父设备的引用计数 */
         parent = get_device(dev->parent);
         setup_parent(dev, parent);
 
         /* 把内嵌的kobject注册到设备模型中 */
         error = kobject_add(&dev->kobj,dev->kobj.parent, NULL);
 
                   /*创建属性文件uevent */
         error = device_create_file(dev,&uevent_attr);
 
         /* 如果定义了devt,则产生dev属性,并在/dev目录下产生设备节点文件 */
         if (MAJOR(dev->devt)) {
                   error = device_create_file(dev,&devt_attr);
                  }
 
         /* 创建属性文件 */
         error = device_add_class_symlinks(dev);
         error = device_add_attrs(dev);
 
         /* 把设备添加到总线上,这个是重点,稍后分析*/
         error = bus_add_device(dev);
 
                   /*产生KOBJ_ADD uevent */
         kobject_uevent(&dev->kobj,KOBJ_ADD);
 
         /* 给设备探测相应的驱动 */
         bus_probe_device(dev);
 
         /* 如果设备有父设备,将它加入parent的子设备链中 */
         if (parent)
                   klist_add_tail(&dev->p->knode_parent,
                                   &parent->p->klist_children);
 
         /* 如果设备附属于某个类,则需完成相应工作*/
         if (dev->class) {
                   /* 把设备添加到class的设备链表中,完成关联*/
                   klist_add_tail(&dev->knode_class,
                                  &dev->class->p->klist_devices);
 
                   /* notify any interfaces thatthe 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);
         }
}
bus_add_device()

int bus_add_device(struct device *dev)
{
         /* 引用计数加一 */
         struct bus_type *bus =bus_get(dev->bus);
 
         if (bus) {
                   /* 创建相应的属性文件 */
                   error = device_add_attrs(bus,dev);
                   error =sysfs_create_link(&bus->p->devices_kset->kobj,
                                                        &dev->kobj,dev_name(dev));
                   error =sysfs_create_link(&dev->kobj,
                                     &dev->bus->p->subsys.kobj,"subsystem");
                   /* 把设备加入到总线的设备链中,这步才是重点*/
                   klist_add_tail(&dev->p->knode_bus,&bus->p->klist_devices);
         }
}

bus_probe_device()->device_attach()

int device_attach(struct device *dev)
{
         if (dev->driver) {
                   /* 如果设备已经依附于某个驱动,进行绑定 */
                   ret =device_bind_driver(dev);
         } else {
                   /* 遍历bus->p->klist_drivers寻找匹配的驱动,然后执行回调函数__device_attach */
                   ret = bus_for_each_drv(dev->bus, NULL, dev,__device_attach);
         }
out_unlock:
         device_unlock(dev);
         return ret;
}

__device_attach()->driver_probe_device()->really_probe()->bus->probe()->drv->probe()  总线中定义的probe函数会优先执行,如果总线中没有定义probe才会执行驱动中定义的probe


3. 设备属性

   设备属性由device_attribute来表示

structdevice_attribute {
         struct attribute        attr;      /* 属性 */
         ssize_t (*show)(struct device *dev,struct device_attribute *attr,
                            char *buf);  /* 读取属性 */
         ssize_t (*store)(struct device *dev,struct device_attribute *attr,
                             const char *buf, size_t count);  /* 写入属性 */
};

初始化设备属性:

DEVICE_ATTR(_name,_mode, _show, _store)

 在sysfs目录下产生和移除属性文件

int device_create_file(struct device *device,
                                               conststruct device_attribute *entry);
void device_remove_file(struct device *dev,
                                                const struct device_attribute*attr);

 

4. 实例解析

创建一个设备和它的属性version,并将此设备挂接到之前创建的总线上

/*
* for learn device
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/device.h>
 
extern struct bus_type scbus_type;
extern struct device scbus;
 
static char *Version = "revision1.0";
 
/* 析构函数,引用计数减为0时调用 */
void screlease(struct device *dev)
{
         printk("scbusrelease\n");
}
 
struct device scdevice = {
         .parent     = &scbus,           /* 父设备,此处为依附的总线 */
         .init_name        = "scdevice0", 
         .bus = &scbus_type,        /* 依附的总线类型 */
         .release   = screlease,
};
 
/*
* export device attribute
*/
static ssize_t show_device_version(structdevice *dev,
                            structdevice_attribute *attr, char *buf)
{
         returnsprintf(buf, "%s\n", Version);
}
DEVICE_ATTR(version, 0666,show_device_version, NULL);
 
 
static int __init scdevice_init(void)
{
         intret;
 
         /*注册设备 */
         ret= device_register(&scdevice);
         if(ret)
                   returnret;
 
         /*创建属性 */
         ret= device_create_file(&scdevice, &dev_attr_version);
         if(ret)
                   gotoerr_create;
 
         printk("Createa scdevice");
         return0;
 
err_create:
         device_unregister(&scdevice);
         returnret;
}
 
static void __exit scdevice_exit(void)
{
         device_remove_file(&scdevice,&dev_attr_version);
         device_unregister(&scdevice);
         printk("Removea scdevice");
}
 
module_init(scdevice_init);
module_exit(scdevice_exit);
 
MODULE_LICENSE("Dual BSD/GPL");
MODULE_AUTHOR("CJOK<cjok.liao@gmail.com>");
 

试验结果:



  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值