linux/drivers/w1/slaves/w1_ds2423.c
Mika Laitio 17fecb5582 w1: DS2423 counter driver and documentation
This is a 1-wire/w1 DS2423 slave driver for reading the values from all 4
counters available DS2423 devices by using standard w1_slave file.  In
ds2423 the counters are tied to ram pages 12-15 in and each of those
ram-pages.  Each of these counter values (and asoociated ram page values)
are represented as a own line in w1_slave file.  Driver has been tested on
mips and x86.

usage example:
cat /sys/bus/w1/devices/1d-00000009b964/w1_slave

00 02 00 00 00 00 00 00 00 6d 38 00 ff ff 00 00 fe ff 00 00 ff ff 00 00 ff
ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=2
00 02 00 00 00 00 00 00 00 e0 1f 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff
ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=2
00 5a 0e 5f 18 00 00 00 00 0b 28 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff
ff 00 00 ff ff 00 00 ff ff 00 00 ff ff 00 00 ff ff crc=YES c=408882778
00 05 00 00 00 00 00 00 00 8d 39 ff ff ff ff ff ff ff ff ff ff ff ff ff ff
ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff crc=YES c=5

Patch includes also the documentation.

[randy.dunlap@oracle.com: fix ds2423 build, needs to select CRC16]
Signed-off-by: Mika Laitio <lamikr@pilppa.org>
Signed-off-by: Randy Dunlap <randy.dunlap@oracle.com>
Cc: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2011-01-13 08:03:22 -08:00

167 lines
4.3 KiB
C

/*
* w1_ds2423.c
*
* Copyright (c) 2010 Mika Laitio <lamikr@pilppa.org>
*
* This driver will read and write the value of 4 counters to w1_slave file in
* sys filesystem.
* Inspired by the w1_therm and w1_ds2431 drivers.
*
* This program is free software; you can redistribute it and/or modify
* it under the therms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#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/crc16.h>
#include "../w1.h"
#include "../w1_int.h"
#include "../w1_family.h"
#define CRC16_VALID 0xb001
#define CRC16_INIT 0
#define COUNTER_COUNT 4
#define READ_BYTE_COUNT 42
static ssize_t w1_counter_read(struct device *device,
struct device_attribute *attr, char *buf);
static struct device_attribute w1_counter_attr =
__ATTR(w1_slave, S_IRUGO, w1_counter_read, NULL);
static ssize_t w1_counter_read(struct device *device,
struct device_attribute *attr, char *out_buf)
{
struct w1_slave *sl = dev_to_w1_slave(device);
struct w1_master *dev = sl->master;
u8 rbuf[COUNTER_COUNT * READ_BYTE_COUNT];
u8 wrbuf[3];
int rom_addr;
int read_byte_count;
int result;
ssize_t c;
int ii;
int p;
int crc;
c = PAGE_SIZE;
rom_addr = (12 << 5) + 31;
wrbuf[0] = 0xA5;
wrbuf[1] = rom_addr & 0xFF;
wrbuf[2] = rom_addr >> 8;
mutex_lock(&dev->mutex);
if (!w1_reset_select_slave(sl)) {
w1_write_block(dev, wrbuf, 3);
read_byte_count = 0;
for (p = 0; p < 4; p++) {
/*
* 1 byte for first bytes in ram page read
* 4 bytes for counter
* 4 bytes for zero bits
* 2 bytes for crc
* 31 remaining bytes from the ram page
*/
read_byte_count += w1_read_block(dev,
rbuf + (p * READ_BYTE_COUNT), READ_BYTE_COUNT);
for (ii = 0; ii < READ_BYTE_COUNT; ++ii)
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "%02x ",
rbuf[(p * READ_BYTE_COUNT) + ii]);
if (read_byte_count != (p + 1) * READ_BYTE_COUNT) {
dev_warn(device,
"w1_counter_read() returned %u bytes "
"instead of %d bytes wanted.\n",
read_byte_count,
READ_BYTE_COUNT);
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=NO\n");
} else {
if (p == 0) {
crc = crc16(CRC16_INIT, wrbuf, 3);
crc = crc16(crc, rbuf, 11);
} else {
/*
* DS2423 calculates crc from all bytes
* read after the previous crc bytes.
*/
crc = crc16(CRC16_INIT,
(rbuf + 11) +
((p - 1) * READ_BYTE_COUNT),
READ_BYTE_COUNT);
}
if (crc == CRC16_VALID) {
result = 0;
for (ii = 4; ii > 0; ii--) {
result <<= 8;
result |= rbuf[(p *
READ_BYTE_COUNT) + ii];
}
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=YES c=%d\n", result);
} else {
c -= snprintf(out_buf + PAGE_SIZE - c,
c, "crc=NO\n");
}
}
}
} else {
c -= snprintf(out_buf + PAGE_SIZE - c, c, "Connection error");
}
mutex_unlock(&dev->mutex);
return PAGE_SIZE - c;
}
static int w1_f1d_add_slave(struct w1_slave *sl)
{
return device_create_file(&sl->dev, &w1_counter_attr);
}
static void w1_f1d_remove_slave(struct w1_slave *sl)
{
device_remove_file(&sl->dev, &w1_counter_attr);
}
static struct w1_family_ops w1_f1d_fops = {
.add_slave = w1_f1d_add_slave,
.remove_slave = w1_f1d_remove_slave,
};
static struct w1_family w1_family_1d = {
.fid = W1_COUNTER_DS2423,
.fops = &w1_f1d_fops,
};
static int __init w1_f1d_init(void)
{
return w1_register_family(&w1_family_1d);
}
static void __exit w1_f1d_exit(void)
{
w1_unregister_family(&w1_family_1d);
}
module_init(w1_f1d_init);
module_exit(w1_f1d_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mika Laitio <lamikr@pilppa.org>");
MODULE_DESCRIPTION("w1 family 1d driver for DS2423, 4 counters and 4kb ram");