lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Date:	Fri, 17 Feb 2012 13:07:02 +0100
From:	Michael Thalmeier <michael.thalmeier@...e.at>
To:	Evgeniy Polyakov <zbr@...emap.net>
Cc:	linux-kernel@...r.kernel.org, michael@...lmeier.at,
	Michael Thalmeier <michael.thalmeier@...e.at>
Subject: [PATCH 3/3] w1: add slave driver for DS1996 and DS1963L

From: Wolfram Stering <wolfram.stering@...e.at>

This driver adds support for some Dallas non-volatile memory 1-wire slave
devices.

At the moment the following devices are supported:

 * family 0x0c: DS1996 64Kb Memory iButton
 * family 0x1A: DS1963L 4kbit Monetary

Access to memory is done via pages exportedi in sysfs.

Signed-off-by: Michael Thalmeier <michael.thalmeier@...e.at>
---
 drivers/w1/slaves/Kconfig     |   15 +
 drivers/w1/slaves/Makefile    |    1 +
 drivers/w1/slaves/w1_ds1996.c |  610 +++++++++++++++++++++++++++++++++++++++++
 drivers/w1/w1_family.h        |    2 +
 4 files changed, 628 insertions(+), 0 deletions(-)
 create mode 100644 drivers/w1/slaves/w1_ds1996.c

diff --git a/drivers/w1/slaves/Kconfig b/drivers/w1/slaves/Kconfig
index d0cb01b..b455d83 100644
--- a/drivers/w1/slaves/Kconfig
+++ b/drivers/w1/slaves/Kconfig
@@ -34,6 +34,21 @@ config W1_SLAVE_DS2423
 	  Say Y here if you want to use a 1-wire
 	  counter family device (DS2423).
 
+config W1_SLAVE_DS1996
+	tristate "64kb Memory iBbutton family support (DS1996)"
+	help
+	  Say Y here if you want to use a 1-wire
+	  64kb Memory iButton family device (DS1996).
+
+config W1_SLAVE_DS1996_CRC
+	bool "Protect DS1996 data with a CRC16"
+	depends on W1_SLAVE_DS1996
+	select CRC16
+	help
+	  Say Y here to protect DS1996 data with a CRC16.
+	  Each block has 30 bytes of data and a two byte CRC16.
+	  Full block writes are only allowed if the CRC is valid.
+
 config W1_SLAVE_DS2431
 	tristate "1kb EEPROM family support (DS2431)"
 	help
diff --git a/drivers/w1/slaves/Makefile b/drivers/w1/slaves/Makefile
index 1f31e9f..b243641 100644
--- a/drivers/w1/slaves/Makefile
+++ b/drivers/w1/slaves/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_W1_SLAVE_THERM)	+= w1_therm.o
 obj-$(CONFIG_W1_SLAVE_SMEM)	+= w1_smem.o
 obj-$(CONFIG_W1_SLAVE_DS2408)   += w1_ds2408.o
 obj-$(CONFIG_W1_SLAVE_DS2423)	+= w1_ds2423.o
+obj-$(CONFIG_W1_SLAVE_DS1996)	+= w1_ds1996.o
 obj-$(CONFIG_W1_SLAVE_DS2431)	+= w1_ds2431.o
 obj-$(CONFIG_W1_SLAVE_DS2433)	+= w1_ds2433.o
 obj-$(CONFIG_W1_SLAVE_DS2760)	+= w1_ds2760.o
diff --git a/drivers/w1/slaves/w1_ds1996.c b/drivers/w1/slaves/w1_ds1996.c
new file mode 100644
index 0000000..5ec18e7
--- /dev/null
+++ b/drivers/w1/slaves/w1_ds1996.c
@@ -0,0 +1,610 @@
+/*
+ *  w1_ds1996.c - w1 family 0C (DS1996) driver
+ *
+ * Copyright (c) 2011 Wolfram Stering <wolfram.stering@...e.at>
+ *
+ * Inspired by Ben Gardner's <bgardner@...tec.com> family 23 (DS2433) driver.
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#include <linux/types.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+#include <linux/crc16.h>
+
+#define CRC16_INIT		0
+#define CRC16_VALID		0xb001
+
+#endif
+
+#include "../w1.h"
+#include "../w1_int.h"
+#include "../w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Wolfram Stering <wolfram.stering@...e.at>");
+MODULE_DESCRIPTION("w1 family 0C driver for DS1996, 64kb memory iButton");
+
+#define W1_F0C_NVMEM_SIZE	8192
+#define W1_F0C_PAGE_COUNT	256
+
+#define W1_F1A_NVMEM_SIZE	512
+#define W1_F1A_PAGE_COUNT	16
+
+#define W1_PAGE_SIZE		32
+#define W1_PAGE_BITS		5
+#define W1_PAGE_MASK		0x1F
+
+#define W1_READ_EEPROM		0xF0
+#define W1_WRITE_SCRATCH	0x0F
+#define W1_READ_SCRATCH		0xAA
+#define W1_COPY_SCRATCH		0x55
+
+#define BITS_TO_BYTES(nr)	DIV_ROUND_UP((nr), BITS_PER_BYTE * sizeof(u8))
+
+struct page_attribute {
+	struct bin_attribute	bin_attr;
+	int			pagenr;
+	char			name[10];	/* space for 'page.NNNN\0' */
+};
+
+struct w1_nvmem_data {
+	int			nr_pages;
+	struct page_attribute	*pages_attrs;	/* sysfs binary page files */
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	u8			cache[0];	/* cache bitmap starts here. */
+#define CACHE_BITMAP_ADDR(d)	((unsigned long *)(d)->cache)
+#define CACHE_BITMAP_SIZE(d)	(BITS_TO_LONGS((d)->nr_pages) \
+				* sizeof(unsigned long))
+#define CACHE_DATA_ADDR(d)	((d)->cache + CACHE_BITMAP_SIZE(d))
+#endif
+};
+
+struct w1_f0c_data {
+	struct w1_nvmem_data	nv_data;
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	/* One bit per page; if set, respective page in cache is valid. */
+	DECLARE_BITMAP(validcrc, W1_F0C_PAGE_COUNT);
+	u8			memory[W1_F0C_NVMEM_SIZE];
+#endif
+};
+
+struct w1_f1a_data {
+	struct w1_nvmem_data	nv_data;
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	/* One bit per page; if set, respective page in cache is valid. */
+	DECLARE_BITMAP(validcrc, W1_F1A_PAGE_COUNT);
+	u8			memory[W1_F1A_NVMEM_SIZE];
+#endif
+};
+
+
+/*
+ * Check the file size bounds and adjusts count as needed.
+ * This would not be needed if the file size didn't reset to 0 after a write.
+ */
+static inline size_t w1_nvmem_fix_count(loff_t off, size_t count, size_t size)
+{
+	if (off > size)
+		return 0;
+
+	if ((off + count) > size)
+		return size - off;
+
+	return count;
+}
+
+/*
+ * Read directly from a slave's non-volatile memory.
+ */
+static int w1_nvmem_read(struct w1_slave *sl, char *buf,
+			 loff_t off, size_t count)
+{
+	u8	wrbuf[3];
+
+	if (w1_reset_select_slave(sl))
+		return -EIO;
+
+	wrbuf[0] = W1_READ_EEPROM;
+	wrbuf[1] = off & 0xff;
+	wrbuf[2] = off >> 8;
+	w1_write_block(sl->master, wrbuf, 3);
+	w1_read_block(sl->master, buf, count);
+
+	return 0;
+}
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+static int w1_nvmem_refresh_block(struct w1_slave *sl,
+				  struct w1_nvmem_data *data,
+				  int block)
+{
+	int	off = block * W1_PAGE_SIZE;
+	int	crc;
+
+	if (test_bit(block, CACHE_BITMAP_ADDR(data)))
+		return 0;
+
+	if (w1_nvmem_read(sl, CACHE_DATA_ADDR(data) + off, off, W1_PAGE_SIZE)) {
+		memset(data->cache, 0, CACHE_BITMAP_SIZE(data));
+		return -EIO;
+	}
+
+	/* cache the block if the CRC is valid */
+	crc = crc16(CRC16_INIT, CACHE_DATA_ADDR(data) + off, W1_PAGE_SIZE);
+	if (crc == CRC16_VALID)
+		__set_bit(block, CACHE_BITMAP_ADDR(data));
+
+	return 0;
+}
+#endif	/* CONFIG_W1_SLAVE_DS1996_CRC */
+
+static ssize_t w1_nvmem_read_bin(struct file *filp, struct kobject *kobj,
+				 struct bin_attribute *bin_attr,
+				 char *buf, loff_t off, size_t count)
+{
+	struct w1_slave *sl = kobj_to_w1_slave(kobj);
+	struct page_attribute *pa = bin_attr->private;
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	struct w1_nvmem_data *data = sl->family_data;
+	int i, min_page, max_page;
+#endif
+
+	if (pa == NULL) {
+		/* read from the 'nvmem' bin file. */
+		count = w1_nvmem_fix_count(off,
+					   count,
+					   data->nr_pages << W1_PAGE_BITS);
+		if (count == 0)
+			return 0;
+	} else {
+		/* read from a 'page.NNN' bin file. */
+		count = w1_nvmem_fix_count(off, count, W1_PAGE_SIZE);
+		if (count == 0)
+			return 0;
+		off += pa->pagenr * W1_PAGE_SIZE;
+	}
+
+	mutex_lock(&sl->master->mutex);
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	min_page = (off >> W1_PAGE_BITS);
+	max_page = (off + count - 1) >> W1_PAGE_BITS;
+	for (i = min_page; i <= max_page; i++) {
+		if (w1_nvmem_refresh_block(sl, data, i)) {
+			count = -EIO;
+			goto out_up;
+		}
+	}
+	memcpy(buf, CACHE_DATA_ADDR(data) + off, count);
+
+#else	/* CONFIG_W1_SLAVE_DS1996_CRC */
+
+	/* read directly from the EEPROM */
+	if (w1_nvmem_read(sl, buf, off, count)) {
+		count = -EIO;
+		goto out_up;
+	}
+#endif	/* CONFIG_W1_SLAVE_DS1996_CRC */
+
+out_up:
+	mutex_unlock(&sl->master->mutex);
+
+	return count;
+}
+
+/**
+ * Writes to the scratchpad and reads it back for verification.
+ * Then copies the scratchpad to EEPROM.
+ * The data must be on one page.
+ * The master must be locked.
+ *
+ * @param sl	The slave structure
+ * @param addr	Address for the write
+ * @param len   length must be <= (W1_PAGE_SIZE - (addr & W1_PAGE_MASK))
+ * @param data	The data to write
+ * @return	0=Success -1=failure
+ */
+static int w1_nvmem_write(struct w1_slave *sl,
+			  int addr,
+			  int len,
+			  const u8 *data)
+{
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	struct w1_nvmem_data *nvd = sl->family_data;
+#endif
+	u8 wrbuf[4];
+	u8 rdbuf[W1_PAGE_SIZE + 3];
+	u8 es = (addr + len - 1) & 0x1f;
+
+	/* Write the data to the scratchpad */
+	if (w1_reset_select_slave(sl))
+		return -1;
+
+	wrbuf[0] = W1_WRITE_SCRATCH;
+	wrbuf[1] = addr & 0xff;
+	wrbuf[2] = addr >> 8;
+
+	w1_write_block(sl->master, wrbuf, 3);
+	w1_write_block(sl->master, data, len);
+
+	/* Read the scratchpad and verify */
+	if (w1_reset_select_slave(sl))
+		return -1;
+
+	w1_write_8(sl->master, W1_READ_SCRATCH);
+	w1_read_block(sl->master, rdbuf, len + 3);
+
+	/* Compare what was read against the data written */
+	if ((rdbuf[0] != wrbuf[1]) || (rdbuf[1] != wrbuf[2]) ||
+	    (rdbuf[2] != es) || (memcmp(data, &rdbuf[3], len) != 0))
+		return -1;
+
+	/* Copy the scratchpad to EEPROM */
+	if (w1_reset_select_slave(sl))
+		return -1;
+
+	wrbuf[0] = W1_COPY_SCRATCH;
+	wrbuf[3] = es;
+	w1_write_block(sl->master, wrbuf, 4);
+
+	/* Sleep for 5 ms to wait for the write to complete */
+	msleep(5);
+
+	/* Reset the bus to wake up the EEPROM (this may not be needed) */
+	w1_reset_bus(sl->master);
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	__clear_bit(addr >> W1_PAGE_BITS, CACHE_BITMAP_ADDR(nvd));
+#endif
+	return 0;
+}
+
+static ssize_t w1_nvmem_write_bin(struct file *filp, struct kobject *kobj,
+				  struct bin_attribute *bin_attr,
+				  char *buf, loff_t off, size_t count)
+{
+	struct w1_slave *sl = kobj_to_w1_slave(kobj);
+	struct page_attribute *pa = bin_attr->private;
+	struct w1_nvmem_data *data = sl->family_data;
+	int addr, len, idx;
+
+dev_info(&sl->dev, "%s - off %llu, count %zd.\n", __func__, off, count);
+
+	if (pa == NULL) {
+		/* write to the 'nvmem' bin file. */
+		count = w1_nvmem_fix_count(off,
+					   count,
+					   data->nr_pages << W1_PAGE_BITS);
+		if (count == 0)
+			return 0;
+	} else {
+		/* write to a 'page.NNN' bin file. */
+		count = w1_nvmem_fix_count(off, count, W1_PAGE_SIZE);
+		if (count == 0)
+			return 0;
+		off += pa->pagenr * W1_PAGE_SIZE;
+	}
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	/* can only write full blocks in cached mode */
+	if ((off & W1_PAGE_MASK) || (count & W1_PAGE_MASK)) {
+		dev_err(&sl->dev, "invalid offset/count off=%d cnt=%zd\n",
+			(int)off, count);
+		return -EINVAL;
+	}
+
+	/* make sure the block CRCs are valid */
+	for (idx = 0; idx < count; idx += W1_PAGE_SIZE) {
+		if (crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE) != CRC16_VALID) {
+			dev_err(&sl->dev, "bad CRC at offset %d, expected %#x, got %#x (%#x,%#x)\n",
+				(int)off,
+				(u16)~crc16(CRC16_INIT, &buf[idx],
+					W1_PAGE_SIZE - 2),
+				*((u16 *)&buf[idx+W1_PAGE_SIZE-2]),
+				(u16)crc16(CRC16_INIT, &buf[idx], W1_PAGE_SIZE),
+				CRC16_VALID);
+			return -EINVAL;
+		}
+	}
+#endif	/* CONFIG_W1_SLAVE_DS1996_CRC */
+
+	mutex_lock(&sl->master->mutex);
+
+	/* Can only write data to one page at a time */
+	idx = 0;
+	while (idx < count) {
+		addr = off + idx;
+		len = W1_PAGE_SIZE - (addr & W1_PAGE_MASK);
+		if (len > (count - idx))
+			len = count - idx;
+		if (w1_nvmem_write(sl, addr, len, &buf[idx]) < 0) {
+			count = -EIO;
+			goto out_up;
+		}
+		idx += len;
+	}
+
+out_up:
+	mutex_unlock(&sl->master->mutex);
+dev_info(&sl->dev, "write done, count %zd\n", count);
+	return count;
+}
+
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+
+static ssize_t w1_cache_read_bin(struct file *filp, struct kobject *kobj,
+				 struct bin_attribute *bin_attr,
+				 char *buf, loff_t off, size_t count)
+{
+	struct w1_slave *sl = kobj_to_w1_slave(kobj);
+	struct w1_nvmem_data *data = sl->family_data;
+
+	count = w1_nvmem_fix_count(off, count, CACHE_BITMAP_SIZE(data));
+	if (count == 0)
+		return 0;
+
+	memcpy(buf, data->cache + off, count);
+	return count;
+}
+#endif
+
+static void setup_page_attr(struct page_attribute *pf, int pn)
+{
+	snprintf(pf->name, sizeof pf->name, "page.%03d", pn);
+	pf->pagenr = pn;
+	attr_name(pf->bin_attr) = pf->name;
+	pf->bin_attr.attr.mode = 0660;
+	pf->bin_attr.size = W1_PAGE_SIZE;
+	pf->bin_attr.private = pf;
+	pf->bin_attr.read = w1_nvmem_read_bin;
+	pf->bin_attr.write = w1_nvmem_write_bin;
+	pf->bin_attr.mmap = NULL;
+}
+
+static void destroy_pages_attrs(struct w1_slave *sl)
+{
+	struct w1_nvmem_data *fdata = sl->family_data;
+	int i;
+
+	for (i = 0; i < fdata->nr_pages; ++i)
+		if (attr_name(fdata->pages_attrs[i].bin_attr))
+			sysfs_remove_bin_file(&sl->dev.kobj,
+				&fdata->pages_attrs[i].bin_attr);
+
+	kfree(fdata->pages_attrs);
+	fdata->pages_attrs = NULL;
+}
+
+static int create_pages_attrs(struct w1_slave *sl)
+{
+	struct w1_nvmem_data *fdata = sl->family_data;
+	int i, error;
+
+	if (!fdata || fdata->nr_pages == 0)
+		return 0;
+
+	fdata->pages_attrs =
+		kzalloc(fdata->nr_pages * sizeof(struct page_attribute),
+			GFP_KERNEL);
+	if (!fdata->pages_attrs)
+		return -ENOMEM;
+
+	for (i = error = 0; i < fdata->nr_pages && !error; ++i) {
+		setup_page_attr(&fdata->pages_attrs[i], i);
+		error = sysfs_create_bin_file(&sl->dev.kobj,
+					      &fdata->pages_attrs[i].bin_attr);
+	}
+
+	dev_dbg(&sl->dev, "Created %d pages: %02x-%012llx: error=%d\n",
+		i, sl->reg_num.family, (unsigned long long)sl->reg_num.id,
+		error);
+
+	if (!error)
+		return 0;
+
+	destroy_pages_attrs(sl);
+	return error;
+}
+
+
+/*
+ * DS1996 -- 64kb Memory iButton, family 0x0C.
+ */
+
+static struct bin_attribute w1_f0c_bin_attr[] = {
+	{
+		.attr = {
+			.name = "nvmem",
+			.mode = S_IRUGO | S_IWUSR,
+		},
+		.size = W1_F0C_NVMEM_SIZE,
+		.read = w1_nvmem_read_bin,
+		.write = w1_nvmem_write_bin,
+	},
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	{
+		.attr = {
+			.name = "cache",
+			.mode = 0440,
+		},
+		.size = BITS_TO_BYTES(W1_F0C_PAGE_COUNT),
+		.read = w1_cache_read_bin,
+		.write = NULL,
+	},
+#endif
+};
+
+static int w1_f0c_add_slave(struct w1_slave *sl)
+{
+	int err;
+	struct w1_f0c_data *data;
+
+	data = kzalloc(sizeof(struct w1_f0c_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->nv_data.nr_pages = W1_F0C_PAGE_COUNT;
+	sl->family_data = data;
+
+	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[0]);
+	if (err)
+		goto out_err;
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[1]);
+	if (err)
+		goto out_err_nvmem;
+#endif
+	err = create_pages_attrs(sl);
+	if (!err)
+		return 0;
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[1]);
+out_err_nvmem:
+#endif
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[0]);
+out_err:
+	kfree(data);
+	return err;
+}
+
+static void w1_f0c_remove_slave(struct w1_slave *sl)
+{
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[0]);
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f0c_bin_attr[1]);
+#endif
+	destroy_pages_attrs(sl);
+
+	kfree(sl->family_data);
+	sl->family_data = NULL;
+}
+
+static struct w1_family_ops w1_f0c_fops = {
+	.add_slave      = w1_f0c_add_slave,
+	.remove_slave   = w1_f0c_remove_slave,
+};
+
+static struct w1_family w1_family_0c = {
+	.fid = W1_EEPROM_DS1996,
+	.fops = &w1_f0c_fops,
+};
+
+
+static struct bin_attribute w1_f1a_bin_attr[] = {
+	{
+		.attr = {
+			.name = "nvmem",
+			.mode = S_IRUGO | S_IWUSR,
+		},
+		.size = W1_F1A_NVMEM_SIZE,
+		.read = w1_nvmem_read_bin,
+		.write = w1_nvmem_write_bin,
+	},
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	{
+		.attr = {
+			.name = "cache",
+			.mode = 0440,
+		},
+		.size = BITS_TO_BYTES(W1_F1A_PAGE_COUNT),
+		.read = w1_cache_read_bin,
+		.write = NULL,
+	},
+#endif
+};
+
+
+/*
+ * DS1963L -- Monetary iButton, family 0x1A.
+ */
+
+static int w1_f1a_add_slave(struct w1_slave *sl)
+{
+	int err;
+	struct w1_f1a_data *data;
+
+	data = kzalloc(sizeof(struct w1_f1a_data), GFP_KERNEL);
+	if (!data)
+		return -ENOMEM;
+	data->nv_data.nr_pages = W1_F1A_PAGE_COUNT;
+	sl->family_data = data;
+
+	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[0]);
+	if (err)
+		goto out_err;
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[1]);
+	if (err)
+		goto out_err_nvmem;
+#endif
+	err = create_pages_attrs(sl);
+	if (!err)
+		return 0;
+
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[1]);
+out_err_nvmem:
+#endif
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[0]);
+out_err:
+	kfree(data);
+	return err;
+}
+
+static void w1_f1a_remove_slave(struct w1_slave *sl)
+{
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[0]);
+#ifdef CONFIG_W1_SLAVE_DS1996_CRC
+	sysfs_remove_bin_file(&sl->dev.kobj, &w1_f1a_bin_attr[1]);
+#endif
+	destroy_pages_attrs(sl);
+
+	kfree(sl->family_data);
+	sl->family_data = NULL;
+}
+
+static struct w1_family_ops w1_f1a_fops = {
+	.add_slave      = w1_f1a_add_slave,
+	.remove_slave   = w1_f1a_remove_slave,
+};
+
+static struct w1_family w1_family_1a = {
+	.fid = W1_FAMILY_DS1963L,
+	.fops = &w1_f1a_fops,
+};
+
+
+static int __init w1_nvmem_init(void)
+{
+	int err = w1_register_family(&w1_family_0c);
+	printk(KERN_INFO "w1 nvmem slave driver; %d, 0C: %d, 1A: %d bytes.\n",
+		sizeof(struct w1_nvmem_data), sizeof(struct w1_f0c_data),
+		sizeof(struct w1_f1a_data));
+	if (!err) {
+		err = w1_register_family(&w1_family_1a);
+		if (err)
+			w1_unregister_family(&w1_family_0c);
+	}
+	return err;
+}
+
+static void __exit w1_nvmem_fini(void)
+{
+	w1_unregister_family(&w1_family_1a);
+	w1_unregister_family(&w1_family_0c);
+}
+
+module_init(w1_nvmem_init);
+module_exit(w1_nvmem_fini);
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index 490cda2..3724048 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -39,6 +39,8 @@
 #define W1_FAMILY_DS2760	0x30
 #define W1_FAMILY_DS2780	0x32
 #define W1_THERM_DS28EA00	0x42
+#define W1_EEPROM_DS1996	0x0C
+#define W1_FAMILY_DS1963L	0x1A
 
 #define MAXNAMELEN		32
 
-- 
1.7.7.6



--
Scanned by MailScanner.

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ