译者:郭少悲
2009/12/02
原文:linux/Documentation/driver-model/device.txt
基本的设备数据结构
~~~~~~~~~~~~~~~~~~
struct device {
struct list_head g_list;
struct list_head node;
struct list_head bus_list;
struct list_head driver_list;
struct list_head intf_list;
struct list_head children;
struct device * parent;
char name[DEVICE_NAME_SIZE];
char bus_id[BUS_ID_SIZE];
spinlock_t lock;
atomic_t refcount;
struct bus_type * bus;
struct driver_dir_entry dir;
u32 class_num;
struct device_driver *driver;
void *driver_data;
void *platform_data;
u32 current_state;
unsigned char *saved_state;
void (*release)(struct device * dev);
};
域
~~
g_list: 全局设备链表中的节点。
node: 设备的父亲的孩子链表中的节点(父子关系是树结构中的常用关系)。
bus_list: 设备所属的总线的设备链表中的节点。
driver_list: 设备所关联的驱动的设备链表中的节点。
intf_list: intf_data结构的链表。该结构分配给设备支持的每个接口。
children: 孩子设备链表。
parent: *** FIXME ***
name: 设备的文本描述。
例子: " 3Com Corporation 3c905 100BaseTX [Boomerang]"
bus_id: 设备所处的总线位置的文本描述。这个域对于每一个属于该总线的设备而言应当是
一个唯一的名字。
例如:PCI bus_ids有下己部分构成:
<bus number>:<slot number>.<function number>
这个名字对于PCI上的设备来说是唯一标识的。
lock: 设备锁。
refcount: 设备的引用计数。
bus: 指向设备所属的总线类型的指针。
dir: 设备的sysfs目录。
class_num: 设备的class-enumerated值。
driver: 指向控制设备的struct device_driver指针。
driver_data: 驱动相关的数据。
platform_data: 针对设备的平台数据。
示例:对于各种嵌入式和SOC上的设备,Linux常常使用platform_data来表示板级相关的数据
结构,以此来描述设备及其连接。这包括了适用的端口号,芯片管脚值(GPIO管脚用途)等等。
这种方式缩小了BSPs尺寸,减少了驱动中板级相关的#ifdefs语句。
current_state: 当前设备的电源状态。
saved_state: 指向设备的保存状态的指针。这个域被控制设备的驱动来使用。
release: 所有引用结束后的释放设备的回调函数。它应当由设备的分配器来设置(例如,
由探测设备的总线驱动来设置)。
编程接口
~~~~~
探测设备的总线驱动使用下面的函数接口来注册设备到核心:
int device_register(struct device * dev);
总线应当初始化下面的域:
- parent
- name
- bus_id
- bus
当一个设备的引用计数为0时,设备会从核心中被一删除。引用计数的增减应当
使用下面的函数接口:
struct device * get_device(struct device * dev);
void put_device(struct device * dev);
get_device()将会返回一个指向struct device的指针,如果引用计数不为0。
一个驱动访问设备结构中的锁,使用下面接口函数:
void lock_device(struct device * dev);
void unlock_device(struct device * dev);
属性
~~~~
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);
};
驱动可以导出设备的属性,通过使用一个简单的procfs-like接口。
请阅读Documentation/filesystems/sysfs.tx来获取更多的sysfs的信息。
通过宏DEVICE_ATTR来声明属性:
#define DEVICE_ATTR(name,mode,show,store)
例如:
DEVICE_ATTR(power,0644,show_power,store_power);
它声明了一个结构为device_attribute的'dev_attr_power'变量。可以使用
下面接口函数来添加和删除这个属性变量。
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
例如:
device_create_file(dev,&dev_attr_power);
device_remove_file(dev,&dev_attr_power);
这个文件名为'power',文件访问属性为0644(-rw-r--r--)。
警告信息:尽管内核允许在一个设备的任何时间上调用device_create_file()和
device_remove_file()函数,用户空间对属性的创建还是有严格的预期。当一个新设备
被注册到内核时,一个uevent事件产生通知用户空间(udev)一个新设备可用。当设备新
属性在设备注册后添加,用户空间没有获得事件通知,就无法得知新的属性。
设备驱动在驱动探测时间内需要为一个设备添加额外的属性,这是很重要的一个需求。
如果设备驱动仅仅简单的调用device_create_file(),用户空间得不到任何关于
新属性的通知。因此,应当在module_init()函数里使用class_create()和
class->dev_attrs建立一个所需属性的链表,然后在.probe()钩子函数里使用device_create()
来创建一个新的设备,作为被探测设备的孩子。这个新设备将产生一个新的uevent,
将新的属性通知给用户空间。
例如,一个驱动想添加如下的属性:
struct device_attribute mydriver_attribs[] = {
__ATTR(port_count, 0444, port_count_show),
__ATTR(serial_number, 0444, serial_number_show),
NULL
};
在module_init()函数里应当:
mydriver_class = class_create(THIS_MODULE, "my_attrs");
mydriver_class.dev_attr = mydriver_attribs;
假定'dev'是传递给probe钩子函数的struct device结构变量,驱动的probe函数应当作如下
的事情:
create_device(&mydriver_class, dev, chrdev, &private_data, "my_name");
Linux内核设备模型(5)
