summaryrefslogtreecommitdiff
path: root/drivers/misc/eeprom/at24.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/misc/eeprom/at24.c')
-rw-r--r--drivers/misc/eeprom/at24.c41
1 files changed, 41 insertions, 0 deletions
diff --git a/drivers/misc/eeprom/at24.c b/drivers/misc/eeprom/at24.c
index 2591c21b2b5d..fb0b8375d5ae 100644
--- a/drivers/misc/eeprom/at24.c
+++ b/drivers/misc/eeprom/at24.c
@@ -8,6 +8,7 @@
#include <linux/acpi.h>
#include <linux/bitops.h>
+#include <linux/capability.h>
#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/init.h>
@@ -89,6 +90,7 @@ struct at24_data {
struct nvmem_device *nvmem;
struct regulator *vcc_reg;
+ void (*read_post)(unsigned int off, char *buf, size_t count);
/*
* Some chips tie up multiple I2C addresses; dummy devices reserve
@@ -121,6 +123,7 @@ MODULE_PARM_DESC(at24_write_timeout, "Time (in ms) to try writes (default 25)");
struct at24_chip_data {
u32 byte_len;
u8 flags;
+ void (*read_post)(unsigned int off, char *buf, size_t count);
};
#define AT24_CHIP_DATA(_name, _len, _flags) \
@@ -128,6 +131,32 @@ struct at24_chip_data {
.byte_len = _len, .flags = _flags, \
}
+#define AT24_CHIP_DATA_CB(_name, _len, _flags, _read_post) \
+ static const struct at24_chip_data _name = { \
+ .byte_len = _len, .flags = _flags, \
+ .read_post = _read_post, \
+ }
+
+static void at24_read_post_vaio(unsigned int off, char *buf, size_t count)
+{
+ int i;
+
+ if (capable(CAP_SYS_ADMIN))
+ return;
+
+ /*
+ * Hide VAIO private settings to regular users:
+ * - BIOS passwords: bytes 0x00 to 0x0f
+ * - UUID: bytes 0x10 to 0x1f
+ * - Serial number: 0xc0 to 0xdf
+ */
+ for (i = 0; i < count; i++) {
+ if ((off + i <= 0x1f) ||
+ (off + i >= 0xc0 && off + i <= 0xdf))
+ buf[i] = 0;
+ }
+}
+
/* needs 8 addresses as A0-A2 are ignored */
AT24_CHIP_DATA(at24_data_24c00, 128 / 8, AT24_FLAG_TAKE8ADDR);
/* old variants can't be handled with this generic entry! */
@@ -144,6 +173,10 @@ AT24_CHIP_DATA(at24_data_24mac602, 64 / 8,
/* spd is a 24c02 in memory DIMMs */
AT24_CHIP_DATA(at24_data_spd, 2048 / 8,
AT24_FLAG_READONLY | AT24_FLAG_IRUGO);
+/* 24c02_vaio is a 24c02 on some Sony laptops */
+AT24_CHIP_DATA_CB(at24_data_24c02_vaio, 2048 / 8,
+ AT24_FLAG_READONLY | AT24_FLAG_IRUGO,
+ at24_read_post_vaio);
AT24_CHIP_DATA(at24_data_24c04, 4096 / 8, 0);
AT24_CHIP_DATA(at24_data_24cs04, 16,
AT24_FLAG_SERIAL | AT24_FLAG_READONLY);
@@ -177,6 +210,7 @@ static const struct i2c_device_id at24_ids[] = {
{ "24mac402", (kernel_ulong_t)&at24_data_24mac402 },
{ "24mac602", (kernel_ulong_t)&at24_data_24mac602 },
{ "spd", (kernel_ulong_t)&at24_data_spd },
+ { "24c02-vaio", (kernel_ulong_t)&at24_data_24c02_vaio },
{ "24c04", (kernel_ulong_t)&at24_data_24c04 },
{ "24cs04", (kernel_ulong_t)&at24_data_24cs04 },
{ "24c08", (kernel_ulong_t)&at24_data_24c08 },
@@ -389,6 +423,9 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
struct device *dev;
char *buf = val;
int ret;
+ unsigned int orig_off = off;
+ char *orig_buf = buf;
+ size_t orig_count = count;
at24 = priv;
dev = at24_base_client_dev(at24);
@@ -427,6 +464,9 @@ static int at24_read(void *priv, unsigned int off, void *val, size_t count)
pm_runtime_put(dev);
+ if (unlikely(at24->read_post))
+ at24->read_post(orig_off, orig_buf, orig_count);
+
return 0;
}
@@ -654,6 +694,7 @@ static int at24_probe(struct i2c_client *client)
at24->byte_len = byte_len;
at24->page_size = page_size;
at24->flags = flags;
+ at24->read_post = cdata->read_post;
at24->num_addresses = num_addresses;
at24->offset_adj = at24_get_offset_adj(flags, byte_len);
at24->client[0].client = client;