diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
| -rw-r--r-- | drivers/i2c/i2c-dev.c | 109 |
1 files changed, 50 insertions, 59 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 58ccddd5c237..3f869033ed70 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c | |||
| @@ -32,43 +32,35 @@ | |||
| 32 | #include <linux/slab.h> | 32 | #include <linux/slab.h> |
| 33 | #include <linux/smp_lock.h> | 33 | #include <linux/smp_lock.h> |
| 34 | #include <linux/init.h> | 34 | #include <linux/init.h> |
| 35 | #include <linux/list.h> | ||
| 35 | #include <linux/i2c.h> | 36 | #include <linux/i2c.h> |
| 36 | #include <linux/i2c-dev.h> | 37 | #include <linux/i2c-dev.h> |
| 37 | #include <linux/platform_device.h> | ||
| 38 | #include <asm/uaccess.h> | 38 | #include <asm/uaccess.h> |
| 39 | 39 | ||
| 40 | static struct i2c_client i2cdev_client_template; | 40 | static struct i2c_driver i2cdev_driver; |
| 41 | 41 | ||
| 42 | struct i2c_dev { | 42 | struct i2c_dev { |
| 43 | int minor; | 43 | struct list_head list; |
| 44 | struct i2c_adapter *adap; | 44 | struct i2c_adapter *adap; |
| 45 | struct class_device *class_dev; | 45 | struct class_device *class_dev; |
| 46 | }; | 46 | }; |
| 47 | #define to_i2c_dev(d) container_of(d, struct i2c_dev, class_dev) | ||
| 48 | 47 | ||
| 49 | #define I2C_MINORS 256 | 48 | #define I2C_MINORS 256 |
| 50 | static struct i2c_dev *i2c_dev_array[I2C_MINORS]; | 49 | static LIST_HEAD(i2c_dev_list); |
| 51 | static DEFINE_SPINLOCK(i2c_dev_array_lock); | 50 | static DEFINE_SPINLOCK(i2c_dev_list_lock); |
| 52 | 51 | ||
| 53 | static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) | 52 | static struct i2c_dev *i2c_dev_get_by_minor(unsigned index) |
| 54 | { | 53 | { |
| 55 | struct i2c_dev *i2c_dev; | 54 | struct i2c_dev *i2c_dev; |
| 56 | 55 | ||
| 57 | spin_lock(&i2c_dev_array_lock); | 56 | spin_lock(&i2c_dev_list_lock); |
| 58 | i2c_dev = i2c_dev_array[index]; | 57 | list_for_each_entry(i2c_dev, &i2c_dev_list, list) { |
| 59 | spin_unlock(&i2c_dev_array_lock); | 58 | if (i2c_dev->adap->nr == index) |
| 60 | return i2c_dev; | 59 | goto found; |
| 61 | } | 60 | } |
| 62 | 61 | i2c_dev = NULL; | |
| 63 | static struct i2c_dev *i2c_dev_get_by_adapter(struct i2c_adapter *adap) | 62 | found: |
| 64 | { | 63 | spin_unlock(&i2c_dev_list_lock); |
| 65 | struct i2c_dev *i2c_dev = NULL; | ||
| 66 | |||
| 67 | spin_lock(&i2c_dev_array_lock); | ||
| 68 | if ((i2c_dev_array[adap->nr]) && | ||
| 69 | (i2c_dev_array[adap->nr]->adap == adap)) | ||
| 70 | i2c_dev = i2c_dev_array[adap->nr]; | ||
| 71 | spin_unlock(&i2c_dev_array_lock); | ||
| 72 | return i2c_dev; | 64 | return i2c_dev; |
| 73 | } | 65 | } |
| 74 | 66 | ||
| @@ -76,30 +68,28 @@ static struct i2c_dev *get_free_i2c_dev(struct i2c_adapter *adap) | |||
| 76 | { | 68 | { |
| 77 | struct i2c_dev *i2c_dev; | 69 | struct i2c_dev *i2c_dev; |
| 78 | 70 | ||
| 71 | if (adap->nr >= I2C_MINORS) { | ||
| 72 | printk(KERN_ERR "i2c-dev: Out of device minors (%d)\n", | ||
| 73 | adap->nr); | ||
| 74 | return ERR_PTR(-ENODEV); | ||
| 75 | } | ||
| 76 | |||
| 79 | i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); | 77 | i2c_dev = kzalloc(sizeof(*i2c_dev), GFP_KERNEL); |
| 80 | if (!i2c_dev) | 78 | if (!i2c_dev) |
| 81 | return ERR_PTR(-ENOMEM); | 79 | return ERR_PTR(-ENOMEM); |
| 80 | i2c_dev->adap = adap; | ||
| 82 | 81 | ||
| 83 | spin_lock(&i2c_dev_array_lock); | 82 | spin_lock(&i2c_dev_list_lock); |
| 84 | if (i2c_dev_array[adap->nr]) { | 83 | list_add_tail(&i2c_dev->list, &i2c_dev_list); |
| 85 | spin_unlock(&i2c_dev_array_lock); | 84 | spin_unlock(&i2c_dev_list_lock); |
| 86 | dev_err(&adap->dev, "i2c-dev already has a device assigned to this adapter\n"); | ||
| 87 | goto error; | ||
| 88 | } | ||
| 89 | i2c_dev->minor = adap->nr; | ||
| 90 | i2c_dev_array[adap->nr] = i2c_dev; | ||
| 91 | spin_unlock(&i2c_dev_array_lock); | ||
| 92 | return i2c_dev; | 85 | return i2c_dev; |
| 93 | error: | ||
| 94 | kfree(i2c_dev); | ||
| 95 | return ERR_PTR(-ENODEV); | ||
| 96 | } | 86 | } |
| 97 | 87 | ||
| 98 | static void return_i2c_dev(struct i2c_dev *i2c_dev) | 88 | static void return_i2c_dev(struct i2c_dev *i2c_dev) |
| 99 | { | 89 | { |
| 100 | spin_lock(&i2c_dev_array_lock); | 90 | spin_lock(&i2c_dev_list_lock); |
| 101 | i2c_dev_array[i2c_dev->minor] = NULL; | 91 | list_del(&i2c_dev->list); |
| 102 | spin_unlock(&i2c_dev_array_lock); | 92 | spin_unlock(&i2c_dev_list_lock); |
| 103 | } | 93 | } |
| 104 | 94 | ||
| 105 | static ssize_t show_adapter_name(struct class_device *class_dev, char *buf) | 95 | static ssize_t show_adapter_name(struct class_device *class_dev, char *buf) |
| @@ -375,12 +365,13 @@ static int i2cdev_open(struct inode *inode, struct file *file) | |||
| 375 | if (!adap) | 365 | if (!adap) |
| 376 | return -ENODEV; | 366 | return -ENODEV; |
| 377 | 367 | ||
| 378 | client = kmalloc(sizeof(*client), GFP_KERNEL); | 368 | client = kzalloc(sizeof(*client), GFP_KERNEL); |
| 379 | if (!client) { | 369 | if (!client) { |
| 380 | i2c_put_adapter(adap); | 370 | i2c_put_adapter(adap); |
| 381 | return -ENOMEM; | 371 | return -ENOMEM; |
| 382 | } | 372 | } |
| 383 | memcpy(client, &i2cdev_client_template, sizeof(*client)); | 373 | snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); |
| 374 | client->driver = &i2cdev_driver; | ||
| 384 | 375 | ||
| 385 | /* registered with adapter, passed as client to user */ | 376 | /* registered with adapter, passed as client to user */ |
| 386 | client->adapter = adap; | 377 | client->adapter = adap; |
| @@ -415,41 +406,47 @@ static struct class *i2c_dev_class; | |||
| 415 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) | 406 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) |
| 416 | { | 407 | { |
| 417 | struct i2c_dev *i2c_dev; | 408 | struct i2c_dev *i2c_dev; |
| 418 | struct device *dev; | 409 | int res; |
| 419 | 410 | ||
| 420 | i2c_dev = get_free_i2c_dev(adap); | 411 | i2c_dev = get_free_i2c_dev(adap); |
| 421 | if (IS_ERR(i2c_dev)) | 412 | if (IS_ERR(i2c_dev)) |
| 422 | return PTR_ERR(i2c_dev); | 413 | return PTR_ERR(i2c_dev); |
| 423 | 414 | ||
| 424 | pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", | ||
| 425 | adap->name, i2c_dev->minor); | ||
| 426 | |||
| 427 | /* register this i2c device with the driver core */ | 415 | /* register this i2c device with the driver core */ |
| 428 | i2c_dev->adap = adap; | ||
| 429 | dev = &adap->dev; | ||
| 430 | i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL, | 416 | i2c_dev->class_dev = class_device_create(i2c_dev_class, NULL, |
| 431 | MKDEV(I2C_MAJOR, i2c_dev->minor), | 417 | MKDEV(I2C_MAJOR, adap->nr), |
| 432 | dev, "i2c-%d", i2c_dev->minor); | 418 | &adap->dev, "i2c-%d", |
| 433 | if (!i2c_dev->class_dev) | 419 | adap->nr); |
| 420 | if (!i2c_dev->class_dev) { | ||
| 421 | res = -ENODEV; | ||
| 434 | goto error; | 422 | goto error; |
| 435 | class_device_create_file(i2c_dev->class_dev, &class_device_attr_name); | 423 | } |
| 424 | res = class_device_create_file(i2c_dev->class_dev, &class_device_attr_name); | ||
| 425 | if (res) | ||
| 426 | goto error_destroy; | ||
| 427 | |||
| 428 | pr_debug("i2c-dev: adapter [%s] registered as minor %d\n", | ||
| 429 | adap->name, adap->nr); | ||
| 436 | return 0; | 430 | return 0; |
| 431 | error_destroy: | ||
| 432 | class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); | ||
| 437 | error: | 433 | error: |
| 438 | return_i2c_dev(i2c_dev); | 434 | return_i2c_dev(i2c_dev); |
| 439 | kfree(i2c_dev); | 435 | kfree(i2c_dev); |
| 440 | return -ENODEV; | 436 | return res; |
| 441 | } | 437 | } |
| 442 | 438 | ||
| 443 | static int i2cdev_detach_adapter(struct i2c_adapter *adap) | 439 | static int i2cdev_detach_adapter(struct i2c_adapter *adap) |
| 444 | { | 440 | { |
| 445 | struct i2c_dev *i2c_dev; | 441 | struct i2c_dev *i2c_dev; |
| 446 | 442 | ||
| 447 | i2c_dev = i2c_dev_get_by_adapter(adap); | 443 | i2c_dev = i2c_dev_get_by_minor(adap->nr); |
| 448 | if (!i2c_dev) | 444 | if (!i2c_dev) /* attach_adapter must have failed */ |
| 449 | return -ENODEV; | 445 | return 0; |
| 450 | 446 | ||
| 447 | class_device_remove_file(i2c_dev->class_dev, &class_device_attr_name); | ||
| 451 | return_i2c_dev(i2c_dev); | 448 | return_i2c_dev(i2c_dev); |
| 452 | class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, i2c_dev->minor)); | 449 | class_device_destroy(i2c_dev_class, MKDEV(I2C_MAJOR, adap->nr)); |
| 453 | kfree(i2c_dev); | 450 | kfree(i2c_dev); |
| 454 | 451 | ||
| 455 | pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); | 452 | pr_debug("i2c-dev: adapter [%s] unregistered\n", adap->name); |
| @@ -471,12 +468,6 @@ static struct i2c_driver i2cdev_driver = { | |||
| 471 | .detach_client = i2cdev_detach_client, | 468 | .detach_client = i2cdev_detach_client, |
| 472 | }; | 469 | }; |
| 473 | 470 | ||
| 474 | static struct i2c_client i2cdev_client_template = { | ||
| 475 | .name = "I2C /dev entry", | ||
| 476 | .addr = -1, | ||
| 477 | .driver = &i2cdev_driver, | ||
| 478 | }; | ||
| 479 | |||
| 480 | static int __init i2c_dev_init(void) | 471 | static int __init i2c_dev_init(void) |
| 481 | { | 472 | { |
| 482 | int res; | 473 | int res; |
