diff options
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
-rw-r--r-- | drivers/i2c/i2c-dev.c | 73 |
1 files changed, 45 insertions, 28 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 5f3a52d517c3..c90ce50b619f 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c | |||
@@ -28,6 +28,8 @@ | |||
28 | 28 | ||
29 | #include <linux/kernel.h> | 29 | #include <linux/kernel.h> |
30 | #include <linux/module.h> | 30 | #include <linux/module.h> |
31 | #include <linux/device.h> | ||
32 | #include <linux/notifier.h> | ||
31 | #include <linux/fs.h> | 33 | #include <linux/fs.h> |
32 | #include <linux/slab.h> | 34 | #include <linux/slab.h> |
33 | #include <linux/init.h> | 35 | #include <linux/init.h> |
@@ -37,16 +39,13 @@ | |||
37 | #include <linux/jiffies.h> | 39 | #include <linux/jiffies.h> |
38 | #include <linux/uaccess.h> | 40 | #include <linux/uaccess.h> |
39 | 41 | ||
40 | static struct i2c_driver i2cdev_driver; | ||
41 | |||
42 | /* | 42 | /* |
43 | * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a | 43 | * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a |
44 | * slave (i2c_client) with which messages will be exchanged. It's coupled | 44 | * slave (i2c_client) with which messages will be exchanged. It's coupled |
45 | * with a character special file which is accessed by user mode drivers. | 45 | * with a character special file which is accessed by user mode drivers. |
46 | * | 46 | * |
47 | * The list of i2c_dev structures is parallel to the i2c_adapter lists | 47 | * The list of i2c_dev structures is parallel to the i2c_adapter lists |
48 | * maintained by the driver model, and is updated using notifications | 48 | * maintained by the driver model, and is updated using bus notifications. |
49 | * delivered to the i2cdev_driver. | ||
50 | */ | 49 | */ |
51 | struct i2c_dev { | 50 | struct i2c_dev { |
52 | struct list_head list; | 51 | struct list_head list; |
@@ -192,13 +191,12 @@ static int i2cdev_check(struct device *dev, void *addrp) | |||
192 | /* walk up mux tree */ | 191 | /* walk up mux tree */ |
193 | static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) | 192 | static int i2cdev_check_mux_parents(struct i2c_adapter *adapter, int addr) |
194 | { | 193 | { |
194 | struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); | ||
195 | int result; | 195 | int result; |
196 | 196 | ||
197 | result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); | 197 | result = device_for_each_child(&adapter->dev, &addr, i2cdev_check); |
198 | 198 | if (!result && parent) | |
199 | if (!result && i2c_parent_is_i2c_adapter(adapter)) | 199 | result = i2cdev_check_mux_parents(parent, addr); |
200 | result = i2cdev_check_mux_parents( | ||
201 | to_i2c_adapter(adapter->dev.parent), addr); | ||
202 | 200 | ||
203 | return result; | 201 | return result; |
204 | } | 202 | } |
@@ -222,11 +220,11 @@ static int i2cdev_check_mux_children(struct device *dev, void *addrp) | |||
222 | driver bound to it, as NOT busy. */ | 220 | driver bound to it, as NOT busy. */ |
223 | static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) | 221 | static int i2cdev_check_addr(struct i2c_adapter *adapter, unsigned int addr) |
224 | { | 222 | { |
223 | struct i2c_adapter *parent = i2c_parent_is_i2c_adapter(adapter); | ||
225 | int result = 0; | 224 | int result = 0; |
226 | 225 | ||
227 | if (i2c_parent_is_i2c_adapter(adapter)) | 226 | if (parent) |
228 | result = i2cdev_check_mux_parents( | 227 | result = i2cdev_check_mux_parents(parent, addr); |
229 | to_i2c_adapter(adapter->dev.parent), addr); | ||
230 | 228 | ||
231 | if (!result) | 229 | if (!result) |
232 | result = device_for_each_child(&adapter->dev, &addr, | 230 | result = device_for_each_child(&adapter->dev, &addr, |
@@ -492,7 +490,6 @@ static int i2cdev_open(struct inode *inode, struct file *file) | |||
492 | return -ENOMEM; | 490 | return -ENOMEM; |
493 | } | 491 | } |
494 | snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); | 492 | snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); |
495 | client->driver = &i2cdev_driver; | ||
496 | 493 | ||
497 | client->adapter = adap; | 494 | client->adapter = adap; |
498 | file->private_data = client; | 495 | file->private_data = client; |
@@ -523,19 +520,18 @@ static const struct file_operations i2cdev_fops = { | |||
523 | 520 | ||
524 | /* ------------------------------------------------------------------------- */ | 521 | /* ------------------------------------------------------------------------- */ |
525 | 522 | ||
526 | /* | ||
527 | * The legacy "i2cdev_driver" is used primarily to get notifications when | ||
528 | * I2C adapters are added or removed, so that each one gets an i2c_dev | ||
529 | * and is thus made available to userspace driver code. | ||
530 | */ | ||
531 | |||
532 | static struct class *i2c_dev_class; | 523 | static struct class *i2c_dev_class; |
533 | 524 | ||
534 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) | 525 | static int i2cdev_attach_adapter(struct device *dev, void *dummy) |
535 | { | 526 | { |
527 | struct i2c_adapter *adap; | ||
536 | struct i2c_dev *i2c_dev; | 528 | struct i2c_dev *i2c_dev; |
537 | int res; | 529 | int res; |
538 | 530 | ||
531 | if (dev->type != &i2c_adapter_type) | ||
532 | return 0; | ||
533 | adap = to_i2c_adapter(dev); | ||
534 | |||
539 | i2c_dev = get_free_i2c_dev(adap); | 535 | i2c_dev = get_free_i2c_dev(adap); |
540 | if (IS_ERR(i2c_dev)) | 536 | if (IS_ERR(i2c_dev)) |
541 | return PTR_ERR(i2c_dev); | 537 | return PTR_ERR(i2c_dev); |
@@ -562,10 +558,15 @@ error: | |||
562 | return res; | 558 | return res; |
563 | } | 559 | } |
564 | 560 | ||
565 | static int i2cdev_detach_adapter(struct i2c_adapter *adap) | 561 | static int i2cdev_detach_adapter(struct device *dev, void *dummy) |
566 | { | 562 | { |
563 | struct i2c_adapter *adap; | ||
567 | struct i2c_dev *i2c_dev; | 564 | struct i2c_dev *i2c_dev; |
568 | 565 | ||
566 | if (dev->type != &i2c_adapter_type) | ||
567 | return 0; | ||
568 | adap = to_i2c_adapter(dev); | ||
569 | |||
569 | i2c_dev = i2c_dev_get_by_minor(adap->nr); | 570 | i2c_dev = i2c_dev_get_by_minor(adap->nr); |
570 | if (!i2c_dev) /* attach_adapter must have failed */ | 571 | if (!i2c_dev) /* attach_adapter must have failed */ |
571 | return 0; | 572 | return 0; |
@@ -578,12 +579,23 @@ static int i2cdev_detach_adapter(struct i2c_adapter *adap) | |||
578 | return 0; | 579 | return 0; |
579 | } | 580 | } |
580 | 581 | ||
581 | static struct i2c_driver i2cdev_driver = { | 582 | int i2cdev_notifier_call(struct notifier_block *nb, unsigned long action, |
582 | .driver = { | 583 | void *data) |
583 | .name = "dev_driver", | 584 | { |
584 | }, | 585 | struct device *dev = data; |
585 | .attach_adapter = i2cdev_attach_adapter, | 586 | |
586 | .detach_adapter = i2cdev_detach_adapter, | 587 | switch (action) { |
588 | case BUS_NOTIFY_ADD_DEVICE: | ||
589 | return i2cdev_attach_adapter(dev, NULL); | ||
590 | case BUS_NOTIFY_DEL_DEVICE: | ||
591 | return i2cdev_detach_adapter(dev, NULL); | ||
592 | } | ||
593 | |||
594 | return 0; | ||
595 | } | ||
596 | |||
597 | static struct notifier_block i2cdev_notifier = { | ||
598 | .notifier_call = i2cdev_notifier_call, | ||
587 | }; | 599 | }; |
588 | 600 | ||
589 | /* ------------------------------------------------------------------------- */ | 601 | /* ------------------------------------------------------------------------- */ |
@@ -608,10 +620,14 @@ static int __init i2c_dev_init(void) | |||
608 | goto out_unreg_chrdev; | 620 | goto out_unreg_chrdev; |
609 | } | 621 | } |
610 | 622 | ||
611 | res = i2c_add_driver(&i2cdev_driver); | 623 | /* Keep track of adapters which will be added or removed later */ |
624 | res = bus_register_notifier(&i2c_bus_type, &i2cdev_notifier); | ||
612 | if (res) | 625 | if (res) |
613 | goto out_unreg_class; | 626 | goto out_unreg_class; |
614 | 627 | ||
628 | /* Bind to already existing adapters right away */ | ||
629 | i2c_for_each_dev(NULL, i2cdev_attach_adapter); | ||
630 | |||
615 | return 0; | 631 | return 0; |
616 | 632 | ||
617 | out_unreg_class: | 633 | out_unreg_class: |
@@ -625,7 +641,8 @@ out: | |||
625 | 641 | ||
626 | static void __exit i2c_dev_exit(void) | 642 | static void __exit i2c_dev_exit(void) |
627 | { | 643 | { |
628 | i2c_del_driver(&i2cdev_driver); | 644 | bus_unregister_notifier(&i2c_bus_type, &i2cdev_notifier); |
645 | i2c_for_each_dev(NULL, i2cdev_detach_adapter); | ||
629 | class_destroy(i2c_dev_class); | 646 | class_destroy(i2c_dev_class); |
630 | unregister_chrdev(I2C_MAJOR, "i2c"); | 647 | unregister_chrdev(I2C_MAJOR, "i2c"); |
631 | } | 648 | } |