Chinaunix首页 | 论坛 | 博客
  • 博客访问: 3549926
  • 博文数量: 1805
  • 博客积分: 135
  • 博客等级: 入伍新兵
  • 技术积分: 3345
  • 用 户 组: 普通用户
  • 注册时间: 2010-03-19 20:01
文章分类

全部博文(1805)

文章存档

2017年(19)

2016年(80)

2015年(341)

2014年(438)

2013年(349)

2012年(332)

2011年(248)

分类: 嵌入式

2014-08-03 08:45:37

原文地址:Linux设备驱动模型——设备 作者:Knivo


The Basic Device Structure
~~~~~~~~~~~~~~~~~~~~~~~~~~
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);
};
 
基本设备结构
------------》
 
同样的这结构也发生了较大的改动,给出2.6.29版本的结构如下:
 
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;
 struct kobject kobj;
 char bus_id[BUS_ID_SIZE]; /* position on parent bus */
 unsigned  uevent_suppress:1;
 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 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 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;
 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 */
 void (*release)(struct device *dev);
};
 
注册函数:
 
    int device_register(struct device *dev);
 
译者见解:
说实话,现在的我也不能完全看懂结构所有成员的用处,这需要走进LDM核心中才
会有所收获。等到翻译完设备驱动模型三个对象的相关文档我会尝试着走进LDM去
研究这的用处。等我把所有的文档翻译完了,对LDM的理解也就差不多了。当然,
我会让我所理解的LDM核心up to date的。先坚持着走完这些吧。
 
Fields
~~~~~~
g_list: Node in the global device list.
node: Node in device's parent's children list.
bus_list: Node in device's bus's devices list.
driver_list:   Node in device's driver's devices list.
intf_list:     List of intf_data. There is one structure allocated for
        each interface that the device supports.
children:      List of child devices.
parent:        *** FIXME ***
name:        ASCII description of device.
        Example: " 3Com Corporation 3c905 100BaseTX [Boomerang]"
bus_id:        ASCII representation of device's bus position. This
        field should be a name unique across all devices on the
        bus type the device belongs to.
        Example: PCI bus_ids are in the form of
        :.
        This name is unique across all PCI devices in the system.
lock:        Spinlock for the device.
refcount:      Reference count on the device.
bus:        Pointer to struct bus_type that device belongs to.
dir:        Device's sysfs directory.
class_num:     Class-enumerated value of the device.
driver:        Pointer to struct device_driver that controls the device.
driver_data:   Driver-specific data.
platform_data: Platform data specific to the device.
        Example:  for devices on custom boards, as typical of embedded
        and SOC based hardware, Linux often uses platform_data to point
        to board-specific structures describing devices and how they
        are wired.  That can include what ports are available, chip
        variants, which GPIO pins act in what additional roles, and so
        on.  This shrinks the "Board Support Packages" (BSPs) and
        minimizes board-specific #ifdefs in drivers.
current_state: Current power state of the device.
saved_state:   Pointer to saved state of the device. This is usable by
        the device driver controlling the device.
release:       Callback to free the device after all references have
        gone away. This should be set by the allocator of the
        device (i.e. the bus driver that discovered the device).
 
成员列表
--------》
 
g_list:         全局设备链表
 
node:         作为链接在当前设备父设备子设备链表中的节点
 
bus_list:       作为链接在当前设备总线结构设备链表中的节点
 
driver_list:    作为链接在当前设备驱动结构设备链表中的节点
 
intf_list:      接口数据链表。内核会为设备支持的每一个接口都分配一个intf_data结构
 
children:       子设备链表
 
parent:         指向父设备的指针
 
name:         设备名字的ASCII描述
 
bus_id:         设备总线位置的ASCII表示。在当前类型总线中,此域的值应该唯一(作为id)
                例如: PCI总线id是这样组成的:.
                       所以,得到了一个整个系统中唯一的名字。
                      
lock:         设备用的自旋锁
 
refcount:       设备的引用计数
 
bus:         指向设备所述总线的总线类型结构
 
dir:         指向设备在sys文件系统中的目录
 
class_num:      设备的类枚举值
 
driver:         指向设备所属的驱动
 
driver_data:    与特定驱动相关的数据
 
platform_data:  设备特定的平台数据
 
                 例如:Linux通常会用platform_data去指定特定主板相关的数据结构,
                 这些数据结构用于描述设备的特性和线路连接(特定主板是指有板载系
                 统的板子)。这些结构能够包含:可用的端口,不同版本的芯片,GPIO
                 针脚的用法等等的。这些减少了使用BSP包和驱动中使用#ifdef的数量。

current_state:   当前电源状态
 
saved_state:     指向保存的设备状态。此域对设备的驱动不可用。
 
release:         当设备引用计数为零时,调用的会调函数。此域应该被设备的分配
                 者所初始化(指发现设备的总线驱动)
 
译者见解:
对比旧的结构,可以发现新的结构改变还是比较大的。我希望自己能够从思想上体会到开
发者的意图,而不是到最后只得到了一个模糊的框架,得不到任何开发思维上的提高。
 
Programming Interface
~~~~~~~~~~~~~~~~~~~~~
The bus driver that discovers the device uses this to register the
device with the core:
int device_register(struct device * dev);
The bus should initialize the following fields:
    - parent
    - name
    - bus_id
    - bus
A device is removed from the core when its reference count goes to
0. The reference count can be adjusted using:
struct device * get_device(struct device * dev);
void put_device(struct device * dev);
get_device() will return a pointer to the struct device passed to it
if the reference is not already 0 (if it's in the process of being
removed already).
A driver can access the lock in the device structure using:
void lock_device(struct device * dev);
void unlock_device(struct device * dev);

编程接口
--------》
 
总线驱动在发现设备后,通过下面函数向LDM核心注册设备:
 
int device_register(struct device * dev);
 
其中,总线应该在注册之间初始化好device结构下面的成员:
 
    - parent
    - name
    - bus_id
    - bus
 
当引用计数被减为0时,设备将会从核心中移除,可以通过下面的方式改变一个设
备的引用计数:
 
struct device * get_device(struct device * dev);
void put_device(struct device * dev);
 
如果被传参的device结构不为零,get_device()结构将会返回一个指向它的指针。
(注:当核心正准备移除它时,当然就不能够再将其引用)
 
驱动可以通过下面接口来改变device结构里面的锁成员:
 
void lock_device(struct device * dev);
void unlock_device(struct device * dev);
 
译者见解:
不但是在LDM核心中,在内核的许多地方都是通过get_xxxx和put_xxxx的方式来增加
和减少某个结构的引用计数的。请以后注意这些关键字。这里我们在总线必须初始
化device结构成员的部分多留个心,对以后研究LDM的内核对象层会有很大的帮助。
这里我所说的内核对象层是指kobject和kset结构的那一层。这里不展开,以后我会
有专门的文章论述。
 
Attributes
~~~~~~~~~~
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);
};
Attributes of devices can be exported via drivers using a simple
procfs-like interface.
Please see Documentation/filesystems/sysfs.txt for more information
on how sysfs works.
Attributes are declared using a macro called DEVICE_ATTR:
#define DEVICE_ATTR(name,mode,show,store)
Example:
DEVICE_ATTR(power,0644,show_power,store_power);
This declares a structure of type struct device_attribute named
'dev_attr_power'. This can then be added and removed to the device's
directory using:
int device_create_file(struct device *device, struct device_attribute * entry);
void device_remove_file(struct device * dev, struct device_attribute * attr);
Example:
device_create_file(dev,&dev_attr_power);
device_remove_file(dev,&dev_attr_power);
The file name will be 'power' with a mode of 0644 (-rw-r--r--).
Word of warning:  While the kernel allows device_create_file() and
device_remove_file() to be called on a device at any time, userspace has
strict expectations on when attributes get created.  When a new device is
registered in the kernel, a uevent is generated to notify userspace (like
udev) that a new device is available.  If attributes are added after the
device is registered, then userspace won't get notified and userspace will
not know about the new attributes.
This is important for device driver that need to publish additional
attributes for a device at driver probe time.  If the device driver simply
calls device_create_file() on the device structure passed to it, then
userspace will never be notified of the new attributes.  Instead, it should
probably use class_create() and class->dev_attrs to set up a list of
desired attributes in the modules_init function, and then in the .probe()
hook, and then use device_create() to create a new device as a child
of the probed device.  The new device will generate a new uevent and
properly advertise the new attributes to userspace.
For example, if a driver wanted to add the following attributes:
struct device_attribute mydriver_attribs[] = {
 __ATTR(port_count, 0444, port_count_show),
 __ATTR(serial_number, 0444, serial_number_show),
 NULL
};
Then in the module init function is would do:
 mydriver_class = class_create(THIS_MODULE, "my_attrs");
 mydriver_class.dev_attr = mydriver_attribs;
And assuming 'dev' is the struct device passed into the probe hook, the driver
probe function would do something like:
 create_device(&mydriver_class, dev, chrdev, &private_data, "my_name");

 
属性
----》
 
属性结构如下:
 
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);
};
 
驱动可以通过类似与proc文件系统的简单接口将设备属性导出。(注:导出是在
sys文件系统中的)
 
注: 详情请看以后翻译的 [翻译]  Linux设备驱动模型——sys文件系统
 
设备属性时通过一个叫DEVICE_ATTR的宏来声明的:
 
#define DEVICE_ATTR(name,mode,show,store)
 
用法如下:
 
DEVICE_ATTR(power,0644,show_power,store_power);
 
//=================================================================>>
给出宏的原型:
 
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
 
#define __ATTR(_name,_mode,_show,_store) { \
 .attr = {.name = __stringify(_name), .mode = _mode }, \
 .show = _show,     \
 .store = _store,     \
}
 
#define __stringify(x)  __stringify_1(x)
 
#define __stringify_1(x) #x
<<=================================================================//

上面宏声明了一个叫dev_attr_power的device_attribute结构。可以通过下面接
口向设备目录(即sys/bus/$/devices/$/下)加入和移除
一个属性:
 
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);
 
在sys文件系统中例子文件将会以power命名,拥有0644(-rw-r--r--)的文件权限。
 
警告:

鉴于内核允许设备随时调用device_create_file()和device_remove_file()去创
建和移除属性文件,所以当这些事件发生时,用户空间对获知这些事件的发生有
着严格的期望。当向内核注册一个新的设备时,(内核事件层)会产生一个事件,
去通知用户空间有一个新的设备可用了(这种方式就像udev)。如果,设备属性是
在该设备注册后才创建的,用户空间将不会被通知(注:事件只是在设备注册时产
生),所以,用户空间将永远不会知道这个被创建的新属性。
 
驱动在probe阶段发现一个设备,要为设备公布额外新属性时,上述的情况就显
得尤为重要了。如果驱动只是简单的调用并传递将(通过probe发现的)设备结构
给device_create_file()函数,这样,用户空间将不会知道这个属性文件的创
立。然而,驱动可以在模块初始化函数(modules_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
};
 
之后在模块初始化函数它可能会这样做:
 
 mydriver_class = class_create(THIS_MODULE, "my_attrs");
 mydriver_class.dev_attr = mydriver_attribs;
 
现假设'dev'是需要在probe函数中挂钩的设备结构,驱动的probe函数可能会以
下面的方式去为它创建属性文件:
 
 create_device(&mydriver_class, dev, chrdev, &private_data, "my_name");
 
译者见解:
上面的警告部分,前半部分涉及到内核事件层的知识,等研究到设备驱动模型的
内核对象时在详细研究。后半部分提供了一种为已注册设备创建额外属性的方法。
在一般的设备驱动中,我们喜欢在probe阶段去创建和初始化设备结构,这是,
该设备结构的属性文件创建的uevent事件当然会在注册时被传递。但是,对于板
载设备等特殊设备,上述方法会是一种解决办法。不过本人认为他会被改良(等走
进代码时,我们再证实这一点)。
   
阅读(338) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~