aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/w1/Kconfig16
-rw-r--r--drivers/w1/Makefile7
-rw-r--r--drivers/w1/ds_w1_bridge.c24
-rw-r--r--drivers/w1/dscore.c161
-rw-r--r--drivers/w1/dscore.h10
-rw-r--r--drivers/w1/w1.c302
-rw-r--r--drivers/w1/w1.h22
-rw-r--r--drivers/w1/w1_ds2433.c327
-rw-r--r--drivers/w1/w1_family.c11
-rw-r--r--drivers/w1/w1_family.h7
-rw-r--r--drivers/w1/w1_int.c26
-rw-r--r--drivers/w1/w1_io.c24
-rw-r--r--drivers/w1/w1_io.h1
-rw-r--r--drivers/w1/w1_netlink.c26
-rw-r--r--drivers/w1/w1_netlink.h2
-rw-r--r--drivers/w1/w1_smem.c49
-rw-r--r--drivers/w1/w1_therm.c47
-rw-r--r--include/linux/crc16.h44
-rw-r--r--lib/Kconfig8
-rw-r--r--lib/Makefile3
-rw-r--r--lib/crc16.c67
21 files changed, 871 insertions, 313 deletions
diff --git a/drivers/w1/Kconfig b/drivers/w1/Kconfig
index 711b90903e7b..9a1e00dd3e02 100644
--- a/drivers/w1/Kconfig
+++ b/drivers/w1/Kconfig
@@ -54,4 +54,20 @@ config W1_SMEM
Say Y here if you want to connect 1-wire
simple 64bit memory rom(ds2401/ds2411/ds1990*) to you wire.
+config W1_DS2433
+ tristate "4kb EEPROM family support (DS2433)"
+ depends on W1
+ help
+ Say Y here if you want to use a 1-wire
+ 4kb EEPROM family device (DS2433).
+
+config W1_DS2433_CRC
+ bool "Protect DS2433 data with a CRC16"
+ depends on W1_DS2433
+ select CRC16
+ help
+ Say Y here to protect DS2433 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.
+
endmenu
diff --git a/drivers/w1/Makefile b/drivers/w1/Makefile
index 80725c348e70..01fb54391470 100644
--- a/drivers/w1/Makefile
+++ b/drivers/w1/Makefile
@@ -6,6 +6,10 @@ ifneq ($(CONFIG_NET), y)
EXTRA_CFLAGS += -DNETLINK_DISABLED
endif
+ifeq ($(CONFIG_W1_DS2433_CRC), y)
+EXTRA_CFLAGS += -DCONFIG_W1_F23_CRC
+endif
+
obj-$(CONFIG_W1) += wire.o
wire-objs := w1.o w1_int.o w1_family.o w1_netlink.o w1_io.o
@@ -13,8 +17,9 @@ obj-$(CONFIG_W1_MATROX) += matrox_w1.o
obj-$(CONFIG_W1_THERM) += w1_therm.o
obj-$(CONFIG_W1_SMEM) += w1_smem.o
-obj-$(CONFIG_W1_DS9490) += ds9490r.o
+obj-$(CONFIG_W1_DS9490) += ds9490r.o
ds9490r-objs := dscore.o
obj-$(CONFIG_W1_DS9490_BRIDGE) += ds_w1_bridge.o
+obj-$(CONFIG_W1_DS2433) += w1_ds2433.o
diff --git a/drivers/w1/ds_w1_bridge.c b/drivers/w1/ds_w1_bridge.c
index 7bddd8ac7d7f..a79d16d5666f 100644
--- a/drivers/w1/ds_w1_bridge.c
+++ b/drivers/w1/ds_w1_bridge.c
@@ -1,8 +1,8 @@
/*
- * ds_w1_bridge.c
+ * ds_w1_bridge.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
- *
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -25,7 +25,7 @@
#include "../w1/w1.h"
#include "../w1/w1_int.h"
#include "dscore.h"
-
+
static struct ds_device *ds_dev;
static struct w1_bus_master *ds_bus_master;
@@ -120,7 +120,7 @@ static u8 ds9490r_reset(unsigned long data)
static int __devinit ds_w1_init(void)
{
int err;
-
+
ds_bus_master = kmalloc(sizeof(*ds_bus_master), GFP_KERNEL);
if (!ds_bus_master) {
printk(KERN_ERR "Failed to allocate DS9490R USB<->W1 bus_master structure.\n");
@@ -136,14 +136,14 @@ static int __devinit ds_w1_init(void)
memset(ds_bus_master, 0, sizeof(*ds_bus_master));
- ds_bus_master->data = (unsigned long)ds_dev;
- ds_bus_master->touch_bit = &ds9490r_touch_bit;
- ds_bus_master->read_bit = &ds9490r_read_bit;
- ds_bus_master->write_bit = &ds9490r_write_bit;
- ds_bus_master->read_byte = &ds9490r_read_byte;
- ds_bus_master->write_byte = &ds9490r_write_byte;
- ds_bus_master->read_block = &ds9490r_read_block;
- ds_bus_master->write_block = &ds9490r_write_block;
+ ds_bus_master->data = (unsigned long)ds_dev;
+ ds_bus_master->touch_bit = &ds9490r_touch_bit;
+ ds_bus_master->read_bit = &ds9490r_read_bit;
+ ds_bus_master->write_bit = &ds9490r_write_bit;
+ ds_bus_master->read_byte = &ds9490r_read_byte;
+ ds_bus_master->write_byte = &ds9490r_write_byte;
+ ds_bus_master->read_block = &ds9490r_read_block;
+ ds_bus_master->write_block = &ds9490r_write_block;
ds_bus_master->reset_bus = &ds9490r_reset;
err = w1_add_master_device(ds_bus_master);
diff --git a/drivers/w1/dscore.c b/drivers/w1/dscore.c
index eee6644d33d6..15fb250451e5 100644
--- a/drivers/w1/dscore.c
+++ b/drivers/w1/dscore.c
@@ -1,8 +1,8 @@
/*
- * dscore.c
+ * dscore.c
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
- *
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -32,19 +32,16 @@ static struct usb_device_id ds_id_table [] = {
};
MODULE_DEVICE_TABLE(usb, ds_id_table);
-int ds_probe(struct usb_interface *, const struct usb_device_id *);
-void ds_disconnect(struct usb_interface *);
+static int ds_probe(struct usb_interface *, const struct usb_device_id *);
+static void ds_disconnect(struct usb_interface *);
int ds_touch_bit(struct ds_device *, u8, u8 *);
int ds_read_byte(struct ds_device *, u8 *);
int ds_read_bit(struct ds_device *, u8 *);
int ds_write_byte(struct ds_device *, u8);
int ds_write_bit(struct ds_device *, u8);
-int ds_start_pulse(struct ds_device *, int);
-int ds_set_speed(struct ds_device *, int);
+static int ds_start_pulse(struct ds_device *, int);
int ds_reset(struct ds_device *, struct ds_status *);
-int ds_detect(struct ds_device *, struct ds_status *);
-int ds_stop_pulse(struct ds_device *, int);
struct ds_device * ds_get_device(void);
void ds_put_device(struct ds_device *);
@@ -79,11 +76,11 @@ void ds_put_device(struct ds_device *dev)
static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index)
{
int err;
-
- err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
CONTROL_CMD, 0x40, value, index, NULL, 0, 1000);
if (err < 0) {
- printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n",
+ printk(KERN_ERR "Failed to send command control message %x.%x: err=%d.\n",
value, index, err);
return err;
}
@@ -94,11 +91,11 @@ static int ds_send_control_cmd(struct ds_device *dev, u16 value, u16 index)
static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index)
{
int err;
-
- err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
MODE_CMD, 0x40, value, index, NULL, 0, 1000);
if (err < 0) {
- printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n",
+ printk(KERN_ERR "Failed to send mode control message %x.%x: err=%d.\n",
value, index, err);
return err;
}
@@ -109,11 +106,11 @@ static int ds_send_control_mode(struct ds_device *dev, u16 value, u16 index)
static int ds_send_control(struct ds_device *dev, u16 value, u16 index)
{
int err;
-
- err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
+
+ err = usb_control_msg(dev->udev, usb_sndctrlpipe(dev->udev, dev->ep[EP_CONTROL]),
COMM_CMD, 0x40, value, index, NULL, 0, 1000);
if (err < 0) {
- printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n",
+ printk(KERN_ERR "Failed to send control message %x.%x: err=%d.\n",
value, index, err);
return err;
}
@@ -126,19 +123,20 @@ static inline void ds_dump_status(unsigned char *buf, unsigned char *str, int of
printk("%45s: %8x\n", str, buf[off]);
}
-int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st, unsigned char *buf, int size)
+static int ds_recv_status_nodump(struct ds_device *dev, struct ds_status *st,
+ unsigned char *buf, int size)
{
int count, err;
-
+
memset(st, 0, sizeof(st));
-
+
count = 0;
err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_STATUS]), buf, size, &count, 100);
if (err < 0) {
printk(KERN_ERR "Failed to read 1-wire data from 0x%x: err=%d.\n", dev->ep[EP_STATUS], err);
return err;
}
-
+
if (count >= sizeof(*st))
memcpy(st, buf, sizeof(*st));
@@ -149,13 +147,13 @@ static int ds_recv_status(struct ds_device *dev, struct ds_status *st)
{
unsigned char buf[64];
int count, err = 0, i;
-
+
memcpy(st, buf, sizeof(*st));
-
+
count = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
if (count < 0)
return err;
-
+
printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], count);
for (i=0; i<count; ++i)
printk("%02x ", buf[i]);
@@ -199,7 +197,7 @@ static int ds_recv_status(struct ds_device *dev, struct ds_status *st)
return err;
}
#endif
-
+
return err;
}
@@ -207,9 +205,9 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
{
int count, err;
struct ds_status st;
-
+
count = 0;
- err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
+ err = usb_bulk_msg(dev->udev, usb_rcvbulkpipe(dev->udev, dev->ep[EP_DATA_IN]),
buf, size, &count, 1000);
if (err < 0) {
printk(KERN_INFO "Clearing ep0x%x.\n", dev->ep[EP_DATA_IN]);
@@ -234,7 +232,7 @@ static int ds_recv_data(struct ds_device *dev, unsigned char *buf, int size)
static int ds_send_data(struct ds_device *dev, unsigned char *buf, int len)
{
int count, err;
-
+
count = 0;
err = usb_bulk_msg(dev->udev, usb_sndbulkpipe(dev->udev, dev->ep[EP_DATA_OUT]), buf, len, &count, 1000);
if (err < 0) {
@@ -245,12 +243,14 @@ static int ds_send_data(struct ds_device *dev, unsigned char *buf, int len)
return err;
}
+#if 0
+
int ds_stop_pulse(struct ds_device *dev, int limit)
{
struct ds_status st;
int count = 0, err = 0;
u8 buf[0x20];
-
+
do {
err = ds_send_control(dev, CTL_HALT_EXE_IDLE, 0);
if (err)
@@ -275,7 +275,7 @@ int ds_stop_pulse(struct ds_device *dev, int limit)
int ds_detect(struct ds_device *dev, struct ds_status *st)
{
int err;
-
+
err = ds_send_control_cmd(dev, CTL_RESET_DEVICE, 0);
if (err)
return err;
@@ -283,11 +283,11 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)
err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM, 0);
if (err)
return err;
-
+
err = ds_send_control(dev, COMM_SET_DURATION | COMM_IM | COMM_TYPE, 0x40);
if (err)
return err;
-
+
err = ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_PROG);
if (err)
return err;
@@ -297,7 +297,9 @@ int ds_detect(struct ds_device *dev, struct ds_status *st)
return err;
}
-int ds_wait_status(struct ds_device *dev, struct ds_status *st)
+#endif /* 0 */
+
+static int ds_wait_status(struct ds_device *dev, struct ds_status *st)
{
u8 buf[0x20];
int err, count = 0;
@@ -305,7 +307,7 @@ int ds_wait_status(struct ds_device *dev, struct ds_status *st)
do {
err = ds_recv_status_nodump(dev, st, buf, sizeof(buf));
#if 0
- if (err >= 0) {
+ if (err >= 0) {
int i;
printk("0x%x: count=%d, status: ", dev->ep[EP_STATUS], err);
for (i=0; i<err; ++i)
@@ -319,10 +321,8 @@ int ds_wait_status(struct ds_device *dev, struct ds_status *st)
if (((err > 16) && (buf[0x10] & 0x01)) || count >= 100 || err < 0) {
ds_recv_status(dev, st);
return -1;
- }
- else {
+ } else
return 0;
- }
}
int ds_reset(struct ds_device *dev, struct ds_status *st)
@@ -345,6 +345,7 @@ int ds_reset(struct ds_device *dev, struct ds_status *st)
return 0;
}
+#if 0
int ds_set_speed(struct ds_device *dev, int speed)
{
int err;
@@ -356,20 +357,21 @@ int ds_set_speed(struct ds_device *dev, int speed)
speed = SPEED_FLEXIBLE;
speed &= 0xff;
-
+
err = ds_send_control_mode(dev, MOD_1WIRE_SPEED, speed);
if (err)
return err;
return err;
}
+#endif /* 0 */
-int ds_start_pulse(struct ds_device *dev, int delay)
+static int ds_start_pulse(struct ds_device *dev, int delay)
{
int err;
u8 del = 1 + (u8)(delay >> 4);
struct ds_status st;
-
+
#if 0
err = ds_stop_pulse(dev, 10);
if (err)
@@ -390,7 +392,7 @@ int ds_start_pulse(struct ds_device *dev, int delay)
mdelay(delay);
ds_wait_status(dev, &st);
-
+
return err;
}
@@ -400,7 +402,7 @@ int ds_touch_bit(struct ds_device *dev, u8 bit, u8 *tbit)
struct ds_status st;
u16 value = (COMM_BIT_IO | COMM_IM) | ((bit) ? COMM_D : 0);
u16 cmd;
-
+
err = ds_send_control(dev, value, 0);
if (err)
return err;
@@ -430,7 +432,7 @@ int ds_write_bit(struct ds_device *dev, u8 bit)
{
int err;
struct ds_status st;
-
+
err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | (bit) ? COMM_D : 0, 0);
if (err)
return err;
@@ -445,7 +447,7 @@ int ds_write_byte(struct ds_device *dev, u8 byte)
int err;
struct ds_status st;
u8 rbyte;
-
+
err = ds_send_control(dev, COMM_BYTE_IO | COMM_IM | COMM_SPU, byte);
if (err)
return err;
@@ -453,11 +455,11 @@ int ds_write_byte(struct ds_device *dev, u8 byte)
err = ds_wait_status(dev, &st);
if (err)
return err;
-
+
err = ds_recv_data(dev, &rbyte, sizeof(rbyte));
if (err < 0)
return err;
-
+
ds_start_pulse(dev, PULLUP_PULSE_DURATION);
return !(byte == rbyte);
@@ -470,11 +472,11 @@ int ds_read_bit(struct ds_device *dev, u8 *bit)
err = ds_send_control_mode(dev, MOD_PULSE_EN, PULSE_SPUE);
if (err)
return err;
-
+
err = ds_send_control(dev, COMM_BIT_IO | COMM_IM | COMM_SPU | COMM_D, 0);
if (err)
return err;
-
+
err = ds_recv_data(dev, bit, sizeof(*bit));
if (err < 0)
return err;
@@ -492,7 +494,7 @@ int ds_read_byte(struct ds_device *dev, u8 *byte)
return err;
ds_wait_status(dev, &st);
-
+
err = ds_recv_data(dev, byte, sizeof(*byte));
if (err < 0)
return err;
@@ -509,17 +511,17 @@ int ds_read_block(struct ds_device *dev, u8 *buf, int len)
return -E2BIG;
memset(buf, 0xFF, len);
-
+
err = ds_send_data(dev, buf, len);
if (err < 0)
return err;
-
+
err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | COMM_SPU, len);
if (err)
return err;
ds_wait_status(dev, &st);
-
+
memset(buf, 0x00, len);
err = ds_recv_data(dev, buf, len);
@@ -530,11 +532,11 @@ int ds_write_block(struct ds_device *dev, u8 *buf, int len)
{
int err;
struct ds_status st;
-
+
err = ds_send_data(dev, buf, len);
if (err < 0)
return err;
-
+
ds_wait_status(dev, &st);
err = ds_send_control(dev, COMM_BLOCK_IO | COMM_IM | COMM_SPU, len);
@@ -548,10 +550,12 @@ int ds_write_block(struct ds_device *dev, u8 *buf, int len)
return err;
ds_start_pulse(dev, PULLUP_PULSE_DURATION);
-
+
return !(err == len);
}
+#if 0
+
int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int conditional_search)
{
int err;
@@ -559,11 +563,11 @@ int ds_search(struct ds_device *dev, u64 init, u64 *buf, u8 id_number, int condi
struct ds_status st;
memset(buf, 0, sizeof(buf));
-
+
err = ds_send_data(ds_dev, (unsigned char *)&init, 8);
if (err)
return err;
-
+
ds_wait_status(ds_dev, &st);
value = COMM_SEARCH_ACCESS | COMM_IM | COMM_SM | COMM_F | COMM_RTS;
@@ -589,7 +593,7 @@ int ds_match_access(struct ds_device *dev, u64 init)
err = ds_send_data(dev, (unsigned char *)&init, sizeof(init));
if (err)
return err;
-
+
ds_wait_status(dev, &st);
err = ds_send_control(dev, COMM_MATCH_ACCESS | COMM_IM | COMM_RST, 0x0055);
@@ -609,11 +613,11 @@ int ds_set_path(struct ds_device *dev, u64 init)
memcpy(buf, &init, 8);
buf[8] = BRANCH_MAIN;
-
+
err = ds_send_data(dev, buf, sizeof(buf));
if (err)
return err;
-
+
ds_wait_status(dev, &st);
err = ds_send_control(dev, COMM_SET_PATH | COMM_IM | COMM_RST, 0);
@@ -625,7 +629,10 @@ int ds_set_path(struct ds_device *dev, u64 init)
return 0;
}
-int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id)
+#endif /* 0 */
+
+static int ds_probe(struct usb_interface *intf,
+ const struct usb_device_id *udev_id)
{
struct usb_device *udev = interface_to_usbdev(intf);
struct usb_endpoint_descriptor *endpoint;
@@ -653,7 +660,7 @@ int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id)
printk(KERN_ERR "Failed to reset configuration: err=%d.\n", err);
return err;
}
-
+
iface_desc = &intf->altsetting[0];
if (iface_desc->desc.bNumEndpoints != NUM_EP-1) {
printk(KERN_INFO "Num endpoints=%d. It is not DS9490R.\n", iface_desc->desc.bNumEndpoints);
@@ -662,37 +669,37 @@ int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id)
atomic_set(&ds_dev->refcnt, 0);
memset(ds_dev->ep, 0, sizeof(ds_dev->ep));
-
+
/*
- * This loop doesn'd show control 0 endpoint,
+ * This loop doesn'd show control 0 endpoint,
* so we will fill only 1-3 endpoints entry.
*/
for (i = 0; i < iface_desc->desc.bNumEndpoints; ++i) {
endpoint = &iface_desc->endpoint[i].desc;
ds_dev->ep[i+1] = endpoint->bEndpointAddress;
-
+
printk("%d: addr=%x, size=%d, dir=%s, type=%x\n",
i, endpoint->bEndpointAddress, le16_to_cpu(endpoint->wMaxPacketSize),
(endpoint->bEndpointAddress & USB_DIR_IN)?"IN":"OUT",
endpoint->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK);
}
-
+
#if 0
{
int err, i;
u64 buf[3];
u64 init=0xb30000002078ee81ull;
struct ds_status st;
-
+
ds_reset(ds_dev, &st);
err = ds_search(ds_dev, init, buf, 3, 0);
if (err < 0)
return err;
for (i=0; i<err; ++i)
printk("%d: %llx\n", i, buf[i]);
-
- printk("Resetting...\n");
+
+ printk("Resetting...\n");
ds_reset(ds_dev, &st);
printk("Setting path for %llx.\n", init);
err = ds_set_path(ds_dev, init);
@@ -707,12 +714,12 @@ int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id)
err = ds_search(ds_dev, init, buf, 3, 0);
printk("ds_search() returned %d\n", err);
-
+
if (err < 0)
return err;
for (i=0; i<err; ++i)
printk("%d: %llx\n", i, buf[i]);
-
+
return 0;
}
#endif
@@ -720,10 +727,10 @@ int ds_probe(struct usb_interface *intf, const struct usb_device_id *udev_id)
return 0;
}
-void ds_disconnect(struct usb_interface *intf)
+static void ds_disconnect(struct usb_interface *intf)
{
struct ds_device *dev;
-
+
dev = usb_get_intfdata(intf);
usb_set_intfdata(intf, NULL);
@@ -740,7 +747,7 @@ void ds_disconnect(struct usb_interface *intf)
ds_dev = NULL;
}
-int ds_init(void)
+static int ds_init(void)
{
int err;
@@ -753,7 +760,7 @@ int ds_init(void)
return 0;
}
-void ds_fini(void)
+static void ds_fini(void)
{
usb_deregister(&ds_driver);
}
@@ -776,8 +783,8 @@ EXPORT_SYMBOL(ds_get_device);
EXPORT_SYMBOL(ds_put_device);
/*
- * This functions can be used for EEPROM programming,
- * when driver will be included into mainline this will
+ * This functions can be used for EEPROM programming,
+ * when driver will be included into mainline this will
* require uncommenting.
*/
#if 0
diff --git a/drivers/w1/dscore.h b/drivers/w1/dscore.h
index 9c767ef4ac24..6cf5671d6ebe 100644
--- a/drivers/w1/dscore.h
+++ b/drivers/w1/dscore.h
@@ -1,8 +1,8 @@
/*
- * dscore.h
+ * dscore.h
*
* Copyright (c) 2004 Evgeniy Polyakov <johnpol@2ka.mipt.ru>
- *
+ *
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
@@ -122,7 +122,7 @@
struct ds_device
{
- struct usb_device *udev;
+ struct usb_device *udev;
struct usb_interface *intf;
int ep[NUM_EP];
@@ -156,11 +156,7 @@ int ds_read_byte(struct ds_device *, u8 *);
int ds_read_bit(struct ds_device *, u8 *);
int ds_write_byte(struct ds_device *, u8);
int ds_write_bit(struct ds_device *, u8);
-int ds_start_pulse(struct ds_device *, int);
-int ds_set_speed(struct ds_device *, int);
int ds_reset(struct ds_device *, struct ds_status *);
-int ds_detect(struct ds_device *, struct ds_status *);
-int ds_stop_pulse(struct ds_device *, int);
struct ds_device * ds_get_device(void);
void ds_put_device(struct ds_device *);
int ds_write_block(struct ds_device *, u8 *, int);
diff --git a/drivers/w1/w1.c b/drivers/w1/w1.c
index 0bbf029b1ef1..1b6b74c116a9 100644
--- a/drivers/w1/w1.c
+++ b/drivers/w1/w1.c
@@ -45,10 +45,12 @@ MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol.");
static int w1_timeout = 10;
+static int w1_control_timeout = 1;
int w1_max_slave_count = 10;
int w1_max_slave_ttl = 10;
module_param_named(timeout, w1_timeout, int, 0);
+module_param_named(control_timeout, w1_control_timeout, int, 0);
module_param_named(max_slave_count, w1_max_slave_count, int, 0);
module_param_named(slave_ttl, w1_max_slave_ttl, int, 0);
@@ -59,19 +61,6 @@ static pid_t control_thread;
static int control_needs_exit;
static DECLARE_COMPLETION(w1_control_complete);
-/* stuff for the default family */
-static ssize_t w1_famdefault_read_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
- return(sprintf(buf, "%s\n", sl->name));
-}
-static struct w1_family_ops w1_default_fops = {
- .rname = &w1_famdefault_read_name,
-};
-static struct w1_family w1_default_family = {
- .fops = &w1_default_fops,
-};
-
static int w1_master_match(struct device *dev, struct device_driver *drv)
{
return 1;
@@ -82,73 +71,116 @@ static int w1_master_probe(struct device *dev)
return -ENODEV;
}
-static int w1_master_remove(struct device *dev)
-{
- return 0;
-}
-
static void w1_master_release(struct device *dev)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
- complete(&md->dev_released);
+ dev_dbg(dev, "%s: Releasing %s.\n", __func__, md->name);
+
+ if (md->nls && md->nls->sk_socket)
+ sock_release(md->nls->sk_socket);
+ memset(md, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
+ kfree(md);
}
static void w1_slave_release(struct device *dev)
{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+
+ dev_dbg(dev, "%s: Releasing %s.\n", __func__, sl->name);
+
+ while (atomic_read(&sl->refcnt)) {
+ dev_dbg(dev, "Waiting for %s to become free: refcnt=%d.\n",
+ sl->name, atomic_read(&sl->refcnt));
+ if (msleep_interruptible(1000))
+ flush_signals(current);
+ }
+
+ w1_family_put(sl->family);
+ sl->master->slave_count--;
- complete(&sl->dev_released);
+ complete(&sl->released);
}
-static ssize_t w1_default_read_name(struct device *dev, struct device_attribute *attr, char *buf)
+static ssize_t w1_slave_read_name(struct device *dev, struct device_attribute *attr, char *buf)
{
- return sprintf(buf, "No family registered.\n");
+ struct w1_slave *sl = dev_to_w1_slave(dev);
+
+ return sprintf(buf, "%s\n", sl->name);
}
-static ssize_t w1_default_read_bin(struct kobject *kobj, char *buf, loff_t off,
- size_t count)
+static ssize_t w1_slave_read_id(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
- return sprintf(buf, "No family registered.\n");
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+
+ atomic_inc(&sl->refcnt);
+ if (off > 8) {
+ count = 0;
+ } else {
+ if (off + count > 8)
+ count = 8 - off;
+
+ memcpy(buf, (u8 *)&sl->reg_num, count);
+ }
+ atomic_dec(&sl->refcnt);
+
+ return count;
}
-static struct device_attribute w1_slave_attribute =
- __ATTR(name, S_IRUGO, w1_default_read_name, NULL);
-
-static struct bin_attribute w1_slave_bin_attribute = {
- .attr = {
- .name = "w1_slave",
- .mode = S_IRUGO,
- .owner = THIS_MODULE,
- },
- .size = W1_SLAVE_DATA_SIZE,
- .read = &w1_default_read_bin,
+static struct device_attribute w1_slave_attr_name =
+ __ATTR(name, S_IRUGO, w1_slave_read_name, NULL);
+
+static struct bin_attribute w1_slave_attr_bin_id = {
+ .attr = {
+ .name = "id",
+ .mode = S_IRUGO,
+ .owner = THIS_MODULE,
+ },
+ .size = 8,
+ .read = w1_slave_read_id,
};
+/* Default family */
+static struct w1_family w1_default_family;
+
+static int w1_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size);
static struct bus_type w1_bus_type = {
.name = "w1",
.match = w1_master_match,
+ .hotplug = w1_hotplug,
};
-struct device_driver w1_driver = {
- .name = "w1_driver",
+struct device_driver w1_master_driver = {
+ .name = "w1_master_driver",
.bus = &w1_bus_type,
.probe = w1_master_probe,
- .remove = w1_master_remove,
};
-struct device w1_device = {
+struct device w1_master_device = {
.parent = NULL,
.bus = &w1_bus_type,
.bus_id = "w1 bus master",
- .driver = &w1_driver,
+ .driver = &w1_master_driver,
.release = &w1_master_release
};
+struct device_driver w1_slave_driver = {
+ .name = "w1_slave_driver",
+ .bus = &w1_bus_type,
+};
+
+struct device w1_slave_device = {
+ .parent = NULL,
+ .bus = &w1_bus_type,
+ .bus_id = "w1 bus slave",
+ .driver = &w1_slave_driver,
+ .release = &w1_slave_release
+};
+
static ssize_t w1_master_attribute_show_name(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible (&md->mutex))
@@ -165,7 +197,7 @@ static ssize_t w1_master_attribute_store_search(struct device * dev,
struct device_attribute *attr,
const char * buf, size_t count)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
if (down_interruptible (&md->mutex))
return -EBUSY;
@@ -181,7 +213,7 @@ static ssize_t w1_master_attribute_show_search(struct device *dev,
struct device_attribute *attr,
char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible (&md->mutex))
@@ -196,7 +228,7 @@ static ssize_t w1_master_attribute_show_search(struct device *dev,
static ssize_t w1_master_attribute_show_pointer(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible(&md->mutex))
@@ -217,7 +249,7 @@ static ssize_t w1_master_attribute_show_timeout(struct device *dev, struct devic
static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible(&md->mutex))
@@ -231,7 +263,7 @@ static ssize_t w1_master_attribute_show_max_slave_count(struct device *dev, stru
static ssize_t w1_master_attribute_show_attempts(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible(&md->mutex))
@@ -245,7 +277,7 @@ static ssize_t w1_master_attribute_show_attempts(struct device *dev, struct devi
static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
ssize_t count;
if (down_interruptible(&md->mutex))
@@ -259,7 +291,7 @@ static ssize_t w1_master_attribute_show_slave_count(struct device *dev, struct d
static ssize_t w1_master_attribute_show_slaves(struct device *dev, struct device_attribute *attr, char *buf)
{
- struct w1_master *md = container_of(dev, struct w1_master, dev);
+ struct w1_master *md = dev_to_w1_master(dev);
int c = PAGE_SIZE;
if (down_interruptible(&md->mutex))
@@ -329,12 +361,55 @@ void w1_destroy_master_attributes(struct w1_master *master)
sysfs_remove_group(&master->dev.kobj, &w1_master_defattr_group);
}
+#ifdef CONFIG_HOTPLUG
+static int w1_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+{
+ struct w1_master *md = NULL;
+ struct w1_slave *sl = NULL;
+ char *event_owner, *name;
+ int err, cur_index=0, cur_len=0;
+
+ if (dev->driver == &w1_master_driver) {
+ md = container_of(dev, struct w1_master, dev);
+ event_owner = "master";
+ name = md->name;
+ } else if (dev->driver == &w1_slave_driver) {
+ sl = container_of(dev, struct w1_slave, dev);
+ event_owner = "slave";
+ name = sl->name;
+ } else {
+ dev_dbg(dev, "Unknown hotplug event.\n");
+ return -EINVAL;
+ }
+
+ dev_dbg(dev, "Hotplug event for %s %s, bus_id=%s.\n", event_owner, name, dev->bus_id);
+
+ if (dev->driver != &w1_slave_driver || !sl)
+ return 0;
+
+ err = add_hotplug_env_var(envp, num_envp, &cur_index, buffer, buffer_size, &cur_len, "W1_FID=%02X", sl->reg_num.family);
+ if (err)
+ return err;
+
+ err = add_hotplug_env_var(envp, num_envp, &cur_index, buffer, buffer_size, &cur_len, "W1_SLAVE_ID=%024LX", (u64)sl->reg_num.id);
+ if (err)
+ return err;
+
+ return 0;
+};
+#else
+static int w1_hotplug(struct device *dev, char **envp, int num_envp, char *buffer, int buffer_size)
+{
+ return 0;
+}
+#endif
+
static int __w1_attach_slave_device(struct w1_slave *sl)
{
int err;
sl->dev.parent = &sl->master->dev;
- sl->dev.driver = sl->master->driver;
+ sl->dev.driver = &w1_slave_driver;
sl->dev.bus = &w1_bus_type;
sl->dev.release = &w1_slave_release;
@@ -347,8 +422,7 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
(unsigned int) sl->reg_num.family,
(unsigned long long) sl->reg_num.id);
- dev_dbg(&sl->dev, "%s: registering %s.\n", __func__,
- &sl->dev.bus_id[0]);
+ dev_dbg(&sl->dev, "%s: registering %s as %p.\n", __func__, &sl->dev.bus_id[0]);
err = device_register(&sl->dev);
if (err < 0) {
@@ -358,36 +432,44 @@ static int __w1_attach_slave_device(struct w1_slave *sl)
return err;
}
- memcpy(&sl->attr_bin, &w1_slave_bin_attribute, sizeof(sl->attr_bin));
- memcpy(&sl->attr_name, &w1_slave_attribute, sizeof(sl->attr_name));
-
- sl->attr_bin.read = sl->family->fops->rbin;
- sl->attr_name.show = sl->family->fops->rname;
+ /* Create "name" entry */
+ err = device_create_file(&sl->dev, &w1_slave_attr_name);
+ if (err < 0) {
+ dev_err(&sl->dev,
+ "sysfs file creation for [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ goto out_unreg;
+ }
- err = device_create_file(&sl->dev, &sl->attr_name);
+ /* Create "id" entry */
+ err = sysfs_create_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
if (err < 0) {
dev_err(&sl->dev,
"sysfs file creation for [%s] failed. err=%d\n",
sl->dev.bus_id, err);
- device_unregister(&sl->dev);
- return err;
+ goto out_rem1;
}
- if ( sl->attr_bin.read ) {
- err = sysfs_create_bin_file(&sl->dev.kobj, &sl->attr_bin);
- if (err < 0) {
- dev_err(&sl->dev,
- "sysfs file creation for [%s] failed. err=%d\n",
- sl->dev.bus_id, err);
- device_remove_file(&sl->dev, &sl->attr_name);
- device_unregister(&sl->dev);
- return err;
- }
+ /* if the family driver needs to initialize something... */
+ if (sl->family->fops && sl->family->fops->add_slave &&
+ ((err = sl->family->fops->add_slave(sl)) < 0)) {
+ dev_err(&sl->dev,
+ "sysfs file creation for [%s] failed. err=%d\n",
+ sl->dev.bus_id, err);
+ goto out_rem2;
}
list_add_tail(&sl->w1_slave_entry, &sl->master->slist);
return 0;
+
+out_rem2:
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
+out_rem1:
+ device_remove_file(&sl->dev, &w1_slave_attr_name);
+out_unreg:
+ device_unregister(&sl->dev);
+ return err;
}
static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
@@ -413,7 +495,7 @@ static int w1_attach_slave_device(struct w1_master *dev, struct w1_reg_num *rn)
memcpy(&sl->reg_num, rn, sizeof(sl->reg_num));
atomic_set(&sl->refcnt, 0);
- init_completion(&sl->dev_released);
+ init_completion(&sl->released);
spin_lock(&w1_flock);
f = w1_family_registered(rn->family);
@@ -452,28 +534,23 @@ static void w1_slave_detach(struct w1_slave *sl)
{
struct w1_netlink_msg msg;
- dev_info(&sl->dev, "%s: detaching %s.\n", __func__, sl->name);
-
- while (atomic_read(&sl->refcnt)) {
- printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
- sl->name, atomic_read(&sl->refcnt));
+ dev_dbg(&sl->dev, "%s: detaching %s [%p].\n", __func__, sl->name, sl);
- if (msleep_interruptible(1000))
- flush_signals(current);
- }
-
- if ( sl->attr_bin.read ) {
- sysfs_remove_bin_file (&sl->dev.kobj, &sl->attr_bin);
- }
- device_remove_file(&sl->dev, &sl->attr_name);
- device_unregister(&sl->dev);
- w1_family_put(sl->family);
+ list_del(&sl->w1_slave_entry);
- sl->master->slave_count--;
+ if (sl->family->fops && sl->family->fops->remove_slave)
+ sl->family->fops->remove_slave(sl);
memcpy(&msg.id.id, &sl->reg_num, sizeof(msg.id.id));
msg.type = W1_SLAVE_REMOVE;
w1_netlink_send(sl->master, &msg);
+
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_slave_attr_bin_id);
+ device_remove_file(&sl->dev, &w1_slave_attr_name);
+ device_unregister(&sl->dev);
+
+ wait_for_completion(&sl->released);
+ kfree(sl);
}
static struct w1_master *w1_search_master(unsigned long data)
@@ -500,14 +577,13 @@ void w1_reconnect_slaves(struct w1_family *f)
spin_lock_bh(&w1_mlock);
list_for_each_entry(dev, &w1_masters, w1_master_entry) {
- dev_info(&dev->dev, "Reconnecting slaves in %s into new family %02x.\n",
+ dev_dbg(&dev->dev, "Reconnecting slaves in %s into new family %02x.\n",
dev->name, f->fid);
set_bit(W1_MASTER_NEED_RECONNECT, &dev->flags);
}
spin_unlock_bh(&w1_mlock);
}
-
static void w1_slave_found(unsigned long data, u64 rn)
{
int slave_count;
@@ -646,7 +722,7 @@ static int w1_control(void *data)
have_to_wait = 0;
try_to_freeze();
- msleep_interruptible(w1_timeout * 1000);
+ msleep_interruptible(w1_control_timeout * 1000);
if (signal_pending(current))
flush_signals(current);
@@ -679,33 +755,30 @@ static int w1_control(void *data)
list_del(&dev->w1_master_entry);
spin_unlock_bh(&w1_mlock);
+ down(&dev->mutex);
list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
- list_del(&sl->w1_slave_entry);
-
w1_slave_detach(sl);
- kfree(sl);
}
w1_destroy_master_attributes(dev);
+ up(&dev->mutex);
atomic_dec(&dev->refcnt);
continue;
}
if (test_bit(W1_MASTER_NEED_RECONNECT, &dev->flags)) {
- dev_info(&dev->dev, "Reconnecting slaves in device %s.\n", dev->name);
+ dev_dbg(&dev->dev, "Reconnecting slaves in device %s.\n", dev->name);
down(&dev->mutex);
- list_for_each_entry(sl, &dev->slist, w1_slave_entry) {
+ list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
if (sl->family->fid == W1_FAMILY_DEFAULT) {
struct w1_reg_num rn;
- list_del(&sl->w1_slave_entry);
- w1_slave_detach(sl);
memcpy(&rn, &sl->reg_num, sizeof(rn));
-
- kfree(sl);
+ w1_slave_detach(sl);
w1_attach_slave_device(dev, &rn);
}
}
+ dev_dbg(&dev->dev, "Reconnecting slaves in device %s has been finished.\n", dev->name);
clear_bit(W1_MASTER_NEED_RECONNECT, &dev->flags);
up(&dev->mutex);
}
@@ -749,10 +822,7 @@ int w1_process(void *data)
list_for_each_entry_safe(sl, sln, &dev->slist, w1_slave_entry) {
if (!test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags) && !--sl->ttl) {
- list_del (&sl->w1_slave_entry);
-
- w1_slave_detach (sl);
- kfree (sl);
+ w1_slave_detach(sl);
dev->slave_count--;
} else if (test_bit(W1_SLAVE_ACTIVE, (unsigned long *)&sl->flags))
@@ -783,7 +853,7 @@ static int w1_init(void)
goto err_out_exit_init;
}
- retval = driver_register(&w1_driver);
+ retval = driver_register(&w1_master_driver);
if (retval) {
printk(KERN_ERR
"Failed to register master driver. err=%d.\n",
@@ -791,18 +861,29 @@ static int w1_init(void)
goto err_out_bus_unregister;
}
+ retval = driver_register(&w1_slave_driver);
+ if (retval) {
+ printk(KERN_ERR
+ "Failed to register master driver. err=%d.\n",
+ retval);
+ goto err_out_master_unregister;
+ }
+
control_thread = kernel_thread(&w1_control, NULL, 0);
if (control_thread < 0) {
printk(KERN_ERR "Failed to create control thread. err=%d\n",
control_thread);
retval = control_thread;
- goto err_out_driver_unregister;
+ goto err_out_slave_unregister;
}
return 0;
-err_out_driver_unregister:
- driver_unregister(&w1_driver);
+err_out_slave_unregister:
+ driver_unregister(&w1_slave_driver);
+
+err_out_master_unregister:
+ driver_unregister(&w1_master_driver);
err_out_bus_unregister:
bus_unregister(&w1_bus_type);
@@ -821,7 +902,8 @@ static void w1_fini(void)
control_needs_exit = 1;
wait_for_completion(&w1_control_complete);
- driver_unregister(&w1_driver);
+ driver_unregister(&w1_slave_driver);
+ driver_unregister(&w1_master_driver);
bus_unregister(&w1_bus_type);
}
diff --git a/drivers/w1/w1.h b/drivers/w1/w1.h
index 4f0a986e33e3..d8900780c3bf 100644
--- a/drivers/w1/w1.h
+++ b/drivers/w1/w1.h
@@ -75,11 +75,9 @@ struct w1_slave
struct w1_master *master;
struct w1_family *family;
+ void *family_data;
struct device dev;
- struct completion dev_released;
-
- struct bin_attribute attr_bin;
- struct device_attribute attr_name;
+ struct completion released;
};
typedef void (* w1_slave_found_callback)(unsigned long, u64);
@@ -179,7 +177,6 @@ struct w1_master
struct device_driver *driver;
struct device dev;
- struct completion dev_released;
struct completion dev_exited;
struct w1_bus_master *bus_master;
@@ -191,6 +188,21 @@ struct w1_master
int w1_create_master_attributes(struct w1_master *);
void w1_search(struct w1_master *dev, w1_slave_found_callback cb);
+static inline struct w1_slave* dev_to_w1_slave(struct device *dev)
+{
+ return container_of(dev, struct w1_slave, dev);
+}
+
+static inline struct w1_slave* kobj_to_w1_slave(struct kobject *kobj)
+{
+ return dev_to_w1_slave(container_of(kobj, struct device, kobj));
+}
+
+static inline struct w1_master* dev_to_w1_master(struct device *dev)
+{
+ return container_of(dev, struct w1_master, dev);
+}
+
#endif /* __KERNEL__ */
#endif /* __W1_H */
diff --git a/drivers/w1/w1_ds2433.c b/drivers/w1/w1_ds2433.c
new file mode 100644
index 000000000000..b7c24b34d270
--- /dev/null
+++ b/drivers/w1/w1_ds2433.c
@@ -0,0 +1,327 @@
+/*
+ * w1_ds2433.c - w1 family 23 (DS2433) driver
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * 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>
+#ifdef CONFIG_W1_F23_CRC
+#include <linux/crc16.h>
+#endif
+
+#include "w1.h"
+#include "w1_io.h"
+#include "w1_int.h"
+#include "w1_family.h"
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Gardner <bgardner@wabtec.com>");
+MODULE_DESCRIPTION("w1 family 23 driver for DS2433, 4kb EEPROM");
+
+#define W1_EEPROM_SIZE 512
+#define W1_PAGE_COUNT 16
+#define W1_PAGE_SIZE 32
+#define W1_PAGE_BITS 5
+#define W1_PAGE_MASK 0x1F
+
+#define W1_F23_TIME 300
+
+#define W1_F23_READ_EEPROM 0xF0
+#define W1_F23_WRITE_SCRATCH 0x0F
+#define W1_F23_READ_SCRATCH 0xAA
+#define W1_F23_COPY_SCRATCH 0x55
+
+struct w1_f23_data {
+ u8 memory[W1_EEPROM_SIZE];
+ u32 validcrc;
+};
+
+/**
+ * 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_f23_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;
+}
+
+#ifdef CONFIG_W1_F23_CRC
+static int w1_f23_refresh_block(struct w1_slave *sl, struct w1_f23_data *data,
+ int block)
+{
+ u8 wrbuf[3];
+ int off = block * W1_PAGE_SIZE;
+
+ if (data->validcrc & (1 << block))
+ return 0;
+
+ if (w1_reset_select_slave(sl)) {
+ data->validcrc = 0;
+ return -EIO;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, &data->memory[off], W1_PAGE_SIZE);
+
+ /* cache the block if the CRC is valid */
+ if (crc16(CRC16_INIT, &data->memory[off], W1_PAGE_SIZE) == CRC16_VALID)
+ data->validcrc |= (1 << block);
+
+ return 0;
+}
+#endif /* CONFIG_W1_F23_CRC */
+
+static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+#ifdef CONFIG_W1_F23_CRC
+ struct w1_f23_data *data = sl->family_data;
+ int i, min_page, max_page;
+#else
+ u8 wrbuf[3];
+#endif
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+ atomic_inc(&sl->refcnt);
+ if (down_interruptible(&sl->master->mutex)) {
+ count = 0;
+ goto out_dec;
+ }
+
+#ifdef CONFIG_W1_F23_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_f23_refresh_block(sl, data, i)) {
+ count = -EIO;
+ goto out_up;
+ }
+ }
+ memcpy(buf, &data->memory[off], count);
+
+#else /* CONFIG_W1_F23_CRC */
+
+ /* read directly from the EEPROM */
+ if (w1_reset_select_slave(sl)) {
+ count = -EIO;
+ goto out_up;
+ }
+
+ wrbuf[0] = W1_F23_READ_EEPROM;
+ wrbuf[1] = off & 0xff;
+ wrbuf[2] = off >> 8;
+ w1_write_block(sl->master, wrbuf, 3);
+ w1_read_block(sl->master, buf, count);
+
+#endif /* CONFIG_W1_F23_CRC */
+
+out_up:
+ up(&sl->master->mutex);
+out_dec:
+ atomic_dec(&sl->refcnt);
+
+ 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_f23_write(struct w1_slave *sl, int addr, int len, const u8 *data)
+{
+ 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_F23_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_F23_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_F23_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);
+
+ return 0;
+}
+
+static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off,
+ size_t count)
+{
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
+ int addr, len, idx;
+
+ if ((count = w1_f23_fix_count(off, count, W1_EEPROM_SIZE)) == 0)
+ return 0;
+
+#ifdef CONFIG_W1_F23_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=%d\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\n", (int)off);
+ return -EINVAL;
+ }
+ }
+#endif /* CONFIG_W1_F23_CRC */
+
+ atomic_inc(&sl->refcnt);
+ if (down_interruptible(&sl->master->mutex)) {
+ count = 0;
+ goto out_dec;
+ }
+
+ /* 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_f23_write(sl, addr, len, &buf[idx]) < 0) {
+ count = -EIO;
+ goto out_up;
+ }
+ idx += len;
+ }
+
+out_up:
+ up(&sl->master->mutex);
+out_dec:
+ atomic_dec(&sl->refcnt);
+
+ return count;
+}
+
+static struct bin_attribute w1_f23_bin_attr = {
+ .attr = {
+ .name = "eeprom",
+ .mode = S_IRUGO | S_IWUSR,
+ .owner = THIS_MODULE,
+ },
+ .size = W1_EEPROM_SIZE,
+ .read = w1_f23_read_bin,
+ .write = w1_f23_write_bin,
+};
+
+static int w1_f23_add_slave(struct w1_slave *sl)
+{
+ int err;
+#ifdef CONFIG_W1_F23_CRC
+ struct w1_f23_data *data;
+
+ data = kmalloc(sizeof(struct w1_f23_data), GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+ memset(data, 0, sizeof(struct w1_f23_data));
+ sl->family_data = data;
+
+#endif /* CONFIG_W1_F23_CRC */
+
+ err = sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
+
+#ifdef CONFIG_W1_F23_CRC
+ if (err)
+ kfree(data);
+#endif /* CONFIG_W1_F23_CRC */
+
+ return err;
+}
+
+static void w1_f23_remove_slave(struct w1_slave *sl)
+{
+#ifdef CONFIG_W1_F23_CRC
+ if (sl->family_data) {
+ kfree(sl->family_data);
+ sl->family_data = NULL;
+ }
+#endif /* CONFIG_W1_F23_CRC */
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_f23_bin_attr);
+}
+
+static struct w1_family_ops w1_f23_fops = {
+ .add_slave = w1_f23_add_slave,
+ .remove_slave = w1_f23_remove_slave,
+};
+
+static struct w1_family w1_family_23 = {
+ .fid = W1_EEPROM_DS2433,
+ .fops = &w1_f23_fops,
+};
+
+static int __init w1_f23_init(void)
+{
+ return w1_register_family(&w1_family_23);
+}
+
+static void __exit w1_f23_fini(void)
+{
+ w1_unregister_family(&w1_family_23);
+}
+
+module_init(w1_f23_init);
+module_exit(w1_f23_fini);
diff --git a/drivers/w1/w1_family.c b/drivers/w1/w1_family.c
index 02eee57d3c0c..88c517a4c178 100644
--- a/drivers/w1/w1_family.c
+++ b/drivers/w1/w1_family.c
@@ -29,23 +29,12 @@ DEFINE_SPINLOCK(w1_flock);
static LIST_HEAD(w1_families);
extern void w1_reconnect_slaves(struct w1_family *f);
-static int w1_check_family(struct w1_family *f)
-{
- if (!f->fops->rname || !f->fops->rbin)
- return -EINVAL;
-
- return 0;
-}
-
int w1_register_family(struct w1_family *newf)
{
struct list_head *ent, *n;
struct w1_family *f;
int ret = 0;
- if (w1_check_family(newf))
- return -EINVAL;
-
spin_lock(&w1_flock);
list_for_each_safe(ent, n, &w1_families) {
f = list_entry(ent, struct w1_family, family_entry);
diff --git a/drivers/w1/w1_family.h b/drivers/w1/w1_family.h
index b26da01bbc38..2ca0489c716a 100644
--- a/drivers/w1/w1_family.h
+++ b/drivers/w1/w1_family.h
@@ -31,14 +31,17 @@
#define W1_FAMILY_SMEM_81 0x81
#define W1_THERM_DS18S20 0x10
#define W1_THERM_DS1822 0x22
+#define W1_EEPROM_DS2433 0x23
#define W1_THERM_DS18B20 0x28
#define MAXNAMELEN 32
+struct w1_slave;
+
struct w1_family_ops
{
- ssize_t (* rname)(struct device *, struct device_attribute *, char *);
- ssize_t (* rbin)(struct kobject *, char *, loff_t, size_t);
+ int (* add_slave)(struct w1_slave *);
+ void (* remove_slave)(struct w1_slave *);
};
struct w1_family
diff --git a/drivers/w1/w1_int.c b/drivers/w1/w1_int.c
index 498ad505fa5f..c3f67eafc7ec 100644
--- a/drivers/w1/w1_int.c
+++ b/drivers/w1/w1_int.c
@@ -29,9 +29,9 @@
static u32 w1_ids = 1;
-extern struct device_driver w1_driver;
+extern struct device_driver w1_master_driver;
extern struct bus_type w1_bus_type;
-extern struct device w1_device;
+extern struct device w1_master_device;
extern int w1_max_slave_count;
extern int w1_max_slave_ttl;
extern struct list_head w1_masters;
@@ -76,7 +76,6 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
INIT_LIST_HEAD(&dev->slist);
init_MUTEX(&dev->mutex);
- init_completion(&dev->dev_released);
init_completion(&dev->dev_exited);
memcpy(&dev->dev, device, sizeof(struct device));
@@ -88,17 +87,14 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
dev->groups = 1;
dev->seq = 1;
- dev->nls = netlink_kernel_create(NETLINK_W1, 1, NULL, THIS_MODULE);
- if (!dev->nls) {
- printk(KERN_ERR "Failed to create new netlink socket(%u) for w1 master %s.\n",
- NETLINK_NFLOG, dev->dev.bus_id);
- }
+ dev_init_netlink(dev);
err = device_register(&dev->dev);
if (err) {
printk(KERN_ERR "Failed to register master device. err=%d\n", err);
- if (dev->nls && dev->nls->sk_socket)
- sock_release(dev->nls->sk_socket);
+
+ dev_fini_netlink(dev);
+
memset(dev, 0, sizeof(struct w1_master));
kfree(dev);
dev = NULL;
@@ -107,13 +103,9 @@ static struct w1_master * w1_alloc_dev(u32 id, int slave_count, int slave_ttl,
return dev;
}
-static void w1_free_dev(struct w1_master *dev)
+void w1_free_dev(struct w1_master *dev)
{
device_unregister(&dev->dev);
- if (dev->nls && dev->nls->sk_socket)
- sock_release(dev->nls->sk_socket);
- memset(dev, 0, sizeof(struct w1_master) + sizeof(struct w1_bus_master));
- kfree(dev);
}
int w1_add_master_device(struct w1_bus_master *master)
@@ -129,7 +121,7 @@ int w1_add_master_device(struct w1_bus_master *master)
return(-EINVAL);
}
- dev = w1_alloc_dev(w1_ids++, w1_max_slave_count, w1_max_slave_ttl, &w1_driver, &w1_device);
+ dev = w1_alloc_dev(w1_ids++, w1_max_slave_count, w1_max_slave_ttl, &w1_master_driver, &w1_master_device);
if (!dev)
return -ENOMEM;
@@ -188,7 +180,7 @@ void __w1_remove_master_device(struct w1_master *dev)
__func__, dev->kpid);
while (atomic_read(&dev->refcnt)) {
- printk(KERN_INFO "Waiting for %s to become free: refcnt=%d.\n",
+ dev_dbg(&dev->dev, "Waiting for %s to become free: refcnt=%d.\n",
dev->name, atomic_read(&dev->refcnt));
if (msleep_interruptible(1000))
diff --git a/drivers/w1/w1_io.c b/drivers/w1/w1_io.c
index 00f032220173..e2a043354ddf 100644
--- a/drivers/w1/w1_io.c
+++ b/drivers/w1/w1_io.c
@@ -277,6 +277,29 @@ void w1_search_devices(struct w1_master *dev, w1_slave_found_callback cb)
w1_search(dev, cb);
}
+/**
+ * Resets the bus and then selects the slave by sending either a skip rom
+ * or a rom match.
+ * The w1 master lock must be held.
+ *
+ * @param sl the slave to select
+ * @return 0=success, anything else=error
+ */
+int w1_reset_select_slave(struct w1_slave *sl)
+{
+ if (w1_reset_bus(sl->master))
+ return -1;
+
+ if (sl->master->slave_count == 1)
+ w1_write_8(sl->master, W1_SKIP_ROM);
+ else {
+ u8 match[9] = {W1_MATCH_ROM, };
+ memcpy(&match[1], (u8 *)&sl->reg_num, 8);
+ w1_write_block(sl->master, match, 9);
+ }
+ return 0;
+}
+
EXPORT_SYMBOL(w1_touch_bit);
EXPORT_SYMBOL(w1_write_8);
EXPORT_SYMBOL(w1_read_8);
@@ -286,3 +309,4 @@ EXPORT_SYMBOL(w1_delay);
EXPORT_SYMBOL(w1_read_block);
EXPORT_SYMBOL(w1_write_block);
EXPORT_SYMBOL(w1_search_devices);
+EXPORT_SYMBOL(w1_reset_select_slave);
diff --git a/drivers/w1/w1_io.h b/drivers/w1/w1_io.h
index af5829778aaa..232860184a29 100644
--- a/drivers/w1/w1_io.h
+++ b/drivers/w1/w1_io.h
@@ -34,5 +34,6 @@ u8 w1_calc_crc8(u8 *, int);
void w1_write_block(struct w1_master *, const u8 *, int);
u8 w1_read_block(struct w1_master *, u8 *, int);
void w1_search_devices(struct w1_master *dev, w1_slave_found_callback cb);
+int w1_reset_select_slave(struct w1_slave *sl);
#endif /* __W1_IO_H */
diff --git a/drivers/w1/w1_netlink.c b/drivers/w1/w1_netlink.c
index e7b774423dd6..328645da7972 100644
--- a/drivers/w1/w1_netlink.c
+++ b/drivers/w1/w1_netlink.c
@@ -57,10 +57,36 @@ void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
nlmsg_failure:
return;
}
+
+int dev_init_netlink(struct w1_master *dev)
+{
+ dev->nls = netlink_kernel_create(NETLINK_W1, 1, NULL, THIS_MODULE);
+ if (!dev->nls) {
+ printk(KERN_ERR "Failed to create new netlink socket(%u) for w1 master %s.\n",
+ NETLINK_W1, dev->dev.bus_id);
+ }
+
+ return 0;
+}
+
+void dev_fini_netlink(struct w1_master *dev)
+{
+ if (dev->nls && dev->nls->sk_socket)
+ sock_release(dev->nls->sk_socket);
+}
#else
#warning Netlink support is disabled. Please compile with NET support enabled.
void w1_netlink_send(struct w1_master *dev, struct w1_netlink_msg *msg)
{
}
+
+int dev_init_netlink(struct w1_master *dev)
+{
+ return 0;
+}
+
+void dev_fini_netlink(struct w1_master *dev)
+{
+}
#endif
diff --git a/drivers/w1/w1_netlink.h b/drivers/w1/w1_netlink.h
index 8615756946df..eb0c8b3152c8 100644
--- a/drivers/w1/w1_netlink.h
+++ b/drivers/w1/w1_netlink.h
@@ -52,6 +52,8 @@ struct w1_netlink_msg
#ifdef __KERNEL__
void w1_netlink_send(struct w1_master *, struct w1_netlink_msg *);
+int dev_init_netlink(struct w1_master *dev);
+void dev_fini_netlink(struct w1_master *dev);
#endif /* __KERNEL__ */
#endif /* __W1_NETLINK_H */
diff --git a/drivers/w1/w1_smem.c b/drivers/w1/w1_smem.c
index 70d2d469963c..e3209d0aca9b 100644
--- a/drivers/w1/w1_smem.c
+++ b/drivers/w1/w1_smem.c
@@ -36,61 +36,12 @@ MODULE_LICENSE("GPL");
MODULE_AUTHOR("Evgeniy Polyakov <johnpol@2ka.mipt.ru>");
MODULE_DESCRIPTION("Driver for 1-wire Dallas network protocol, 64bit memory family.");
-static ssize_t w1_smem_read_name(struct device *, struct device_attribute *attr, char *);
-static ssize_t w1_smem_read_bin(struct kobject *, char *, loff_t, size_t);
-
-static struct w1_family_ops w1_smem_fops = {
- .rname = &w1_smem_read_name,
- .rbin = &w1_smem_read_bin,
-};
-
-static ssize_t w1_smem_read_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
- return sprintf(buf, "%s\n", sl->name);
-}
-
-static ssize_t w1_smem_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
-{
- struct w1_slave *sl = container_of(container_of(kobj, struct device, kobj),
- struct w1_slave, dev);
- int i;
-
- atomic_inc(&sl->refcnt);
- if (down_interruptible(&sl->master->mutex)) {
- count = 0;
- goto out_dec;
- }
-
- if (off > W1_SLAVE_DATA_SIZE) {
- count = 0;
- goto out;
- }
- if (off + count > W1_SLAVE_DATA_SIZE) {
- count = 0;
- goto out;
- }
- for (i = 0; i < 8; ++i)
- count += sprintf(buf + count, "%02x ", ((u8 *)&sl->reg_num)[i]);
- count += sprintf(buf + count, "\n");
-
-out:
- up(&sl->master->mutex);
-out_dec:
- atomic_dec(&sl->refcnt);
-
- return count;
-}
-
static struct w1_family w1_smem_family_01 = {
.fid = W1_FAMILY_SMEM_01,
- .fops = &w1_smem_fops,
};
static struct w1_family w1_smem_family_81 = {
.fid = W1_FAMILY_SMEM_81,
- .fops = &w1_smem_fops,
};
static int __init w1_smem_init(void)
diff --git a/drivers/w1/w1_therm.c b/drivers/w1/w1_therm.c
index 165526c9360a..4577df3cfc48 100644
--- a/drivers/w1/w1_therm.c
+++ b/drivers/w1/w1_therm.c
@@ -42,12 +42,31 @@ static u8 bad_roms[][9] = {
{}
};
-static ssize_t w1_therm_read_name(struct device *, struct device_attribute *attr, char *);
static ssize_t w1_therm_read_bin(struct kobject *, char *, loff_t, size_t);
+static struct bin_attribute w1_therm_bin_attr = {
+ .attr = {
+ .name = "w1_slave",
+ .mode = S_IRUGO,
+ .owner = THIS_MODULE,
+ },
+ .size = W1_SLAVE_DATA_SIZE,
+ .read = w1_therm_read_bin,
+};
+
+static int w1_therm_add_slave(struct w1_slave *sl)
+{
+ return sysfs_create_bin_file(&sl->dev.kobj, &w1_therm_bin_attr);
+}
+
+static void w1_therm_remove_slave(struct w1_slave *sl)
+{
+ sysfs_remove_bin_file(&sl->dev.kobj, &w1_therm_bin_attr);
+}
+
static struct w1_family_ops w1_therm_fops = {
- .rname = &w1_therm_read_name,
- .rbin = &w1_therm_read_bin,
+ .add_slave = w1_therm_add_slave,
+ .remove_slave = w1_therm_remove_slave,
};
static struct w1_family w1_therm_family_DS18S20 = {
@@ -59,6 +78,7 @@ static struct w1_family w1_therm_family_DS18B20 = {
.fid = W1_THERM_DS18B20,
.fops = &w1_therm_fops,
};
+
static struct w1_family w1_therm_family_DS1822 = {
.fid = W1_THERM_DS1822,
.fops = &w1_therm_fops,
@@ -90,13 +110,6 @@ static struct w1_therm_family_converter w1_therm_families[] = {
},
};
-static ssize_t w1_therm_read_name(struct device *dev, struct device_attribute *attr, char *buf)
-{
- struct w1_slave *sl = container_of(dev, struct w1_slave, dev);
-
- return sprintf(buf, "%s\n", sl->name);
-}
-
static inline int w1_DS18B20_convert_temp(u8 rom[9])
{
int t = (rom[1] << 8) | rom[0];
@@ -148,8 +161,7 @@ static int w1_therm_check_rom(u8 rom[9])
static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t count)
{
- struct w1_slave *sl = container_of(container_of(kobj, struct device, kobj),
- struct w1_slave, dev);
+ struct w1_slave *sl = kobj_to_w1_slave(kobj);
struct w1_master *dev = sl->master;
u8 rom[9], crc, verdict;
int i, max_trying = 10;
@@ -178,15 +190,10 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, si
crc = 0;
while (max_trying--) {
- if (!w1_reset_bus (dev)) {
+ if (!w1_reset_select_slave(sl)) {
int count = 0;
- u8 match[9] = {W1_MATCH_ROM, };
unsigned int tm = 750;
- memcpy(&match[1], (u64 *) & sl->reg_num, 8);
-
- w1_write_block(dev, match, 9);
-
w1_write_8(dev, W1_CONVERT_TEMP);
while (tm) {
@@ -195,8 +202,7 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, si
flush_signals(current);
}
- if (!w1_reset_bus (dev)) {
- w1_write_block(dev, match, 9);
+ if (!w1_reset_select_slave(sl)) {
w1_write_8(dev, W1_READ_SCRATCHPAD);
if ((count = w1_read_block(dev, rom, 9)) != 9) {
@@ -207,7 +213,6 @@ static ssize_t w1_therm_read_bin(struct kobject *kobj, char *buf, loff_t off, si
if (rom[8] == crc && rom[0])
verdict = 1;
-
}
}
diff --git a/include/linux/crc16.h b/include/linux/crc16.h
new file mode 100644
index 000000000000..bdedf825b04a
--- /dev/null
+++ b/include/linux/crc16.h
@@ -0,0 +1,44 @@
+/*
+ * crc16.h - CRC-16 routine
+ *
+ * Implements the standard CRC-16, as used with 1-wire devices:
+ * Width 16
+ * Poly 0x8005 (x^16 + x^15 + x^2 + 1)
+ * Init 0
+ *
+ * For 1-wire devices, the CRC is stored inverted, LSB-first
+ *
+ * Example buffer with the CRC attached:
+ * 31 32 33 34 35 36 37 38 39 C2 44
+ *
+ * The CRC over a buffer with the CRC attached is 0xB001.
+ * So, if (crc16(0, buf, size) == 0xB001) then the buffer is valid.
+ *
+ * Refer to "Application Note 937: Book of iButton Standards" for details.
+ * http://www.maxim-ic.com/appnotes.cfm/appnote_number/937
+ *
+ * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com>
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#ifndef __CRC16_H
+#define __CRC16_H
+
+#include <linux/types.h>
+
+#define CRC16_INIT 0
+#define CRC16_VALID 0xb001
+
+extern u16 const crc16_table[256];
+
+extern u16 crc16(u16 crc, const u8 *buffer, size_t len);
+
+static inline u16 crc16_byte(u16 crc, const u8 data)
+{
+ return (crc >> 8) ^ crc16_table[(crc ^ data) & 0xff];
+}
+
+#endif /* __CRC16_H */
+
diff --git a/lib/Kconfig b/lib/Kconfig
index e43197efeb9c..3de93357f5ab 100644
--- a/lib/Kconfig
+++ b/lib/Kconfig
@@ -12,6 +12,14 @@ config CRC_CCITT
the kernel tree does. Such modules that use library CRC-CCITT
functions require M here.
+config CRC16
+ tristate "CRC16 functions"
+ help
+ This option is provided for the case where no in-kernel-tree
+ modules require CRC16 functions, but a module built outside
+ the kernel tree does. Such modules that use library CRC16
+ functions require M here.
+
config CRC32
tristate "CRC32 functions"
default y
diff --git a/lib/Makefile b/lib/Makefile
index 3e2bd0df23bb..d9c38ba05e7b 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -23,11 +23,12 @@ lib-$(CONFIG_GENERIC_FIND_NEXT_BIT) += find_next_bit.o
obj-$(CONFIG_LOCK_KERNEL) += kernel_lock.o
obj-$(CONFIG_DEBUG_PREEMPT) += smp_processor_id.o
-ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
+ifneq ($(CONFIG_HAVE_DEC_LOCK),y)
lib-y += dec_and_lock.o
endif
obj-$(CONFIG_CRC_CCITT) += crc-ccitt.o
+obj-$(CONFIG_CRC16) += crc16.o
obj-$(CONFIG_CRC32) += crc32.o
obj-$(CONFIG_LIBCRC32C) += libcrc32c.o
obj-$(CONFIG_GENERIC_IOMAP) += iomap.o
diff --git a/lib/crc16.c b/lib/crc16.c
new file mode 100644
index 000000000000..011fe573c666
--- /dev/null
+++ b/lib/crc16.c
@@ -0,0 +1,67 @@
+/*
+ * crc16.c
+ *
+ * This source code is licensed under the GNU General Public License,
+ * Version 2. See the file COPYING for more details.
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/crc16.h>
+
+/** CRC table for the CRC-16. The poly is 0x8005 (x^16 + x^15 + x^2 + 1) */
+u16 const crc16_table[256] = {
+ 0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
+ 0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
+ 0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
+ 0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
+ 0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
+ 0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
+ 0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
+ 0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
+ 0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
+ 0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
+ 0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
+ 0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
+ 0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
+ 0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
+ 0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
+ 0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
+ 0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
+ 0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
+ 0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
+ 0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
+ 0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
+ 0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
+ 0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
+ 0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
+ 0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
+ 0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
+ 0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
+ 0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
+ 0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
+ 0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
+ 0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
+ 0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040
+};
+EXPORT_SYMBOL(crc16_table);
+
+/**
+ * Compute the CRC-16 for the data buffer
+ *
+ * @param crc previous CRC value
+ * @param buffer data pointer
+ * @param len number of bytes in the buffer
+ * @return the updated CRC value
+ */
+u16 crc16(u16 crc, u8 const *buffer, size_t len)
+{
+ while (len--)
+ crc = crc16_byte(crc, *buffer++);
+ return crc;
+}
+EXPORT_SYMBOL(crc16);
+
+MODULE_DESCRIPTION("CRC16 calculations");
+MODULE_LICENSE("GPL");
+