#include <linux/seq_file.h>
#include <linux/platform_device.h>
#include <linux/mutex.h>
+#include <linux/completion.h>
#include <asm/uaccess.h>
static DEFINE_MUTEX(core_lists);
static DEFINE_IDR(i2c_adapter_idr);
+
+/* ------------------------------------------------------------------------- */
+
/* match always succeeds, as we want the probe() to tell if we really accept this match */
static int i2c_device_match(struct device *dev, struct device_driver *drv)
{
return 1;
}
-static int i2c_bus_suspend(struct device * dev, pm_message_t state)
+static int i2c_device_probe(struct device *dev)
{
- int rc = 0;
-
- if (dev->driver && dev->driver->suspend)
- rc = dev->driver->suspend(dev, state);
- return rc;
+ return -ENODEV;
}
-static int i2c_bus_resume(struct device * dev)
+static int i2c_device_remove(struct device *dev)
{
- int rc = 0;
-
- if (dev->driver && dev->driver->resume)
- rc = dev->driver->resume(dev);
- return rc;
+ return 0;
}
-static int i2c_device_probe(struct device *dev)
+static void i2c_device_shutdown(struct device *dev)
{
- return -ENODEV;
+ struct i2c_driver *driver;
+
+ if (!dev->driver)
+ return;
+ driver = to_i2c_driver(dev->driver);
+ if (driver->shutdown)
+ driver->shutdown(to_i2c_client(dev));
}
-static int i2c_device_remove(struct device *dev)
+static int i2c_device_suspend(struct device * dev, pm_message_t mesg)
{
- return 0;
-}
+ struct i2c_driver *driver;
-struct bus_type i2c_bus_type = {
- .name = "i2c",
- .match = i2c_device_match,
- .probe = i2c_device_probe,
- .remove = i2c_device_remove,
- .suspend = i2c_bus_suspend,
- .resume = i2c_bus_resume,
-};
+ if (!dev->driver)
+ return 0;
+ driver = to_i2c_driver(dev->driver);
+ if (!driver->suspend)
+ return 0;
+ return driver->suspend(to_i2c_client(dev), mesg);
+}
-void i2c_adapter_dev_release(struct device *dev)
+static int i2c_device_resume(struct device * dev)
{
- struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
- complete(&adap->dev_released);
+ struct i2c_driver *driver;
+
+ if (!dev->driver)
+ return 0;
+ driver = to_i2c_driver(dev->driver);
+ if (!driver->resume)
+ return 0;
+ return driver->resume(to_i2c_client(dev));
}
-struct device_driver i2c_adapter_driver = {
- .owner = THIS_MODULE,
- .name = "i2c_adapter",
- .bus = &i2c_bus_type,
+struct bus_type i2c_bus_type = {
+ .name = "i2c",
+ .match = i2c_device_match,
+ .probe = i2c_device_probe,
+ .remove = i2c_device_remove,
+ .shutdown = i2c_device_shutdown,
+ .suspend = i2c_device_suspend,
+ .resume = i2c_device_resume,
};
/* ------------------------------------------------------------------------- */
/* I2C bus adapters -- one roots each I2C or SMBUS segment */
-static void i2c_adapter_class_dev_release(struct class_device *dev)
+void i2c_adapter_dev_release(struct device *dev)
{
- struct i2c_adapter *adap = class_dev_to_i2c_adapter(dev);
- complete(&adap->class_dev_released);
+ struct i2c_adapter *adap = to_i2c_adapter(dev);
+ complete(&adap->dev_released);
}
-static ssize_t i2c_adapter_show_name(struct class_device *cdev, char *buf)
+static ssize_t
+show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct i2c_adapter *adap = class_dev_to_i2c_adapter(cdev);
+ struct i2c_adapter *adap = to_i2c_adapter(dev);
return sprintf(buf, "%s\n", adap->name);
}
-static struct class_device_attribute i2c_adapter_attrs[] = {
- __ATTR(name, S_IRUGO, i2c_adapter_show_name, NULL),
+static struct device_attribute i2c_adapter_attrs[] = {
+ __ATTR(name, S_IRUGO, show_adapter_name, NULL),
{ },
};
struct class i2c_adapter_class = {
.owner = THIS_MODULE,
.name = "i2c-adapter",
- .class_dev_attrs = i2c_adapter_attrs,
- .release = &i2c_adapter_class_dev_release,
+ .dev_attrs = i2c_adapter_attrs,
};
-static ssize_t show_adapter_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct i2c_adapter *adap = dev_to_i2c_adapter(dev);
- return sprintf(buf, "%s\n", adap->name);
-}
-static DEVICE_ATTR(name, S_IRUGO, show_adapter_name, NULL);
-
static void i2c_client_release(struct device *dev)
{
*/
if (adap->dev.parent == NULL) {
adap->dev.parent = &platform_bus;
- printk(KERN_WARNING "**WARNING** I2C adapter driver [%s] "
- "forgot to specify physical device; fix it!\n",
- adap->name);
+ pr_debug("I2C adapter driver [%s] forgot to specify "
+ "physical device\n", adap->name);
}
sprintf(adap->dev.bus_id, "i2c-%d", adap->nr);
- adap->dev.driver = &i2c_adapter_driver;
adap->dev.release = &i2c_adapter_dev_release;
+ adap->dev.class = &i2c_adapter_class;
res = device_register(&adap->dev);
if (res)
goto out_list;
- res = device_create_file(&adap->dev, &dev_attr_name);
- if (res)
- goto out_unregister;
-
- /* Add this adapter to the i2c_adapter class */
- memset(&adap->class_dev, 0x00, sizeof(struct class_device));
- adap->class_dev.dev = &adap->dev;
- adap->class_dev.class = &i2c_adapter_class;
- strlcpy(adap->class_dev.class_id, adap->dev.bus_id, BUS_ID_SIZE);
- res = class_device_register(&adap->class_dev);
- if (res)
- goto out_remove_name;
dev_dbg(&adap->dev, "adapter [%s] registered\n", adap->name);
mutex_unlock(&core_lists);
return res;
-out_remove_name:
- device_remove_file(&adap->dev, &dev_attr_name);
-out_unregister:
- init_completion(&adap->dev_released); /* Needed? */
- device_unregister(&adap->dev);
- wait_for_completion(&adap->dev_released);
out_list:
list_del(&adap->list);
idr_remove(&i2c_adapter_idr, adap->nr);
/* clean up the sysfs representation */
init_completion(&adap->dev_released);
- init_completion(&adap->class_dev_released);
- class_device_unregister(&adap->class_dev);
- device_remove_file(&adap->dev, &dev_attr_name);
device_unregister(&adap->dev);
list_del(&adap->list);
/* wait for sysfs to drop all references */
wait_for_completion(&adap->dev_released);
- wait_for_completion(&adap->class_dev_released);
/* free dynamically allocated bus id */
idr_remove(&i2c_adapter_idr, adap->nr);
int i2c_register_driver(struct module *owner, struct i2c_driver *driver)
{
- struct list_head *item;
- struct i2c_adapter *adapter;
int res;
/* add the driver to the list of i2c drivers in the driver core */
/* now look for instances of driver on our adapters */
if (driver->attach_adapter) {
- list_for_each(item,&adapters) {
- adapter = list_entry(item, struct i2c_adapter, list);
+ struct i2c_adapter *adapter;
+
+ list_for_each_entry(adapter, &adapters, list) {
driver->attach_adapter(adapter);
}
}
int retval;
retval = bus_register(&i2c_bus_type);
- if (retval)
- return retval;
- retval = driver_register(&i2c_adapter_driver);
if (retval)
return retval;
return class_register(&i2c_adapter_class);
static void __exit i2c_exit(void)
{
class_unregister(&i2c_adapter_class);
- driver_unregister(&i2c_adapter_driver);
bus_unregister(&i2c_bus_type);
}
#ifdef DEBUG
for (ret = 0; ret < num; ret++) {
dev_dbg(&adap->dev, "master_xfer[%d] %c, addr=0x%02x, "
- "len=%d\n", ret, msgs[ret].flags & I2C_M_RD ?
- 'R' : 'W', msgs[ret].addr, msgs[ret].len);
+ "len=%d%s\n", ret, (msgs[ret].flags & I2C_M_RD)
+ ? 'R' : 'W', msgs[ret].addr, msgs[ret].len,
+ (msgs[ret].flags & I2C_M_RECV_LEN) ? "+" : "");
}
#endif
break;
case I2C_SMBUS_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
- dev_err(&adapter->dev, "Block read not supported "
- "under I2C emulation!\n");
- return -1;
+ msg[1].flags |= I2C_M_RECV_LEN;
+ msg[1].len = 1; /* block length will be added by
+ the underlying bus driver */
} else {
msg[0].len = data->block[0] + 2;
if (msg[0].len > I2C_SMBUS_BLOCK_MAX + 2) {
}
break;
case I2C_SMBUS_BLOCK_PROC_CALL:
- dev_dbg(&adapter->dev, "Block process call not supported "
- "under I2C emulation!\n");
- return -1;
+ num = 2; /* Another special case */
+ read_write = I2C_SMBUS_READ;
+ if (data->block[0] > I2C_SMBUS_BLOCK_MAX) {
+ dev_err(&adapter->dev, "%s called with invalid "
+ "block proc call size (%d)\n", __FUNCTION__,
+ data->block[0]);
+ return -1;
+ }
+ msg[0].len = data->block[0] + 2;
+ for (i = 1; i < msg[0].len; i++)
+ msgbuf0[i] = data->block[i-1];
+ msg[1].flags |= I2C_M_RECV_LEN;
+ msg[1].len = 1; /* block length will be added by
+ the underlying bus driver */
+ break;
case I2C_SMBUS_I2C_BLOCK_DATA:
if (read_write == I2C_SMBUS_READ) {
msg[1].len = I2C_SMBUS_BLOCK_MAX;
for (i = 0; i < I2C_SMBUS_BLOCK_MAX; i++)
data->block[i+1] = msgbuf1[i];
break;
+ case I2C_SMBUS_BLOCK_DATA:
+ case I2C_SMBUS_BLOCK_PROC_CALL:
+ for (i = 0; i < msgbuf1[0] + 1; i++)
+ data->block[i] = msgbuf1[i];
+ break;
}
return 0;
}
}
-/* Next four are needed by i2c-isa */
+/* Next three are needed by i2c-isa */
EXPORT_SYMBOL_GPL(i2c_adapter_dev_release);
-EXPORT_SYMBOL_GPL(i2c_adapter_driver);
EXPORT_SYMBOL_GPL(i2c_adapter_class);
EXPORT_SYMBOL_GPL(i2c_bus_type);