[i2c] [PATCH] i2c: Add write support to the EEPROM driver
Martin Hicks
mort at bork.org
Thu Aug 9 17:47:40 CEST 2007
This adds write support to the i2c eeprom driver. I suspect that
this is extremely dangerous due to what is sometimes stored in these
chips. Added a separate Kconfig option to enable it.
Signed-off-by: Martin Hicks <mort at bork.org>
---
drivers/i2c/chips/Kconfig | 8 ++
drivers/i2c/chips/eeprom.c | 154 +++++++++++++++++++++++++++++++++++++------
2 files changed, 140 insertions(+), 22 deletions(-)
diff --git a/drivers/i2c/chips/Kconfig b/drivers/i2c/chips/Kconfig
index 87ee3ce..5335cfb 100644
--- a/drivers/i2c/chips/Kconfig
+++ b/drivers/i2c/chips/Kconfig
@@ -36,6 +36,14 @@ config SENSORS_EEPROM
This driver can also be built as a module. If so, the module
will be called eeprom.
+config SENSORS_EEPROM_WRITE
+ bool "EEPROM write support"
+ depends on SENSORS_EEPROM && EXPERIMENTAL
+ help
+ If you say yes here you will be able to write to the EEPROM.
+ This could be extremely dangerous, depending on what the EEPROM
+ data is used for.
+
config SENSORS_PCF8574
tristate "Philips PCF8574 and PCF8574A"
depends on I2C && EXPERIMENTAL
diff --git a/drivers/i2c/chips/eeprom.c b/drivers/i2c/chips/eeprom.c
index cec3a0c..e4bbb52 100644
--- a/drivers/i2c/chips/eeprom.c
+++ b/drivers/i2c/chips/eeprom.c
@@ -45,6 +45,8 @@ I2C_CLIENT_INSMOD_1(eeprom);
/* Size of EEPROM in bytes */
#define EEPROM_SIZE 256
+/* Number of bytes that can be written in a block. */
+#define EEPROM_PAGE_SIZE 8
/* possible types of eeprom devices */
enum eeprom_nature {
@@ -77,38 +79,97 @@ static struct i2c_driver eeprom_driver = {
.detach_client = eeprom_detach_client,
};
-static void eeprom_update_client(struct i2c_client *client, u8 slice)
+static int eeprom_update_client_write(struct i2c_client *client, u8 slice)
{
struct eeprom_data *data = i2c_get_clientdata(client);
- int i, j;
+ int ret = 0;
+ int i;
mutex_lock(&data->update_lock);
- if (!(data->valid & (1 << slice)) ||
- time_after(jiffies, data->last_updated[slice] + 300 * HZ)) {
- dev_dbg(&client->dev, "Starting eeprom update, slice %u\n", slice);
-
- if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
- for (i = slice << 5; i < (slice + 1) << 5; i += I2C_SMBUS_BLOCK_MAX)
- if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_BLOCK_MAX)
+ dev_dbg(&client->dev, "Starting eeprom write update, slice %u\n", slice);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)) {
+ for (i = slice << 5; i < (slice + 1) << 5; i+= EEPROM_PAGE_SIZE) {
+ /* Wait for the chip to finish current writes */
+ int count = 0;
+ do {
+ ret = i2c_smbus_write_byte(client, i);
+ /* Chip is not responding */
+ if (count++ > 10000) {
+ dev_dbg(&client->dev,
+ "eeprom write failed\n");
+ ret = -1;
goto exit;
- } else {
- if (i2c_smbus_write_byte(client, slice << 5)) {
- dev_dbg(&client->dev, "eeprom read start has failed!\n");
+ }
+ } while (ret != 0);
+
+ if ((ret = i2c_smbus_write_i2c_block_data(client, i, EEPROM_PAGE_SIZE,
+ data->data + i)))
goto exit;
- }
- for (i = slice << 5; i < (slice + 1) << 5; i++) {
- j = i2c_smbus_read_byte(client);
- if (j < 0)
+ }
+ } else {
+ for (i = slice << 5; i < (slice + 1) << 5; i++) {
+ /* Wait for the chip to finish current writes */
+ int count = 0;
+ do {
+ ret = i2c_smbus_write_byte(client, i);
+ /* Chip is not responding */
+ if (count++ > 10000) {
+ dev_dbg(&client->dev,
+ "eeprom write failed\n");
+ ret = -1;
goto exit;
- data->data[i] = (u8) j;
- }
+ }
+ } while (ret != 0);
+
+ if ((ret = i2c_smbus_write_byte_data(client, i, data->data[i])))
+ goto exit;
}
- data->last_updated[slice] = jiffies;
- data->valid |= (1 << slice);
}
+ data->last_updated[slice] = jiffies;
+ data->valid |= (1 << slice);
exit:
mutex_unlock(&data->update_lock);
+ return ret;
+}
+
+static int eeprom_update_client_read(struct i2c_client *client, u8 slice)
+{
+ struct eeprom_data *data = i2c_get_clientdata(client);
+ int ret = 0;
+ int i;
+
+ mutex_lock(&data->update_lock);
+
+ if ((data->valid & (1 << slice)) &&
+ time_before(jiffies, data->last_updated[slice] + 300 * HZ))
+ goto exit;
+
+ dev_dbg(&client->dev, "Starting eeprom read update, slice %u\n", slice);
+
+ if (i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_READ_I2C_BLOCK)) {
+ for (i = slice << 5; i < (slice + 1) << 5; i += I2C_SMBUS_BLOCK_MAX)
+ if (i2c_smbus_read_i2c_block_data(client, i, data->data + i) != I2C_SMBUS_BLOCK_MAX)
+ goto exit;
+ } else {
+ if (i2c_smbus_write_byte(client, slice << 5)) {
+ dev_dbg(&client->dev, "eeprom read start has failed!\n");
+ ret = -1;
+ goto exit;
+ }
+ for (i = slice << 5; i < (slice + 1) << 5; i++) {
+ ret = i2c_smbus_read_byte(client);
+ if (ret < 0)
+ goto exit;
+ data->data[i] = (u8)ret;
+ }
+ }
+ data->last_updated[slice] = jiffies;
+ data->valid |= (1 << slice);
+exit:
+ mutex_unlock(&data->update_lock);
+ return ret;
}
static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t count)
@@ -124,7 +185,8 @@ static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t c
/* Only refresh slices which contain requested bytes */
for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++)
- eeprom_update_client(client, slice);
+ if (eeprom_update_client_read(client, slice) < 0)
+ return -EIO;
/* Hide Vaio security settings to regular users (16 first bytes) */
if (data->nature == VAIO && off < 16 && !capable(CAP_SYS_ADMIN)) {
@@ -140,14 +202,62 @@ static ssize_t eeprom_read(struct kobject *kobj, char *buf, loff_t off, size_t c
return count;
}
+#ifdef CONFIG_SENSORS_EEPROM_WRITE
+#define EEPROM_ATTR_MODE (S_IRUGO | S_IWUSR)
+static ssize_t eeprom_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(container_of(kobj, struct device, kobj));
+ struct eeprom_data *data = i2c_get_clientdata(client);
+ int ret = 0;
+ u8 slice;
+
+ if (off > EEPROM_SIZE)
+ return 0;
+ if (off + count > EEPROM_SIZE)
+ count = EEPROM_SIZE - off;
+
+ /* Don't allow anyone to overwrite the Vaio security settings */
+ if (data->nature == VAIO && off < 16) {
+ size_t buf_off = 16 - off;
+ buf_off = min(buf_off, count);
+ if (count - buf_off > 0)
+ memcpy(&data->data[16], buf + buf_off, count - buf_off);
+ } else {
+ memcpy(&data->data[off], buf, count);
+ }
+
+ /* Only write slices which contain modified bytes */
+ for (slice = off >> 5; slice <= (off + count - 1) >> 5; slice++) {
+ if (eeprom_update_client_write(client, slice) < 0) {
+ /*
+ * If write fails, then re-read the slice to get rid
+ * of stale data in data->data
+ */
+ data->valid &= ~(1 << slice);
+ eeprom_update_client_read(client, slice);
+ ret = -1;
+ }
+ }
+
+ return (ret == 0) ? count : -EIO;
+}
+#else /* !CONFIG_SENSORS_EEPROM_WRITE */
+#define EEPROM_ATTR_MODE S_IRUGO
+static ssize_t eeprom_write(struct kobject *kobj, char *buf, loff_t off, size_t count)
+{
+ return 0;
+}
+#endif
+
static struct bin_attribute eeprom_attr = {
.attr = {
.name = "eeprom",
- .mode = S_IRUGO,
+ .mode = EEPROM_ATTR_MODE,
.owner = THIS_MODULE,
},
.size = EEPROM_SIZE,
.read = eeprom_read,
+ .write = eeprom_write,
};
static int eeprom_attach_adapter(struct i2c_adapter *adapter)
--
1.5.1.104.g5bcbc
--
Martin Hicks || mort at bork.org || PGP/GnuPG: 0x4C7F2BEE
More information about the i2c
mailing list