Linux设备模型(总线、设备、驱动程序和类)之二:device

       在最底层,Linux 系统中的每个设备由一个struct device 代表:
struct device

{
    struct klist  klist_children;
    struct klist_node knode_parent; /* node in sibling list */
    struct klist_node knode_driver;
    struct klist_node knode_bus;
    struct device  *parent; * 设备的 "父" 设备,该设备所属的设备,通常一个父设备是某种总线或者主控制器。如果 parent 是 NULL, 则该设备是顶层设备,较少见 */

    struct kobject kobj;
    char bus_id[BUS_ID_SIZE]; /* position on parent bus */
    const char  *init_name; /* initial name of the device */
    struct device_type *type;
    unsigned  uevent_suppress:1;
    struct semaphore sem; /* semaphore to synchronize calls to its driver.*/
    struct bus_type *bus;  /* type of bus device is on */
    struct device_driver *driver; /* which driver has allocated this device */
    void  *driver_data; /* data private to the driver */
    void  *platform_data; /* Platform specific data, device core doesn't touch it */
    struct dev_pm_info power;

#ifdef CONFIG_NUMA
    int  numa_node; /* NUMA node this device is close to */
#endif
    u64  *dma_mask; /* dma mask (if dma'able device) */
    u64  coherent_dma_mask;

    struct device_dma_parameters *dma_parms;
    struct list_head dma_pools; /* dma pools (if dma'ble) */
    struct dma_coherent_mem *dma_mem; /* internal for coherent mem override */
    /* arch specific additions */
    struct dev_archdata archdata;
    spinlock_t  devres_lock;
    struct list_head devres_head;

    struct list_head node;
    struct class  *class;
    dev_t   devt; /* dev_t, creates the sysfs "dev" */
    struct attribute_group **groups; /* optional groups */
    void (*release)(struct device *dev);  /*当这个设备的最后引用被删除时,内核调用该方法; 它从被嵌入的kobject的release 方法中调用。所有注册到核心的设备结构必须有一个 release 方法, 否则内核将打印错误信息*/
};
(1)设备注册
在注册struct device 前,最少要设置parent, bus_id, bus, 和 release 成员,设备的注册和注销函数为:
int device_register(struct device *dev);
void device_unregister(struct device *dev);
      一个实际的总线也是一个设备,所以必须单独注册,以下为lddbus注册它的虚拟总线设备:
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
}; /*这是顶层总线,parent 和 bus 成员为 NULL*/
/*作为第一个总线,它的名字为ldd0,这个总线设备的注册代码如下:*/
ret = device_register(&ldd_bus);
if (ret)
   printk(KERN_NOTICE "Unable to register ldd0/n");
/*一旦调用完成, 新总线ldd0会在sysfs中/sys/devices下显示,任何挂到这个总线的设备会在/sys/devices/ldd0 下显示*/
(2)设备属性
sysfs 中的设备入口可有属性,相关的结构是:
struct device_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);
};
/*设备属性结构可使用以下宏(与BUS类比):*/
DEVICE_ATTR(_name,_mode,_show,_store);
/*这个宏声明一个结构, 将 dev_attr_ 作为给定 _name 的前缀来命名设备属性
/*属性文件的实际处理使用以下函数:*/
 int device_create_file(struct device *device,  struct device_attribute * entry);
 void device_remove_file(struct device * dev,  struct device_attribute * attr);

一个实例是:终端执行:cd /sys/class/leds/lcd-backlight,

ls回显:

uevent
subsystem
device
power
brightness

这些属性可能都是通过device_create_file添加上去(至少brightness是这样)。进入device目录,再输入pwd,

回显:/sys/devices/platform/smdk-backlight。变换到devices目录下了,可见设备模型的不同构成是指向同一个设备的。

      2.6下字符设备开始用struct cdev结构体表示,但是我想调用device_create_file(dev, &dev_attr_debug);函数在/sys中导出信息,device_create_file()的第一个入口参数类型为struct device结构体。问题是struct cdev与struct device这两个结构体没有任何联系的地方?答案是可以采用共同拥有的Kobjcet这个成员作为纽带,所以从子类cdev--->父类kobject--->子类device,推导得到:container_of(kobj)-->list_entry(entry)->(struct device*)  。因为containerof是从结构体指针成员找到结构体地址,所以从cdev的kobj可以找到父类kobject的地址,而所有的kobject的entery都是在一个链表里面,遍历这个链表,找到结构体成员为特定device结构的那一项。
(3)设备结构的嵌入
      device 结构包含设备模型核心用来模拟系统的信息。但大部分子系统记录了关于它们拥有的设备的额外信息,所以很少单纯用device 结构代表设备,而是通常将其嵌入一个设备的高层结构体表示中。

      lddbus 驱动创建了它自己的 device 类型(也即每类设备会建立自己的设备结构体,其中至少一个成员是struct device类型,比如video_device),并期望每个设备驱动使用这个类型来注册它们的设备:
struct ldd_device {
 char *name;
 struct ldd_driver *driver;
 struct device dev;
};
#define to_ldd_device(dev) container_of(dev, struct ldd_device, dev); 
      lddbus 导出的注册和注销接口如下:
static void ldd_dev_release(struct device *dev)
{ }

int register_ldd_device(struct ldd_device *ldddev)
{
      ldddev->dev.bus = &ldd_bus_type;   //依赖的总线
      ldddev->dev.parent = &ldd_bus;       //父设备
      ldddev->dev.release = ldd_dev_release;
      strncpy(ldddev->dev.bus_id, ldddev->name, BUS_ID_SIZE); //设备名拷贝入device结构体中
      return device_register(&ldddev->dev);   //仍然用device_register注册,只不过上层打包了
}
EXPORT_SYMBOL(register_ldd_device);
void unregister_ldd_device(struct ldd_device *ldddev)
{
       device_unregister(&ldddev->dev);
}
EXPORT_SYMBOL(unregister_ldd_device);

 


参考原文:http://blog.csdn.net/funy_liu/archive/2010/02/25/5322040.aspx

参考原文:http://www.linuxforum.net/forum/showthreaded.php?Cat=&Board=driver&Number=659533&page=0&view=collapsed&sb=5&o=0

  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值