i2c: Add a sysfs interface to instantiate devices

Add a sysfs interface to instantiate and delete I2C devices. This is
primarily a replacement of the force_* module parameters implemented
by some i2c drivers. These module parameters were implemented
internally by the I2C_CLIENT_INSMOD* macros, which don't scale well.

This can also be used when developing a driver on a self-soldered
board which doesn't yet have proper I2C device declaration at the
platform level, and presumably for various debugging situations.

Signed-off-by: Jean Delvare <khali@linux-fr.org>
Cc: David Brownell <dbrownell@users.sourceforge.net>
This commit is contained in:
Jean Delvare
2009-06-19 16:58:20 +02:00
parent 35fc37f818
commit 99cd8e2587
3 changed files with 168 additions and 2 deletions

View File

@@ -38,11 +38,12 @@
#include "i2c-core.h"
/* core_lock protects i2c_adapter_idr, and guarantees
/* core_lock protects i2c_adapter_idr, userspace_devices, and guarantees
that device detection, deletion of detected devices, and attach_adapter
and detach_adapter calls are serialized */
static DEFINE_MUTEX(core_lock);
static DEFINE_IDR(i2c_adapter_idr);
static LIST_HEAD(userspace_devices);
static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver);
@@ -373,8 +374,128 @@ show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
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
* whatever reason. Also useful for drivers that do device detection and
* detection fails, either because the device uses an unexpected address,
* or this is a compatible device with different ID register values.
*
* Parameter checking may look overzealous, but we really don't want
* the user to provide incorrect parameters.
*/
static ssize_t
i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_board_info info;
struct i2c_client *client;
char *blank, end;
int res;
dev_warn(dev, "The new_device interface is still experimental "
"and may change in a near future\n");
memset(&info, 0, sizeof(struct i2c_board_info));
blank = strchr(buf, ' ');
if (!blank) {
dev_err(dev, "%s: Missing parameters\n", "new_device");
return -EINVAL;
}
if (blank - buf > I2C_NAME_SIZE - 1) {
dev_err(dev, "%s: Invalid device name\n", "new_device");
return -EINVAL;
}
memcpy(info.type, buf, blank - buf);
/* Parse remaining parameters, reject extra parameters */
res = sscanf(++blank, "%hi%c", &info.addr, &end);
if (res < 1) {
dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
return -EINVAL;
}
if (res > 1 && end != '\n') {
dev_err(dev, "%s: Extra parameters\n", "new_device");
return -EINVAL;
}
if (info.addr < 0x03 || info.addr > 0x77) {
dev_err(dev, "%s: Invalid I2C address 0x%hx\n", "new_device",
info.addr);
return -EINVAL;
}
client = i2c_new_device(adap, &info);
if (!client)
return -EEXIST;
/* Keep track of the added device */
mutex_lock(&core_lock);
list_add_tail(&client->detected, &userspace_devices);
mutex_unlock(&core_lock);
dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
info.type, info.addr);
return count;
}
/*
* And of course let the users delete the devices they instantiated, if
* they got it wrong. This interface can only be used to delete devices
* instantiated by i2c_sysfs_new_device above. This guarantees that we
* don't delete devices to which some kernel code still has references.
*
* Parameter checking may look overzealous, but we really don't want
* the user to delete the wrong device.
*/
static ssize_t
i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
struct i2c_adapter *adap = to_i2c_adapter(dev);
struct i2c_client *client, *next;
unsigned short addr;
char end;
int res;
/* Parse parameters, reject extra parameters */
res = sscanf(buf, "%hi%c", &addr, &end);
if (res < 1) {
dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
return -EINVAL;
}
if (res > 1 && end != '\n') {
dev_err(dev, "%s: Extra parameters\n", "delete_device");
return -EINVAL;
}
/* Make sure the device was added through sysfs */
res = -ENOENT;
mutex_lock(&core_lock);
list_for_each_entry_safe(client, next, &userspace_devices, detected) {
if (client->addr == addr && client->adapter == adap) {
dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
"delete_device", client->name, client->addr);
list_del(&client->detected);
i2c_unregister_device(client);
res = count;
break;
}
}
mutex_unlock(&core_lock);
if (res < 0)
dev_err(dev, "%s: Can't find device in list\n",
"delete_device");
return 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),
{ },
};