diff options
author | Evgeniy Polyakov <johnpol@2ka.mipt.ru> | 2005-08-17 13:24:37 +0200 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2005-09-08 23:41:27 +0200 |
commit | 0a25e4d5647003a32ba5496f9d0f40ba9c1e3863 (patch) | |
tree | 678a0f192d7b72270ea3431f642baca9566a249b /drivers/w1/w1_ds2433.c | |
parent | [PATCH] w1: added private family data into w1_slave strucutre. (diff) | |
download | linux-0a25e4d5647003a32ba5496f9d0f40ba9c1e3863.tar.xz linux-0a25e4d5647003a32ba5496f9d0f40ba9c1e3863.zip |
[PATCH] w1_ds2433: Added crc16 protection and read caching.
The changes to ds2433 to add CRC16 protection and read caching.
Signed-off-by: Ben Gardner <bgardner@wabtec.com>
Signed-off-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/w1/w1_ds2433.c')
-rw-r--r-- | drivers/w1/w1_ds2433.c | 117 |
1 files changed, 111 insertions, 6 deletions
diff --git a/drivers/w1/w1_ds2433.c b/drivers/w1/w1_ds2433.c index 9ec9163a0a9b..b7c24b34d270 100644 --- a/drivers/w1/w1_ds2433.c +++ b/drivers/w1/w1_ds2433.c @@ -3,9 +3,8 @@ * * Copyright (c) 2005 Ben Gardner <bgardner@wabtec.com> * - * This program is free software; you can redistribute it and/or modify - * it under the smems of the GNU General Public License as published by - * the Free Software Foundation; version 2 of the License. + * This source code is licensed under the GNU General Public License, + * Version 2. See the file COPYING for more details. */ #include <linux/kernel.h> @@ -14,6 +13,9 @@ #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" @@ -25,18 +27,26 @@ 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 may not be needed if the sysfs layer checks bounds. + * 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) { @@ -49,10 +59,45 @@ static inline size_t w1_f23_fix_count(loff_t off, size_t count, size_t size) return count; } -static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size_t 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; @@ -63,6 +108,20 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size 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; @@ -75,6 +134,8 @@ static ssize_t w1_f23_read_bin(struct kobject *kobj, char *buf, loff_t off, size 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: @@ -85,6 +146,8 @@ out_dec: /** * 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 @@ -148,6 +211,23 @@ static ssize_t w1_f23_write_bin(struct kobject *kobj, char *buf, loff_t off, 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; @@ -190,11 +270,36 @@ static struct bin_attribute w1_f23_bin_attr = { static int w1_f23_add_slave(struct w1_slave *sl) { - return sysfs_create_bin_file(&sl->dev.kobj, &w1_f23_bin_attr); + 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); } |