aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/i2c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/i2c')
-rw-r--r--drivers/i2c/i2c-core.c123
1 files changed, 122 insertions, 1 deletions
diff --git a/drivers/i2c/i2c-core.c b/drivers/i2c/i2c-core.c
index a2f1cd3766f3..eb084fa0df83 100644
--- a/drivers/i2c/i2c-core.c
+++ b/drivers/i2c/i2c-core.c
@@ -38,11 +38,12 @@
38#include "i2c-core.h" 38#include "i2c-core.h"
39 39
40 40
41/* core_lock protects i2c_adapter_idr, and guarantees 41/* core_lock protects i2c_adapter_idr, userspace_devices, and guarantees
42 that device detection, deletion of detected devices, and attach_adapter 42 that device detection, deletion of detected devices, and attach_adapter
43 and detach_adapter calls are serialized */ 43 and detach_adapter calls are serialized */
44static DEFINE_MUTEX(core_lock); 44static DEFINE_MUTEX(core_lock);
45static DEFINE_IDR(i2c_adapter_idr); 45static DEFINE_IDR(i2c_adapter_idr);
46static LIST_HEAD(userspace_devices);
46 47
47static int i2c_check_addr(struct i2c_adapter *adapter, int addr); 48static int i2c_check_addr(struct i2c_adapter *adapter, int addr);
48static int i2c_detect(struct i2c_adapter *adapter, struct i2c_driver *driver); 49static 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)
373 return sprintf(buf, "%s\n", adap->name); 374 return sprintf(buf, "%s\n", adap->name);
374} 375}
375 376
377/*
378 * Let users instantiate I2C devices through sysfs. This can be used when
379 * platform initialization code doesn't contain the proper data for
380 * whatever reason. Also useful for drivers that do device detection and
381 * detection fails, either because the device uses an unexpected address,
382 * or this is a compatible device with different ID register values.
383 *
384 * Parameter checking may look overzealous, but we really don't want
385 * the user to provide incorrect parameters.
386 */
387static ssize_t
388i2c_sysfs_new_device(struct device *dev, struct device_attribute *attr,
389 const char *buf, size_t count)
390{
391 struct i2c_adapter *adap = to_i2c_adapter(dev);
392 struct i2c_board_info info;
393 struct i2c_client *client;
394 char *blank, end;
395 int res;
396
397 dev_warn(dev, "The new_device interface is still experimental "
398 "and may change in a near future\n");
399 memset(&info, 0, sizeof(struct i2c_board_info));
400
401 blank = strchr(buf, ' ');
402 if (!blank) {
403 dev_err(dev, "%s: Missing parameters\n", "new_device");
404 return -EINVAL;
405 }
406 if (blank - buf > I2C_NAME_SIZE - 1) {
407 dev_err(dev, "%s: Invalid device name\n", "new_device");
408 return -EINVAL;
409 }
410 memcpy(info.type, buf, blank - buf);
411
412 /* Parse remaining parameters, reject extra parameters */
413 res = sscanf(++blank, "%hi%c", &info.addr, &end);
414 if (res < 1) {
415 dev_err(dev, "%s: Can't parse I2C address\n", "new_device");
416 return -EINVAL;
417 }
418 if (res > 1 && end != '\n') {
419 dev_err(dev, "%s: Extra parameters\n", "new_device");
420 return -EINVAL;
421 }
422
423 if (info.addr < 0x03 || info.addr > 0x77) {
424 dev_err(dev, "%s: Invalid I2C address 0x%hx\n", "new_device",
425 info.addr);
426 return -EINVAL;
427 }
428
429 client = i2c_new_device(adap, &info);
430 if (!client)
431 return -EEXIST;
432
433 /* Keep track of the added device */
434 mutex_lock(&core_lock);
435 list_add_tail(&client->detected, &userspace_devices);
436 mutex_unlock(&core_lock);
437 dev_info(dev, "%s: Instantiated device %s at 0x%02hx\n", "new_device",
438 info.type, info.addr);
439
440 return count;
441}
442
443/*
444 * And of course let the users delete the devices they instantiated, if
445 * they got it wrong. This interface can only be used to delete devices
446 * instantiated by i2c_sysfs_new_device above. This guarantees that we
447 * don't delete devices to which some kernel code still has references.
448 *
449 * Parameter checking may look overzealous, but we really don't want
450 * the user to delete the wrong device.
451 */
452static ssize_t
453i2c_sysfs_delete_device(struct device *dev, struct device_attribute *attr,
454 const char *buf, size_t count)
455{
456 struct i2c_adapter *adap = to_i2c_adapter(dev);
457 struct i2c_client *client, *next;
458 unsigned short addr;
459 char end;
460 int res;
461
462 /* Parse parameters, reject extra parameters */
463 res = sscanf(buf, "%hi%c", &addr, &end);
464 if (res < 1) {
465 dev_err(dev, "%s: Can't parse I2C address\n", "delete_device");
466 return -EINVAL;
467 }
468 if (res > 1 && end != '\n') {
469 dev_err(dev, "%s: Extra parameters\n", "delete_device");
470 return -EINVAL;
471 }
472
473 /* Make sure the device was added through sysfs */
474 res = -ENOENT;
475 mutex_lock(&core_lock);
476 list_for_each_entry_safe(client, next, &userspace_devices, detected) {
477 if (client->addr == addr && client->adapter == adap) {
478 dev_info(dev, "%s: Deleting device %s at 0x%02hx\n",
479 "delete_device", client->name, client->addr);
480
481 list_del(&client->detected);
482 i2c_unregister_device(client);
483 res = count;
484 break;
485 }
486 }
487 mutex_unlock(&core_lock);
488
489 if (res < 0)
490 dev_err(dev, "%s: Can't find device in list\n",
491 "delete_device");
492 return res;
493}
494
376static struct device_attribute i2c_adapter_attrs[] = { 495static struct device_attribute i2c_adapter_attrs[] = {
377 __ATTR(name, S_IRUGO, show_adapter_name, NULL), 496 __ATTR(name, S_IRUGO, show_adapter_name, NULL),
497 __ATTR(new_device, S_IWUSR, NULL, i2c_sysfs_new_device),
498 __ATTR(delete_device, S_IWUSR, NULL, i2c_sysfs_delete_device),
378 { }, 499 { },
379}; 500};
380 501