/** * System devices follow a slightly different driver model. * They don't need to do dynammic driver binding, can't be probed, * and don't reside on any type of peripheral bus. * So, we represent and treat them a little differently. * * We still have a notion of a driver for a system device, because we still * want to perform basic operations on these devices. * * We also support auxiliary drivers binding to devices of a certain class. * * This allows configurable drivers to register themselves for devices of * a certain type. And, it allows class definitions to reside in generic * code while arch-specific code can register specific drivers. * * Auxiliary drivers registered with a NULL cls are registered as drivers * for all system devices, and get notification calls for each device. */ #ifndef _SYSDEV_H_ #define _SYSDEV_H_ #include <linux/kobject.h> #include <linux/module.h> #include <linux/pm.h> struct sys_device; struct sysdev_class_attribute; struct sysdev_class { const char *name; struct list_head drivers; struct sysdev_class_attribute **attrs; struct kset kset; #ifndef CONFIG_ARCH_NO_SYSDEV_OPS /* Default operations for these types of devices */ int (*shutdown)(struct sys_device *); int (*suspend)(struct sys_device *, pm_message_t state); int (*resume)(struct sys_device *); #endif }; struct sysdev_class_attribute { struct attribute attr; ssize_t (*show)(struct sysdev_class *, struct sysdev_class_attribute *, char *); ssize_t (*store)(struct sysdev_class *, struct sysdev_class_attribute *, const char *, size_t); }; #define _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ { \ .attr = {.name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } #define SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) \ struct sysdev_class_attribute attr_##_name = \ _SYSDEV_CLASS_ATTR(_name,_mode,_show,_store) extern int sysdev_class_register(struct sysdev_class *); extern void sysdev_class_unregister(struct sysdev_class *); extern int sysdev_class_create_file(struct sysdev_class *, struct sysdev_class_attribute *); extern void sysdev_class_remove_file(struct sysdev_class *, struct sysdev_class_attribute *); /** * Auxiliary system device drivers. */ struct sysdev_driver { struct list_head entry; int (*add)(struct sys_device *); int (*remove)(struct sys_device *); #ifndef CONFIG_ARCH_NO_SYSDEV_OPS int (*shutdown)(struct sys_device *); int (*suspend)(struct sys_device *, pm_message_t state); int (*resume)(struct sys_device *); #endif }; extern int sysdev_driver_register(struct sysdev_class *, struct sysdev_driver *); extern void sysdev_driver_unregister(struct sysdev_class *, struct sysdev_driver *); /** * sys_devices can be simplified a lot from regular devices, because they're * simply not as versatile. */ struct sys_device { u32 id; struct sysdev_class * cls; struct kobject kobj; }; extern int sysdev_register(struct sys_device *); extern void sysdev_unregister(struct sys_device *); struct sysdev_attribute { struct attribute attr; ssize_t (*show)(struct sys_device *, struct sysdev_attribute *, char *); ssize_t (*store)(struct sys_device *, struct sysdev_attribute *, const char *, size_t); }; #define _SYSDEV_ATTR(_name, _mode, _show, _store) \ { \ .attr = { .name = __stringify(_name), .mode = _mode }, \ .show = _show, \ .store = _store, \ } #define SYSDEV_ATTR(_name, _mode, _show, _store) \ struct sysdev_attribute attr_##_name = \ _SYSDEV_ATTR(_name, _mode, _show, _store); extern int sysdev_create_file(struct sys_device *, struct sysdev_attribute *); extern void sysdev_remove_file(struct sys_device *, struct sysdev_attribute *); /* Create/remove NULL terminated attribute list */ static inline int sysdev_create_files(struct sys_device *d, struct sysdev_attribute **a) { return sysfs_create_files(&d->kobj, (const struct attribute **)a); } static inline void sysdev_remove_files(struct sys_device *d, struct sysdev_attribute **a) { return sysfs_remove_files(&d->kobj, (const struct attribute **)a); } struct sysdev_ext_attribute { struct sysdev_attribute attr; void *var; }; /* * Support for simple variable sysdev attributes. * The pointer to the variable is stored in a sysdev_ext_attribute */ /* Add more types as needed */ extern ssize_t sysdev_show_ulong(struct sys_device *, struct sysdev_attribute *, char *); extern ssize_t sysdev_store_ulong(struct sys_device *, struct sysdev_attribute *, const char *, size_t); extern ssize_t sysdev_show_int(struct sys_device *, struct sysdev_attribute *, char *); extern ssize_t sysdev_store_int(struct sys_device *, struct sysdev_attribute *, const char *, size_t); #define _SYSDEV_ULONG_ATTR(_name, _mode, _var) \ { _SYSDEV_ATTR(_name, _mode, sysdev_show_ulong, sysdev_store_ulong), \ &(_var) } #define SYSDEV_ULONG_ATTR(_name, _mode, _var) \ struct sysdev_ext_attribute attr_##_name = \ _SYSDEV_ULONG_ATTR(_name, _mode, _var); #define _SYSDEV_INT_ATTR(_name, _mode, _var) \ { _SYSDEV_ATTR(_name, _mode, sysdev_show_int, sysdev_store_int), \ &(_var) } #define SYSDEV_INT_ATTR(_name, _mode, _var) \ struct sysdev_ext_attribute attr_##_name = \ _SYSDEV_INT_ATTR(_name, _mode, _var); #endif /* _SYSDEV_H_ */