Convert i2c adapters from class devices to bus devices
Subsystems (3)
cover Convert i2c adapters from class devices to bus devices
As Kay Sievers explained that i2c adapters should be bus devices rather than class devices, convert them.
[PATCH 1/4] Add support for compatibility classes [PATCH 2/4] i2c: Convert i2c clients to a device type [PATCH 3/4] i2c: Convert i2c adapters to bus devices [PATCH 4/4] i2c: Provide compatibility links for i2c adapters
drivers/base/class.c | 87 +++++++++++++++++++++++++ drivers/i2c/Kconfig | 8 ++ drivers/i2c/i2c-core.c | 165 +++++++++++++++++++++++++++++++++---------------- include/linux/device.h | 8 ++ 4 files changed, 217 insertions(+), 51 deletions(-)
Care has been taken to not break user-space compatibility. I have also updated libsensors and sensors-detect already to look for i2c adapters in /sys/bus/i2c/devices rather than /sys/class/i2c-adapter. I am not aware of other user-space applications or libraries using /sys/class/i2c-adapter, but if you do please tell me.
Improvements are possible on the power management side, but I did not have the time to deal with this now, so this is left for later.
Signature · 1 line
Jean Delvare
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/4 Add support for compatibility classes
From: Kay Sievers <[email protected]>
When turning class devices into bus devices, we may need to temporarily add links in sysfs so that user-space applications are not confused. This is done by adding the following API:
* Functions to register and unregister compatibility classes. These appear in sysfs at the same location as regular classes, but instead of class devices, they contain links to bus devices.
* Functions to create and delete such links. Additionally, the caller can optionally pass a target device to which a "device" link should point (typically that would be the device's parent), to fully emulate the original class device.
The i2c subsystem will be the first user of this API, as i2c adapters are being converted from class devices to bus devices.
--- drivers/base/class.c | 87 ++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 8 ++++ 2 files changed, 95 insertions(+)
drivers/base/class.c +87 -0
@@ -488,6 +488,93 @@ void class_interface_unregister(struct cclass_put (parent); } +struct class_compat { +struct kobject *kobj; +}; + +/** +* class_compat_register - register a compatibility class +* @name: the name of the class +* +* Compatibility classes are meant as a temporary user-space compatibility +* workaround when converting a family of class devices to bus devices. +*/ +struct class_compat *class_compat_register (const char *name) +{ +struct class_compat *cls; + + cls =kmalloc (sizeof (struct class_compat ),GFP_KERNEL ); +if (!cls) +return NULL ; + cls->kobj =kobject_create_and_add (name, &class_kset->kobj); +if (!cls->kobj) { +kfree (cls); +return NULL ; + } +return cls; +} +EXPORT_SYMBOL_GPL (class_compat_register); + +/** +* class_compat_unregister - unregister a compatibility class +* @cls: the class to unregister +*/ +void class_compat_unregister (struct class_compat *cls) +{ +kobject_put (cls->kobj); +kfree (cls); +} +EXPORT_SYMBOL_GPL (class_compat_unregister); + +/** +* class_compat_create_link - create a compatibility class device link to +* a bus device +* @cls: the compatibility class +* @dev: the target bus device +* @device_link: an optional device to which a "device" link should be created +*/ +int class_compat_create_link (struct class_compat *cls,struct device *dev, +struct device *device_link) +{ +int error; + + error =sysfs_create_link (cls->kobj, &dev->kobj,dev_name (dev)); +if (error) +return error; + +/* +* Optionally add a "device" link (typically to the parent), as a +* class device would have one and we want to provide as much +* backwards compatibility as possible. +*/ +if (device_link) { + error =sysfs_create_link (&dev->kobj, &device_link->kobj, +"device" ); +if (error) +sysfs_remove_link (cls->kobj,dev_name (dev)); + } + +return error; +} +EXPORT_SYMBOL_GPL (class_compat_create_link); + +/** +* class_compat_remove_link - remove a compatibility class device link to +* a bus device +* @cls: the compatibility class +* @dev: the target bus device +* @device_link: an optional device to which a "device" link was previously +* created +*/ +void class_compat_remove_link (struct class_compat *cls,struct device *dev, +struct device *device_link) +{ +if (device_link) +sysfs_remove_link (&dev->kobj,"device" ); +sysfs_remove_link (cls->kobj,dev_name (dev)); +} +EXPORT_SYMBOL_GPL (class_compat_remove_link); +int __init classes_init (void ) { class_kset =kset_create_and_add ("class" ,NULL ,NULL );
include/linux/device.h +8 -0
@@ -223,6 +223,14 @@ extern void class_unregister(struct clas__class_register (class, &__key); \ }) +struct class_compat ; +struct class_compat *class_compat_register (const char *name); +void class_compat_unregister (struct class_compat *cls); +int class_compat_create_link (struct class_compat *cls,struct device *dev, +struct device *device_link); +void class_compat_remove_link (struct class_compat *cls,struct device *dev, +struct device *device_link); + extern void class_dev_iter_init(struct class_dev_iter *iter, struct class *class, struct device *start,
Signature · 1 line
Jean Delvare
PATCH 2/4 i2c: Convert i2c clients to a device type
In reply to a message not in the archiveThis is required for upcoming changes.
--- drivers/i2c/i2c-core.c | 67 +++++++++++++++++++++++++++++++++--------------- 1 file changed, 47 insertions(+), 20 deletions(-)
drivers/i2c/i2c-core.c +47 -20
@@ -64,9 +64,13 @@ static const struct i2c_device_id *i2c_mstatic int i2c_device_match (struct device *dev,struct device_driver *drv) { -struct i2c_client *client =to_i2c_client (dev); -struct i2c_driver *driver =to_i2c_driver (drv); +struct i2c_client *client =i2c_verify_client (dev); +struct i2c_driver *driver; + +if (!client) +return 0 ; + driver =to_i2c_driver (drv);/* match on an id table if there is one */ if (driver->id_table)return i2c_match_id (driver->id_table, client) !=NULL ;
@@ -94,10 +98,14 @@ static int i2c_device_uevent(struct devistatic int i2c_device_probe (struct device *dev) { -struct i2c_client *client =to_i2c_client (dev); -struct i2c_driver *driver =to_i2c_driver (dev->driver); +struct i2c_client *client =i2c_verify_client (dev); +struct i2c_driver *driver;int status; +if (!client) +return 0 ; + + driver =to_i2c_driver (dev->driver);if (!driver->probe || !driver->id_table)return -ENODEV ; client->driver = driver;
@@ -114,11 +122,11 @@ static int i2c_device_probe(struct devicstatic int i2c_device_remove (struct device *dev) { -struct i2c_client *client =to_i2c_client (dev); +struct i2c_client *client =i2c_verify_client (dev);struct i2c_driver *driver;int status; -if (!dev->driver) +if (!client || !dev->driver)return 0 ; driver =to_i2c_driver (dev->driver);
@@ -136,37 +144,40 @@ static int i2c_device_remove(struct devistatic void i2c_device_shutdown (struct device *dev) { +struct i2c_client *client =i2c_verify_client (dev);struct i2c_driver *driver; -if (!dev->driver) +if (!client || !dev->driver)return ; driver =to_i2c_driver (dev->driver);if (driver->shutdown) - driver->shutdown (to_i2c_client (dev)); + driver->shutdown (client); }static int i2c_device_suspend (struct device *dev,pm_message_t mesg) { +struct i2c_client *client =i2c_verify_client (dev);struct i2c_driver *driver; -if (!dev->driver) +if (!client || !dev->driver)return 0 ; driver =to_i2c_driver (dev->driver);if (!driver->suspend)return 0 ; -return driver->suspend (to_i2c_client (dev), mesg); +return driver->suspend (client, mesg); }static int i2c_device_resume (struct device *dev) { +struct i2c_client *client =i2c_verify_client (dev);struct i2c_driver *driver; -if (!dev->driver) +if (!client || !dev->driver)return 0 ; driver =to_i2c_driver (dev->driver);if (!driver->resume)return 0 ; -return driver->resume (to_i2c_client (dev)); +return driver->resume (client); }static void i2c_client_dev_release (struct device *dev)
@@ -188,18 +199,28 @@ show_modalias(struct device *dev, structreturn sprintf (buf,"%s%s\n" ,I2C_MODULE_PREFIX , client->name); } -static struct device_attribute i2c_dev_attrs[] = { -__ATTR (name,S_IRUGO , show_client_name,NULL ), +static DEVICE_ATTR(name, S_IRUGO, show_client_name, NULL); +static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL); + +static struct attribute *i2c_dev_attrs[] = { + &dev_attr_name.attr,/* modalias helps coldplug: modprobe $(cat .../modalias) */ -__ATTR (modalias,S_IRUGO , show_modalias,NULL ), - { }, + &dev_attr_modalias.attr, +NULL +}; + +static struct attribute_group i2c_dev_attr_group = { + .attrs = i2c_dev_attrs, +}; + +static struct attribute_group *i2c_dev_attr_groups[] = { + &i2c_dev_attr_group, +NULL };struct bus_type i2c_bus_type = { .name ="i2c" , - .dev_attrs = i2c_dev_attrs, .match = i2c_device_match, - .uevent = i2c_device_uevent, .probe = i2c_device_probe, .remove = i2c_device_remove, .shutdown = i2c_device_shutdown,
@@ -208,6 +229,12 @@ struct bus_type i2c_bus_type = { };EXPORT_SYMBOL_GPL (i2c_bus_type); +static struct device_type i2c_client_type = { + .groups = i2c_dev_attr_groups, + .uevent = i2c_device_uevent, + .release = i2c_client_dev_release, +}; +/** * i2c_verify_client - return parameter as i2c_client, or NULL
@@ -220,7 +247,7 @@ EXPORT_SYMBOL_GPL(i2c_bus_type);*/ struct i2c_client *i2c_verify_client (struct device *dev) { -return (dev->bus == &i2c_bus_type) +return (dev->type == &i2c_client_type) ?to_i2c_client (dev) :NULL ; }
@@ -273,7 +300,7 @@ i2c_new_device(struct i2c_adapter *adap, client->dev.parent = &client->adapter->dev; client->dev.bus = &i2c_bus_type; - client->dev.release = i2c_client_dev_release; + client->dev.type = &i2c_client_type;dev_set_name (&client->dev,"%d-%04x" ,i2c_adapter_id (adap), client->addr);
Signature · 1 line
Jean Delvare
PATCH 3/4 i2c: Convert i2c adapters to bus devices
In reply to a message not in the archiveKay says i2c adapters shouldn't be class devices but bus devices. Convert them that way, using a device type.
--- drivers/i2c/i2c-core.c | 75 +++++++++++++++++++++++++----------------------- 1 file changed, 40 insertions(+), 35 deletions(-)
drivers/i2c/i2c-core.c +40 -35
@@ -46,6 +46,7 @@ static DEFINE_MUTEX(core_lock);static DEFINE_IDR (i2c_adapter_idr);static LIST_HEAD (userspace_devices); +static struct device_type i2c_client_type;static int i2c_check_addr (struct i2c_adapter *adapter,int addr);static int i2c_detect (struct i2c_adapter *adapter,struct i2c_driver *driver);
@@ -186,10 +187,10 @@ static void i2c_client_dev_release(struc }static ssize_t -show_client_name (struct device *dev,struct device_attribute *attr,char *buf) +show_name (struct device *dev,struct device_attribute *attr,char *buf) { -struct i2c_client *client =to_i2c_client (dev); -return sprintf (buf,"%s\n" , client->name); +return sprintf (buf,"%s\n" , dev->type == &i2c_client_type ? +to_i2c_client (dev)->name :to_i2c_adapter (dev)->name); } static ssize_t
@@ -199,7 +200,7 @@ show_modalias(struct device *dev, structreturn sprintf (buf,"%s%s\n" ,I2C_MODULE_PREFIX , client->name); } -static DEVICE_ATTR(name, S_IRUGO, show_client_name, NULL); +static DEVICE_ATTR(name, S_IRUGO, show_name, NULL); static DEVICE_ATTR(modalias, S_IRUGO, show_modalias, NULL);static struct attribute *i2c_dev_attrs[] = {
@@ -395,13 +396,6 @@ static void i2c_adapter_dev_release(strucomplete (&adap->dev_released); } -static ssize_t -show_adapter_name (struct device *dev,struct device_attribute *attr,char *buf) -{ -struct i2c_adapter *adap =to_i2c_adapter (dev); -return sprintf (buf,"%s\n" , adap->name); -} -/* * Let users instantiate I2C devices through sysfs. This can be used when * platform initialization code doesn't contain the proper data for
@@ -520,17 +514,28 @@ i2c_sysfs_delete_device(struct device *dreturn res; } -static struct device_attribute i2c_adapter_attrs[] = { -__ATTR (name,S_IRUGO , show_adapter_name,NULL ), -__ATTR (new_device,S_IWUSR ,NULL , i2c_sysfs_new_device), -__ATTR (delete_device,S_IWUSR ,NULL , i2c_sysfs_delete_device), - { }, +static DEVICE_ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device); +static DEVICE_ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device); + +static struct attribute *i2c_adapter_attrs[] = { + &dev_attr_name.attr, + &dev_attr_new_device.attr, + &dev_attr_delete_device.attr, +NULL +}; + +static struct attribute_group i2c_adapter_attr_group = { + .attrs = i2c_adapter_attrs, +}; + +static struct attribute_group *i2c_adapter_attr_groups[] = { + &i2c_adapter_attr_group, +NULL }; -static struct class i2c_adapter_class = { - .owner =THIS_MODULE , - .name ="i2c-adapter" , - .dev_attrs = i2c_adapter_attrs, +static struct device_type i2c_adapter_type = { + .groups = i2c_adapter_attr_groups, + .release = i2c_adapter_dev_release, };static void i2c_scan_static_board_info (struct i2c_adapter *adapter)
@@ -582,8 +587,8 @@ static int i2c_register_adapter(struct i adap->timeout =HZ ;dev_set_name (&adap->dev,"i2c-%d" , adap->nr); - adap->dev.release = &i2c_adapter_dev_release; - adap->dev.class = &i2c_adapter_class; + adap->dev.bus = &i2c_bus_type; + adap->dev.type = &i2c_adapter_type; res =device_register (&adap->dev);if (res) goto out_list;
@@ -795,9 +800,13 @@ EXPORT_SYMBOL(i2c_del_adapter);static int __attach_adapter (struct device *dev,void *data) { -struct i2c_adapter *adapter =to_i2c_adapter (dev); +struct i2c_adapter *adapter;struct i2c_driver *driver = data; +if (dev->type != &i2c_adapter_type) +return 0 ; + adapter =to_i2c_adapter (dev); +i2c_detect (adapter, driver);/* Legacy drivers scan i2c busses directly */
@@ -836,8 +845,7 @@ int i2c_register_driver(struct module *oINIT_LIST_HEAD (&driver->clients);/* Walk the adapters that are already present */ mutex_lock (&core_lock); -class_for_each_device (&i2c_adapter_class,NULL , driver, - __attach_adapter); +bus_for_each_dev (&i2c_bus_type,NULL , driver, __attach_adapter);mutex_unlock (&core_lock);return 0 ;
@@ -846,10 +854,14 @@ EXPORT_SYMBOL(i2c_register_driver);static int __detach_adapter (struct device *dev,void *data) { -struct i2c_adapter *adapter =to_i2c_adapter (dev); +struct i2c_adapter *adapter;struct i2c_driver *driver = data;struct i2c_client *client, *_n; +if (dev->type != &i2c_adapter_type) +return 0 ; + adapter =to_i2c_adapter (dev); +/* Remove the devices we created ourselves as the result of hardware * probing (using a driver's detect method) */ list_for_each_entry_safe (client, _n, &driver->clients, detected) {
@@ -877,8 +889,7 @@ static int __detach_adapter(struct devicvoid i2c_del_driver (struct i2c_driver *driver) {mutex_lock (&core_lock); -class_for_each_device (&i2c_adapter_class,NULL , driver, - __detach_adapter); +bus_for_each_dev (&i2c_bus_type,NULL , driver, __detach_adapter);mutex_unlock (&core_lock);driver_unregister (&driver->driver);
@@ -967,16 +978,11 @@ static int __init i2c_init(void) retval =bus_register (&i2c_bus_type);if (retval)return retval; - retval =class_register (&i2c_adapter_class); -if (retval) - goto bus_err; retval =i2c_add_driver (&dummy_driver);if (retval) - goto class_err; + goto bus_err;return 0 ; -class_err: -class_unregister (&i2c_adapter_class); bus_err:bus_unregister (&i2c_bus_type);return retval;
@@ -985,7 +991,6 @@ bus_err: static void __exit i2c_exit(void) {i2c_del_driver (&dummy_driver); -class_unregister (&i2c_adapter_class);bus_unregister (&i2c_bus_type); }
Signature · 1 line
Jean Delvare
PATCH 4/4 i2c: Provide compatibility links for i2c adapters
In reply to a message not in the archiveSome user-space applications may be relying on i2c adapters showing up as class devices in sysfs. Provide compatibility links for them for the time being. We will remove them after a long transition period.
--- drivers/i2c/Kconfig | 8 ++++++++ drivers/i2c/i2c-core.c | 33 ++++++++++++++++++++++++++++++++- 2 files changed, 40 insertions(+), 1 deletion(-)
drivers/i2c/Kconfig +8 -0
@@ -27,6 +27,14 @@ config I2C_BOARDINFO boolean default y +config I2C_COMPAT + boolean "Enable compatibility bits for old user-space" + default y + help + Say Y here if you intend to run lm-sensors 3.1.1 or older, or any + other user-space package which expects i2c adapters to be class + devices. If you don't know, say Y. + config I2C_CHARDEV tristate "I2C device interface" help
drivers/i2c/i2c-core.c +32 -1
@@ -538,6 +538,10 @@ static struct device_type i2c_adapter_ty .release = i2c_adapter_dev_release, }; +#ifdef CONFIG_I2C_COMPAT +static struct class_compat *i2c_adapter_compat_class; +#endif +static void i2c_scan_static_board_info (struct i2c_adapter *adapter) {struct i2c_devinfo *devinfo;
@@ -595,6 +599,14 @@ static int i2c_register_adapter(struct idev_dbg (&adap->dev,"adapter [%s] registered\n" , adap->name); +#ifdef CONFIG_I2C_COMPAT + res =class_compat_create_link (i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); +if (res) +dev_warn (&adap->dev, +"Failed to create compatibility class link\n" ); +#endif +/* create pre-declared device nodes */ if (adap->nr < __i2c_first_dynamic_bus_num)i2c_scan_static_board_info (adap);
@@ -773,6 +785,11 @@ int i2c_del_adapter(struct i2c_adapter *checking the returned value. */ res =device_for_each_child (&adap->dev,NULL , __unregister_client); +#ifdef CONFIG_I2C_COMPAT +class_compat_remove_link (i2c_adapter_compat_class, &adap->dev, + adap->dev.parent); +#endif +/* clean up the sysfs representation */ init_completion (&adap->dev_released);device_unregister (&adap->dev);
@@ -978,12 +995,23 @@ static int __init i2c_init(void) retval =bus_register (&i2c_bus_type);if (retval)return retval; +#ifdef CONFIG_I2C_COMPAT + i2c_adapter_compat_class =class_compat_register ("i2c-adapter" ); +if (!i2c_adapter_compat_class) { + retval = -ENOMEM ; + goto bus_err; + } +#endif retval =i2c_add_driver (&dummy_driver);if (retval) - goto bus_err; + goto class_err;return 0 ; +class_err: +#ifdef CONFIG_I2C_COMPAT +class_compat_unregister (i2c_adapter_compat_class ); bus_err: +#endifbus_unregister (&i2c_bus_type);return retval; }
@@ -991,6 +1019,9 @@ bus_err: static void __exit i2c_exit(void) {i2c_del_driver (&dummy_driver); +#ifdef CONFIG_I2C_COMPAT +class_compat_unregister (i2c_adapter_compat_class); +#endif bus_unregister (&i2c_bus_type); }
Signature · 1 line
Jean Delvare