[快速上手Linux设备驱动]之我看Linux设备模型(设备篇)

上一篇,walfred讲述了一个简单的字符设备驱动,没有看的同学一定要好好的看哦[快速上手Linux设备驱动]之我看Linux字符设备驱动,从这篇文章起,OurUnix转了一篇很不错的描写Linux设备模型的文章。例子和实验使用《LDD3》所配的lddbus模块(稍作修改)。

    当然在看这一篇文章之前,希望你已经阅读了[快速上手Linux设备驱动]之我看Linux设备模型(总线篇)一文。

 设备

1 概述

    在最底层, 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;/*代表该设备并将其连接到结构体系中的 kobject; 注意:作为通用的规则, device->kobj->parent 应等于 device->parent->kobj*/

    char    bus_id[BUS_ID_SIZE];/*在总线上唯一标识该设备的字符串;例如: PCI 设备使用标准的 PCI ID 格式, 包含:域, 总线, 设备, 和功能号.*/

    struct device_type    *type;

    unsigned        is_registered:1;

    unsigned        uevent_suppress:1;

    struct device_attribute uevent_attr;

    struct device_attribute *devt_attr;

    struct semaphore    sem;  /* semaphore to synchronize calls to its driver. */

    struct bus_type    * bus;     /*标识该设备连接在何种类型的总线上*/

    struct device_driver *driver;    /*管理该设备的驱动程序*/

    void        *driver_data;    /*该设备驱动使用的私有数据成员*/

    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;/* Like dma_mask, but for

                     alloc_coherent mappings as

                     not all hardware supports

                     64 bit addresses for consistent

                     allocations such descriptors. */

    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;

    /* class_device migration path */

    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 方法, 否则内核将打印错误信息*/

};

/*在注册 struct device 前,最少要设置parent, bus_id, bus, 和 release 成员*/

2 设备注册

2.1设备的注册和注销函数为:

int device_register(struct device *dev);

void device_unregister(struct device *dev);

2.2一个实际的总线也是一个设备,所以必须单独注册,以下为 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");

/*一旦调用完成, 新总线会在 sysfs 中 /sys/devices 下显示,任何挂到这个总线的设备会在/sys/devices/ldd0 下显示*/

 3 设备属性

        sysfs 中的设备入口可有属性,相关的结构是: 

/* interface for exporting device attributes 这个结构体和《LDD3》中的不同,已经被更新过了,请特别注意!*/

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);

};

 /*设备属性结构可在编译时建立, 使用以下宏:*/

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);

4 设备结构的嵌入

         device 结构包含设备模型核心用来模拟系统的信息。但大部分子系统记录了关于它们又拥有的设备的额外信息,所以很少单纯用 device 结构代表设备,而是,通常将其嵌入一个设备的高层表示中。底层驱动几乎不知道 struct device。

4.1 lddbus 驱动创建了它自己的 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);

4.2 lddbus 导出的注册和注销接口如下:

 * For now, no references to LDDbus devices go out which are not

 * tracked via the module reference count, so we use a no-op

 * release function.

 */

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);

    return device_register(&ldddev->dev);

}

EXPORT_SYMBOL(register_ldd_device);

 

void unregister_ldd_device(struct ldd_device *ldddev)

{

    device_unregister(&ldddev->dev);

}

EXPORT_SYMBOL(unregister_ldd_device);

 sculld 驱动添加一个自己的属性到它的设备入口,称为 dev, 仅包含关联的设备号,源码如下:

static ssize_t sculld_show_dev(struct device *ddev,struct device_attribute *attr, 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);

/*接着, 在初始化时间, 设备被注册, 并且 dev 属性通过下面的函数被创建:*/

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);

 if (device_create_file(&dev->ldev.dev, &dev_attr_dev))

    printk( "Unable to create dev attribute ! /n");

} /*注意:程序使用 driver_data 成员来存储指向我们自己的内部的设备结构的指针。请检查device_create_file的返回值,否则编译时会有警告。*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值