sysfs____之用户属性接口(XXX_ATTR)

来源:互联网 发布:c语言小项目 编辑:程序博客网 时间:2024/04/28 05:15


前言

如果你正在开发的设备驱动程序中需要与用户层的接口,一般可选的方法有:

(1)、注册虚拟的字符设备文件,以这个虚拟设备上的 read/write/ioctl 等接口与用户交互;但 read/write 一般只能做一件事情, ioctl 可以根据 cmd 参数做多个功能,但其缺点是很明显的: ioctl 接口无法直接在 Shell 脚本中使用,为了使用 ioctl 的功能,还必须编写配套的 C语言的虚拟设备操作程序, ioctl 的二进制数据接口也是造成大小端问题 (big endian与little endian)、32位/64位不可移植问题的根源; 

(2)、 注册 proc 接口,接受用户的 read/write/ioctl 操作;同样的,一个 proc 项通常使用其 read/write/ioctl 接口,它所存在的问题与上面的虚拟字符设备的的问题相似; 
(3)、注册 sysfs 属性; 
最重要的是,添加虚拟字符设备支持和注册 proc 接口支持这两者所需要增加的代码量都并不少,最好的方法还是使用 sysfs 属性支持,一切在用户层是可见的透明,且增加的代码量是最少的,可维护性也最好;方法就是使用 <include/linux/device.h> 头文件提供的四个宏,分别应用于总线/类别/驱动/设备四种内核数据结构对象上。

#define BUS_ATTR(_name, _mode, _show, _store)    struct bus_attribute bus_attr_##_name = __ATTR(_name, _mode, _show, _store)  #define CLASS_ATTR(_name, _mode, _show, _store)                  struct class_attribute class_attr_##_name = __ATTR(_name, _mode, _show, _store)  #define DRIVER_ATTR(_name, _mode, _show, _store)         struct driver_attribute driver_attr_##_name =                    __ATTR(_name, _mode, _show, _store)  #define DEVICE_ATTR(_name, _mode, _show, _store)  struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
以上常常会用到一下几个基本定义和宏

struct attribute {const char*name;umode_tmode;};struct attribute_group {const char*name;umode_t(*is_visible)(struct kobject *,      struct attribute *, int);struct attribute**attrs;};/** * Use these macros to make defining attributes easier. See include/linux/device.h * for examples.. */#define __ATTR(_name,_mode,_show,_store) { \.attr = {.name = __stringify(_name), .mode = _mode },\.show= _show,\.store= _store,\}#define __ATTR_RO(_name) { \.attr= { .name = __stringify(_name), .mode = 0444 },\.show= _name##_show,\}#define __ATTR_NULL { .attr = { .name = NULL } }#define attr_name(_attr) (_attr).attr.name

四种属性接口

其实常用的就是DEVICE_ATTR,而DRIVER_ATTR用的也不多,另外2个BUS_ATTR和CLASS_ATTR基本不用,是定义好的框架或子系统,作为驱动开发我们一般不会去建立新的框架或系统,在其上开发就行了,比如我们开发input子系统,你开发的属性、设备基本都在/sys/xxx/input/下。

1、DEVICE_ATTR

属性:
/* interface for exporting device attributes */struct device_attribute {struct attributeattr;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)
//此处_name就是属性名,出现在/sys/xxx.../xxx下的属性节点,但是注意对应于device_attribute的真实变量名还要加上前缀dev_attr_,具体查看上面DEVICE_ATTR定义,比如声明为static DEVICE_ATTR(max7359_info, 0664, max7359_info_show, max7359_info_store);它在代码中的属性名实际为dev_attr_max7359_info。_mode是这个节点的操作权限,与普通文件一样,后2个就是属性的读和写函数。
创建:
/** * device_create_file - create sysfs attribute file for device. * @dev: device. * @attr: device attribute descriptor. */int device_create_file(struct device *dev,       const struct device_attribute *attr){int error = 0;if (dev)error = sysfs_create_file(&dev->kobj, &attr->attr);return error;}
//例如 device_create_file(&client->dev, &dev_attr_max7359_info),在client->dev指向的kobject下创建max7359_info节点

释放
/** * device_remove_file - remove sysfs attribute file. * @dev: device. * @attr: device attribute descriptor. */void device_remove_file(struct device *dev,const struct device_attribute *attr){if (dev)sysfs_remove_file(&dev->kobj, &attr->attr);}

2、DRIVER_ATTR

属性
<span style="font-size:18px;">/* sysfs interface for exporting driver attributes */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);};</span>

声明
DRIVER_ATTR(_name, _mode, _show, _store) 

创建
/** * driver_create_file - create sysfs file for driver. * @drv: driver. * @attr: driver attribute descriptor. */int driver_create_file(struct device_driver *drv,       const struct driver_attribute *attr){int error;if (drv)error = sysfs_create_file(&drv->p->kobj, &attr->attr);elseerror = -EINVAL;return error;}

释放
/** * driver_remove_file - remove sysfs file for driver. * @drv: driver. * @attr: driver attribute descriptor. */void driver_remove_file(struct device_driver *drv,const struct driver_attribute *attr){if (drv)sysfs_remove_file(&drv->p->kobj, &attr->attr);}

3、BUS_ATTR

属性
struct bus_attribute {struct attributeattr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);};

声明
BUS_ATTR(_name, _mode, _show, _store)

创建
int bus_create_file(struct bus_type *bus, struct bus_attribute *attr){int error;if (bus_get(bus)) {error = sysfs_create_file(&bus->p->subsys.kobj, &attr->attr);bus_put(bus);} elseerror = -EINVAL;return error;}

释放
void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr){if (bus_get(bus)) {sysfs_remove_file(&bus->p->subsys.kobj, &attr->attr);bus_put(bus);}}

4、CLASS_ATTR

属性
struct class_attribute {struct attribute attr;ssize_t (*show)(struct class *class, struct class_attribute *attr,char *buf);ssize_t (*store)(struct class *class, struct class_attribute *attr,const char *buf, size_t count);const void *(*namespace)(struct class *class, const struct class_attribute *attr);};

声明
CLASS_ATTR(_name, _mode, _show, _store)

创建
int class_create_file(struct class *cls, const struct class_attribute *attr){int error;if (cls)error = sysfs_create_file(&cls->p->subsys.kobj,  &attr->attr);elseerror = -EINVAL;return error;}

释放
void class_remove_file(struct class *cls, const struct class_attribute *attr){if (cls)sysfs_remove_file(&cls->p->subsys.kobj, &attr->attr);}

如果你认真看了,就能注意到,创建和释放调用的都是sysfs_create_file()和sysfs_remove_file(),唯一的区别就是这个attr放在哪个kobject目录下,对于大多数驱动人员来说,跟设备打交道,当然DEVICE_ATTR是用最多啦,其他3中知道这么回事就行!


设备驱动基本数据结构

懒得去翻阅/linux/device.h,贴一些常用的数据结构

1. struct device

<span style="font-size:14px;">/** * struct device - The basic device structure * @parent:The device's "parent" device, the device to which it is attached. * In most cases, a parent device is some sort of bus or host * controller. If parent is NULL, the device, is a top-level device, * which is not usually what you want. * @p:Holds the private data of the driver core portions of the device. * See the comment of the struct device_private for detail. * @kobj:A top-level, abstract class from which other classes are derived. * @init_name:Initial name of the device. * @type:The type of device. * This identifies the device type and carries type-specific * information. * @mutex:Mutex to synchronize calls to its driver. * @bus:Type of bus device is on. * @driver:Which driver has allocated this * @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. * @power:For device power management. * See Documentation/power/devices.txt for details. * @pm_domain:Provide callbacks that are executed during system suspend, * hibernation, system resume and during runtime PM transitions * along with subsystem-level and driver-level callbacks. * @pins:For device pin management. *See Documentation/pinctrl.txt for details. * @numa_node:NUMA node this device is close to. * @dma_mask:Dma mask (if dma'ble device). * @coherent_dma_mask: Like dma_mask, but for alloc_coherent mapping as not all * hardware supports 64-bit addresses for consistent allocations * such descriptors. * @dma_parms:A low level driver may set these to teach IOMMU code about * segment limitations. * @dma_pools:Dma pools (if dma'ble device). * @dma_mem:Internal for coherent mem override. * @archdata:For arch-specific additions. * @of_node:Associated device tree node. * @devt:For creating the sysfs "dev". * @id:device instance * @devres_lock: Spinlock to protect the resource of the device. * @devres_head: The resources list of the device. * @knode_class: The node used to add the device to the class list. * @class:The class of the device. * @groups:Optional attribute groups. * @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). * * At the lowest level, every device in a Linux system is represented by an * instance of struct device. The device structure contains the information * that the device model core needs to model the system. Most subsystems, * however, track additional information about the devices they host. As a * result, it is rare for devices to be represented by bare device structures; * instead, that structure, like kobject structures, is usually embedded within * a higher-level representation of the device. */struct device {struct device*parent;struct device_private*p;struct kobject kobj;const char*init_name; /* initial name of the device */const struct device_type *type;struct mutexmutex;/* mutex 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*platform_data;/* Platform specific data, device   core doesn't touch it */struct dev_pm_infopower;struct dev_pm_domain*pm_domain;#ifdef CONFIG_PINCTRLstruct dev_pin_info*pins;#endif#ifdef CONFIG_NUMAintnuma_node;/* NUMA node this device is close to */#endifu64*dma_mask;/* dma mask (if dma'able device) */u64coherent_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_headdma_pools;/* dma pools (if dma'ble) */struct dma_coherent_mem*dma_mem; /* internal for coherent mem     override */#ifdef CONFIG_CMAstruct cma *cma_area;/* contiguous memory area for dma   allocations */#endif/* arch specific additions */struct dev_archdataarchdata;struct device_node*of_node; /* associated device tree node */dev_tdevt;/* dev_t, creates the sysfs "dev" */u32id;/* device instance */spinlock_tdevres_lock;struct list_headdevres_head;struct klist_nodeknode_class;struct class*class;const struct attribute_group **groups;/* optional groups */void(*release)(struct device *dev);struct iommu_group*iommu_group;};</span>


2. struct device_driver

<span style="font-size:14px;">/** * struct device_driver - The basic device driver structure * @name:Name of the device driver. * @bus:The bus which the device of this driver belongs to. * @owner:The module owner. * @mod_name:Used for built-in modules. * @suppress_bind_attrs: Disables bind/unbind via sysfs. * @of_match_table: The open firmware table. * @probe:Called to query the existence of a specific device, *whether this driver can work with it, and bind the driver *to a specific device. * @remove:Called when the device is removed from the system to *unbind a device from this driver. * @shutdown:Called at shut-down time to quiesce the device. * @suspend:Called to put the device to sleep mode. Usually to a *low power state. * @resume:Called to bring a device from sleep mode. * @groups:Default attributes that get created by the driver core *automatically. * @pm:Power management operations of the device which matched *this driver. * @p:Driver core's private data, no one other than the driver *core can touch this. * * The device driver-model tracks all of the drivers known to the system. * The main reason for this tracking is to enable the driver core to match * up drivers with new devices. Once drivers are known objects within the * system, however, a number of other things become possible. Device drivers * can export information and configuration variables that are independent * of any specific device. */struct device_driver {const char*name;struct bus_type*bus;struct module*owner;const char*mod_name;/* used for built-in modules */bool suppress_bind_attrs;/* disables bind/unbind via sysfs */const struct of_device_id*of_match_table;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);const struct attribute_group **groups;const struct dev_pm_ops *pm;struct driver_private *p;};</span>

struct driver_private

挖你隐私,呵呵!这就是驱动对应的sysfs下的某个driver目录,说白了driver_create_file()就是在其driver目录下建attr

struct driver_private {struct kobject kobj;st<span style="font-size:12px;">ruct klist klist_devices;struct klist_node</span> knode_bus;struct module_kobject *mkobj;struct device_driver *driver;};


3. struct bus_type

<span style="font-size:14px;">/** * struct bus_type - The bus type of the device * * @name:The name of the bus. * @dev_name:Used for subsystems to enumerate devices like ("foo%u", dev->id). * @dev_root:Default device to use as the parent. * @bus_attrs:Default attributes of the bus. * @dev_attrs:Default attributes of the devices on the bus. * @drv_attrs:Default attributes of the device drivers on the bus. * @match:Called, perhaps multiple times, whenever a new device or driver *is added for this bus. It should return a nonzero value if the *given device can be handled by the given driver. * @uevent:Called when a device is added, removed, or a few other things *that generate uevents to add the environment variables. * @probe:Called when a new device or driver add to this bus, and callback *the specific driver's probe to initial the matched device. * @remove:Called when a device removed from this bus. * @shutdown:Called at shut-down time to quiesce the device. * @suspend:Called when a device on this bus wants to go to sleep mode. * @resume:Called to bring a device on this bus out of sleep mode. * @pm:Power management operations of this bus, callback the specific *device driver's pm-ops. * @iommu_ops:  IOMMU specific operations for this bus, used to attach IOMMU *              driver implementations to a bus and allow the driver to do *              bus-specific setup * @p:The private data of the driver core, only the driver core can *touch this. * * A bus is a channel between the processor and one or more devices. For the * purposes of the device model, all devices are connected via a bus, even if * it is an internal, virtual, "platform" bus. Buses can plug into each other. * A USB controller is usually a PCI device, for example. The device model * represents the actual connections between buses and the devices they control. * A bus is represented by the bus_type structure. It contains the name, the * default attributes, the bus' methods, PM operations, and the driver core's * private data. */struct bus_type {const char*name;const char*dev_name;struct device*dev_root;struct bus_attribute*bus_attrs;struct device_attribute*dev_attrs;struct driver_attribute*drv_attrs;int (*match)(struct device *dev, struct device_driver *drv);int (*uevent)(struct device *dev, struct kobj_uevent_env *env);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);const struct dev_pm_ops *pm;struct iommu_ops *iommu_ops;struct subsys_private *p;};</span>

4. struct class

<span style="font-size:14px;">/** * struct class - device classes * @name:Name of the class. * @owner:The module owner. * @class_attrs: Default attributes of this class. * @dev_attrs:Default attributes of the devices belong to the class. * @dev_bin_attrs: Default binary attributes of the devices belong to the class. * @dev_kobj:The kobject that represents this class and links it into the hierarchy. * @dev_uevent:Called when a device is added, removed from this class, or a *few other things that generate uevents to add the environment *variables. * @devnode:Callback to provide the devtmpfs. * @class_release: Called to release this class. * @dev_release: Called to release the device. * @suspend:Used to put the device to sleep mode, usually to a low power *state. * @resume:Used to bring the device from the sleep mode. * @ns_type:Callbacks so sysfs can detemine namespaces. * @namespace:Namespace of the device belongs to this class. * @pm:The default device power management operations of this class. * @p:The private data of the driver core, no one other than the *driver core can touch this. * * A class is a higher-level view of a device that abstracts out low-level * implementation details. Drivers may see a SCSI disk or an ATA disk, but, * at the class level, they are all simply disks. Classes allow user space * to work with devices based on what they do, rather than how they are * connected or how they work. */struct class {const char*name;struct module*owner;struct class_attribute*class_attrs;struct device_attribute*dev_attrs;struct bin_attribute*dev_bin_attrs;struct kobject*dev_kobj;int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);char *(*devnode)(struct device *dev, umode_t *mode);void (*class_release)(struct class *class);void (*dev_release)(struct device *dev);int (*suspend)(struct device *dev, pm_message_t state);int (*resume)(struct device *dev);const struct kobj_ns_type_operations *ns_type;const void *(*namespace)(struct device *dev);const struct dev_pm_ops *pm;struct subsys_private *p;};struct class_dev_iter {struct klist_iterki;const struct device_type*type;};</span>

4.1  struct subsys_private

device,device_driver,bus_type,class都有一个私有成员p,这都是各自私有的,并且其用途由各自框架决定。前2个不讨论,驱动人员经常碰到,后面2个都是用subsys_provate,贴下来,这要与bus_attr和class_attr的创建对比起来看。


<span style="font-size:14px;"> /*** struct subsys_private - structure to hold the private to the driver core portions of the bus_type/class structure. * * @subsys - the struct kset that defines this subsystem * @devices_kset - the subsystem's 'devices' directory * @interfaces - list of subsystem interfaces associated * @mutex - protect the devices, and interfaces lists. * * @drivers_kset - the list of drivers associated * @klist_devices - the klist to iterate over the @devices_kset * @klist_drivers - the klist to iterate over the @drivers_kset * @bus_notifier - the bus notifier list for anything that cares about things *                 on this bus. * @bus - pointer back to the struct bus_type that this structure is associated *        with. * * @glue_dirs - "glue" directory to put in-between the parent device to *              avoid namespace conflicts * @class - pointer back to the struct class that this structure is associated *          with. * * This structure is the one that is the actual kobject allowing struct * bus_type/class to be statically allocated safely.  Nothing outside of the * driver core should ever touch these fields. */struct subsys_private {struct kset subsys;struct kset *devices_kset;struct list_head interfaces;struct mutex mutex;struct kset *drivers_kset;struct klist klist_devices;struct klist klist_drivers;struct blocking_notifier_head bus_notifier;unsigned int drivers_autoprobe:1;struct bus_type *bus;struct kset glue_dirs;struct class *class;};</span>



0 0
原创粉丝点击