总线设备驱动模型总结
2、设备
设备的实现主要是依靠struct device函数实现的,设备的实现主要是对结构体的填充。实现相应的函数即可。
struct device {
/*父设备,通常就是总线设备,这也是为什么需要将总线作为设备添加的原因*/
struct device *parent;
struct device_private *p;
struct kobject kobj;
/*init_name是新添加的,替代了原来的bus_id,但是init_name不能直接被读写操作*/
const char *init_name; /* initial name of the device */
struct device_type *type;
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 doesnt 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 dmaable 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_head dma_pools; /* dma pools (if dmable) */
struct dma_coherent_mem *dma_mem; /* internal for coherent mem
override */
/* arch specific additions */
struct dev_archdata archdata;
dev_t devt; /* dev_t, creates the sysfs "dev" */
spinlock_t devres_lock;
struct list_head devres_head;
struct klist_node knode_class;
struct class *class;
struct attribute_group **groups; /* optional groups */
/*必须实现的release函数*/
void (*release)(struct device *dev);
};
/*由于init_name 不能直接读写,只能通过*dev_name来读写设备名*/
static inline const char *dev_name(const struct device *dev)
{
return kobject_name(&dev->kobj);
}
/*实现对设备名的设置*/
int dev_set_name(struct device *dev, const char *name, ...)
__attribute__((format(printf, 2, 3)));
/*设备文件属性结构体,必须注意的改变点*/
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);
};
/*设备属性宏定义,主要用来实现设备文件属性*/
#define DEVICE_ATTR(_name, _mode, _show, _store)
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
/*创建设备文件属性函数,必须检查返回值*/
int __must_check device_create_file(struct device *device,struct device_attribute *entry);
/*删除设备文件属性函数*/
void device_remove_file(struct device *dev,struct device_attribute *attr);
/*设备注册函数,必须检查返回值*/
int __must_check device_register(struct device *dev);
/*设备释放函数*/
void device_unregister(struct device *dev);
需要注意的是linux-2.6.30内核以前,没有init_name元素,而是元素bus_id,这个主要是实现设备名的填充,但是linux-2.6.30内核之后的在struct device中init_name代替了bus_id,但是需要注意的是init_name不能直接被读写,当需要读写设备名时只能采用特定的函数实现:dev_name(),set_dev_name()。当直接读写init_name会导致内核错误,出现Unable to handle kernel NULL pointer dereference at virtual address 00000000的错误。
最后注意device_attribute中的show,store函数不同于其他类型(总线、驱动)的函数,device_attribute中的show和store函数中的参数数量多了一个。
3、驱动
驱动管理一定的设备,其中的关系主要是内核的内部机制实现的,但是实现的具体逻辑需要在bus_type中的match函数中具体设计。通常是一定的设备名和驱动名匹配,当然也可以有其他的逻辑,具体的只需要设计好bus_type中的match函数。
驱动是由驱动结构体实现的。具体如下所示:
/*驱动结构体*/
struct device_driver {
/*驱动名,通常用来匹配设备*/
const char *name;
/*关联的总线类型,总线、设备、驱动关联的总线类型*/
struct bus_type *bus;
struct module *owner;
const char *mod_name; /* used for built-in modules */
/*驱动中最应该实现的操作函数主要包括probe和remove函数*/
/*当匹配完成以后的,入口函数*/
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);
struct attribute_group **groups;
struct dev_pm_ops *pm;
struct driver_private *p;
};
/*驱动注册函数,返回值必须检测*/
int __must_check driver_register(struct device_driver *drv);
/*驱动释放函数*/
void driver_unregister(struct device_driver *drv);
/*驱动属性结构体*/
struct driver_attribute {
/*属性值*/
struct attribute attr;
/*属性读操作函数*/
ssize_t (*show)(struct device_driver *driver, char *buf);
/*属性写操作函数*/
ssize_t (*store)(struct device_driver *driver, const char *buf,
size_t count);
};
/*驱动属性定义宏命令*/
#define DRIVER_ATTR(_name, _mode, _show, _store)
struct driver_attribute driver_attr_##_name =
__ATTR(_name, _mode, _show, _store)
/*驱动属性文件创建函数,返回值必须检测*/
int __must_check driver_create_file(struct device_driver *driver,struct driver_attribute *attr);
/*驱动属性文件移除函数*/
void driver_remove_file(struct device_driver *driver,struct driver_attribute *attr);
驱动结构体的定义不需要完成所有元素的赋值,只需要完成主要的几个变量的赋值即可,其中主要的元素包含name,bus,以及probe和remove函数的实现。
其中的probe函数是当总线中的match完成匹配操作以后,进入驱动的入口函数,因此必须实现。remove我认为就是对应的退出函数,因此也有必要实现。
驱动的注册,释放也有相关的函数来操作,主要是driver_register()和driver_unregister()。
总结:
1、在总线驱动模型中我认为最主要的是搞清楚三个不同的结构体,分别是总线、驱动、设备。了解三个元素对应的属性结构体以及相应的属性操作函数的差异性。
2、不同驱动设计的关键主要是完成不同结构体的填充过程,但是并不需要对结构体中所有的对象进行赋值,只需要完成重要的几个元素的值。
3、总线是一种类型,同时也是一种设备,在总线的相关处理中需要首先添加总线类型,然后添加总线设备,这是需要注意的。由于总线类型关联驱动和设备,因此需要导出总线类型变量。由于总线设备是设备的父设备,因此也需要将总线设备变量导出。同样在驱动和设备中也要导出相关的结构体变量,便于总线中的match函数实现驱动和设备的匹配操作。
4、XXX_attr结构体基本相同,都是一个属性结构体和函数show()、stroe()。但是不同的XXX可能会导致show、stroe函数的参数发生变化。这需要对照源码。
5、struct device中的init_name是一个特殊的量,不能直接读写操作,只能采用函数device_name()和set_device_name来设置设备名。
关键词:
总线设备驱动模
评论