diff options
author | David Brownell <david-b@pacbell.net> | 2007-11-15 13:24:01 -0500 |
---|---|---|
committer | Jean Delvare <khali@hyperion.delvare> | 2007-11-15 13:24:01 -0500 |
commit | 907135aaa0cc120a347222c8f274ecc5ca0db641 (patch) | |
tree | 0572c3fc649030ffee737907228a9bfb6094a63a /drivers/i2c/i2c-dev.c | |
parent | 99fee6d7e5748d96884667a4628118f7fc130ea0 (diff) |
i2c-dev: "how does it work" comments
This adds some "how does this work" comments to the i2c-dev driver,
plus separators between the three main components:
- The parallel list of i2c_adapters ("i2c_dev_list"), each of which
gets a "struct i2c_dev" and a /dev/i2c-X character special file.
- An i2cdev_driver gets adapter add/remove notifications, which are
used to maintain that list of adapters.
- Special file operations, which let userspace talk either directly to
the adapter (for i2c_msg operations) or through cached addressing info
using an anonymous i2c_client (never registered anywhere).
Plus there's the usual module load/unload record keeping.
After making sense of this code, I think that the anonymous i2c_client
is pretty shady. But since it's never registered, using this code with
a system set up for "new style" I2C drivers is no more complicated than
always using the I2C_SLAVE_FORCE ioctl (instead of I2C_SLAVE).
Signed-off-by: David Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers/i2c/i2c-dev.c')
-rw-r--r-- | drivers/i2c/i2c-dev.c | 60 |
1 files changed, 59 insertions, 1 deletions
diff --git a/drivers/i2c/i2c-dev.c b/drivers/i2c/i2c-dev.c index 5a15e50748de..7360f9c37256 100644 --- a/drivers/i2c/i2c-dev.c +++ b/drivers/i2c/i2c-dev.c | |||
@@ -38,6 +38,15 @@ | |||
38 | 38 | ||
39 | static struct i2c_driver i2cdev_driver; | 39 | static struct i2c_driver i2cdev_driver; |
40 | 40 | ||
41 | /* | ||
42 | * An i2c_dev represents an i2c_adapter ... an I2C or SMBus master, not a | ||
43 | * slave (i2c_client) with which messages will be exchanged. It's coupled | ||
44 | * with a character special file which is accessed by user mode drivers. | ||
45 | * | ||
46 | * The list of i2c_dev structures is parallel to the i2c_adapter lists | ||
47 | * maintained by the driver model, and is updated using notifications | ||
48 | * delivered to the i2cdev_driver. | ||
49 | */ | ||
41 | struct i2c_dev { | 50 | struct i2c_dev { |
42 | struct list_head list; | 51 | struct list_head list; |
43 | struct i2c_adapter *adap; | 52 | struct i2c_adapter *adap; |
@@ -103,6 +112,25 @@ static ssize_t show_adapter_name(struct device *dev, | |||
103 | } | 112 | } |
104 | static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); | 113 | static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL); |
105 | 114 | ||
115 | /* ------------------------------------------------------------------------- */ | ||
116 | |||
117 | /* | ||
118 | * After opening an instance of this character special file, a file | ||
119 | * descriptor starts out associated only with an i2c_adapter (and bus). | ||
120 | * | ||
121 | * Using the I2C_RDWR ioctl(), you can then *immediately* issue i2c_msg | ||
122 | * traffic to any devices on the bus used by that adapter. That's because | ||
123 | * the i2c_msg vectors embed all the addressing information they need, and | ||
124 | * are submitted directly to an i2c_adapter. However, SMBus-only adapters | ||
125 | * don't support that interface. | ||
126 | * | ||
127 | * To use read()/write() system calls on that file descriptor, or to use | ||
128 | * SMBus interfaces (and work with SMBus-only hosts!), you must first issue | ||
129 | * an I2C_SLAVE (or I2C_SLAVE_FORCE) ioctl. That configures an anonymous | ||
130 | * (never registered) i2c_client so it holds the addressing information | ||
131 | * needed by those system calls and by this SMBus interface. | ||
132 | */ | ||
133 | |||
106 | static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, | 134 | static ssize_t i2cdev_read (struct file *file, char __user *buf, size_t count, |
107 | loff_t *offset) | 135 | loff_t *offset) |
108 | { | 136 | { |
@@ -172,6 +200,16 @@ static int i2cdev_ioctl(struct inode *inode, struct file *file, | |||
172 | switch ( cmd ) { | 200 | switch ( cmd ) { |
173 | case I2C_SLAVE: | 201 | case I2C_SLAVE: |
174 | case I2C_SLAVE_FORCE: | 202 | case I2C_SLAVE_FORCE: |
203 | /* NOTE: devices set up to work with "new style" drivers | ||
204 | * can't use I2C_SLAVE, even when the device node is not | ||
205 | * bound to a driver. Only I2C_SLAVE_FORCE will work. | ||
206 | * | ||
207 | * Setting the PEC flag here won't affect kernel drivers, | ||
208 | * which will be using the i2c_client node registered with | ||
209 | * the driver model core. Likewise, when that client has | ||
210 | * the PEC flag already set, the i2c-dev driver won't see | ||
211 | * (or use) this setting. | ||
212 | */ | ||
175 | if ((arg > 0x3ff) || | 213 | if ((arg > 0x3ff) || |
176 | (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) | 214 | (((client->flags & I2C_M_TEN) == 0) && arg > 0x7f)) |
177 | return -EINVAL; | 215 | return -EINVAL; |
@@ -386,6 +424,13 @@ static int i2cdev_open(struct inode *inode, struct file *file) | |||
386 | if (!adap) | 424 | if (!adap) |
387 | return -ENODEV; | 425 | return -ENODEV; |
388 | 426 | ||
427 | /* This creates an anonymous i2c_client, which may later be | ||
428 | * pointed to some address using I2C_SLAVE or I2C_SLAVE_FORCE. | ||
429 | * | ||
430 | * This client is ** NEVER REGISTERED ** with the driver model | ||
431 | * or I2C core code!! It just holds private copies of addressing | ||
432 | * information and maybe a PEC flag. | ||
433 | */ | ||
389 | client = kzalloc(sizeof(*client), GFP_KERNEL); | 434 | client = kzalloc(sizeof(*client), GFP_KERNEL); |
390 | if (!client) { | 435 | if (!client) { |
391 | i2c_put_adapter(adap); | 436 | i2c_put_adapter(adap); |
@@ -394,7 +439,6 @@ static int i2cdev_open(struct inode *inode, struct file *file) | |||
394 | snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); | 439 | snprintf(client->name, I2C_NAME_SIZE, "i2c-dev %d", adap->nr); |
395 | client->driver = &i2cdev_driver; | 440 | client->driver = &i2cdev_driver; |
396 | 441 | ||
397 | /* registered with adapter, passed as client to user */ | ||
398 | client->adapter = adap; | 442 | client->adapter = adap; |
399 | file->private_data = client; | 443 | file->private_data = client; |
400 | 444 | ||
@@ -422,6 +466,14 @@ static const struct file_operations i2cdev_fops = { | |||
422 | .release = i2cdev_release, | 466 | .release = i2cdev_release, |
423 | }; | 467 | }; |
424 | 468 | ||
469 | /* ------------------------------------------------------------------------- */ | ||
470 | |||
471 | /* | ||
472 | * The legacy "i2cdev_driver" is used primarily to get notifications when | ||
473 | * I2C adapters are added or removed, so that each one gets an i2c_dev | ||
474 | * and is thus made available to userspace driver code. | ||
475 | */ | ||
476 | |||
425 | static struct class *i2c_dev_class; | 477 | static struct class *i2c_dev_class; |
426 | 478 | ||
427 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) | 479 | static int i2cdev_attach_adapter(struct i2c_adapter *adap) |
@@ -486,6 +538,12 @@ static struct i2c_driver i2cdev_driver = { | |||
486 | .detach_client = i2cdev_detach_client, | 538 | .detach_client = i2cdev_detach_client, |
487 | }; | 539 | }; |
488 | 540 | ||
541 | /* ------------------------------------------------------------------------- */ | ||
542 | |||
543 | /* | ||
544 | * module load/unload record keeping | ||
545 | */ | ||
546 | |||
489 | static int __init i2c_dev_init(void) | 547 | static int __init i2c_dev_init(void) |
490 | { | 548 | { |
491 | int res; | 549 | int res; |