forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Some preliminary work at making use of this driver led me to implement CRC-16 checks on read and write to deal with the occasional glitchiness of the 1-Wire bus. The revised driver (attached) returns an I/O error if the CRC check fails. When reading the chip's state, either you get a valid indication or you get an I/O error. When changing its state, either the change is successful or an I/O error is returned. Signed-off-by: Scott Alfter <[email protected]> Acked-by: Evgeniy Polyakov <[email protected]> Signed-off-by: Greg Kroah-Hartman <[email protected]>
- Loading branch information
Showing
5 changed files
with
202 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
w1_ds2406 kernel driver | ||
======================= | ||
|
||
Supported chips: | ||
* Maxim DS2406 (and other family 0x12) addressable switches | ||
|
||
Author: Scott Alfter <[email protected]> | ||
|
||
Description | ||
----------- | ||
|
||
The w1_ds2406 driver allows connected devices to be switched on and off. | ||
These chips also provide 128 bytes of OTP EPROM, but reading/writing it is | ||
not supported. In TSOC-6 form, the DS2406 provides two switch outputs and | ||
can be provided with power on a dedicated input. In TO-92 form, it provides | ||
one output and uses parasitic power only. | ||
|
||
The driver provides two sysfs files. state is readable; it gives the | ||
current state of each switch, with PIO A in bit 0 and PIO B in bit 1. The | ||
driver ORs this state with 0x30, so shell scripts get an ASCII 0/1/2/3 to | ||
work with. output is writable; bits 0 and 1 control PIO A and B, | ||
respectively. Bits 2-7 are ignored, so it's safe to write ASCII data. | ||
|
||
CRCs are checked on read and write. Failed checks cause an I/O error to be | ||
returned. On a failed write, the switch status is not changed. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,168 @@ | ||
/* | ||
* w1_ds2406.c - w1 family 12 (DS2406) driver | ||
* based on w1_ds2413.c by Mariusz Bialonczyk <[email protected]> | ||
* | ||
* Copyright (c) 2014 Scott Alfter <[email protected]> | ||
* | ||
* 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> | ||
#include <linux/crc16.h> | ||
|
||
#include "../w1.h" | ||
#include "../w1_int.h" | ||
#include "../w1_family.h" | ||
|
||
MODULE_LICENSE("GPL"); | ||
MODULE_AUTHOR("Scott Alfter <[email protected]>"); | ||
MODULE_DESCRIPTION("w1 family 12 driver for DS2406 2 Pin IO"); | ||
|
||
#define W1_F12_FUNC_READ_STATUS 0xAA | ||
#define W1_F12_FUNC_WRITE_STATUS 0x55 | ||
|
||
static ssize_t w1_f12_read_state( | ||
struct file *filp, struct kobject *kobj, | ||
struct bin_attribute *bin_attr, | ||
char *buf, loff_t off, size_t count) | ||
{ | ||
u8 w1_buf[6]={W1_F12_FUNC_READ_STATUS, 7, 0, 0, 0, 0}; | ||
struct w1_slave *sl = kobj_to_w1_slave(kobj); | ||
u16 crc=0; | ||
int i; | ||
ssize_t rtnval=1; | ||
|
||
if (off != 0) | ||
return 0; | ||
if (!buf) | ||
return -EINVAL; | ||
|
||
mutex_lock(&sl->master->bus_mutex); | ||
|
||
if (w1_reset_select_slave(sl)) { | ||
mutex_unlock(&sl->master->bus_mutex); | ||
return -EIO; | ||
} | ||
|
||
w1_write_block(sl->master, w1_buf, 3); | ||
w1_read_block(sl->master, w1_buf+3, 3); | ||
for (i=0; i<6; i++) | ||
crc=crc16_byte(crc, w1_buf[i]); | ||
if (crc==0xb001) /* good read? */ | ||
*buf=((w1_buf[3]>>5)&3)|0x30; | ||
else | ||
rtnval=-EIO; | ||
|
||
mutex_unlock(&sl->master->bus_mutex); | ||
|
||
return rtnval; | ||
} | ||
|
||
static ssize_t w1_f12_write_output( | ||
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); | ||
u8 w1_buf[6]={W1_F12_FUNC_WRITE_STATUS, 7, 0, 0, 0, 0}; | ||
u16 crc=0; | ||
int i; | ||
ssize_t rtnval=1; | ||
|
||
if (count != 1 || off != 0) | ||
return -EFAULT; | ||
|
||
mutex_lock(&sl->master->bus_mutex); | ||
|
||
if (w1_reset_select_slave(sl)) { | ||
mutex_unlock(&sl->master->bus_mutex); | ||
return -EIO; | ||
} | ||
|
||
w1_buf[3] = (((*buf)&3)<<5)|0x1F; | ||
w1_write_block(sl->master, w1_buf, 4); | ||
w1_read_block(sl->master, w1_buf+4, 2); | ||
for (i=0; i<6; i++) | ||
crc=crc16_byte(crc, w1_buf[i]); | ||
if (crc==0xb001) /* good read? */ | ||
w1_write_8(sl->master, 0xFF); | ||
else | ||
rtnval=-EIO; | ||
|
||
mutex_unlock(&sl->master->bus_mutex); | ||
return rtnval; | ||
} | ||
|
||
#define NB_SYSFS_BIN_FILES 2 | ||
static struct bin_attribute w1_f12_sysfs_bin_files[NB_SYSFS_BIN_FILES] = { | ||
{ | ||
.attr = { | ||
.name = "state", | ||
.mode = S_IRUGO, | ||
}, | ||
.size = 1, | ||
.read = w1_f12_read_state, | ||
}, | ||
{ | ||
.attr = { | ||
.name = "output", | ||
.mode = S_IRUGO | S_IWUSR | S_IWGRP, | ||
}, | ||
.size = 1, | ||
.write = w1_f12_write_output, | ||
} | ||
}; | ||
|
||
static int w1_f12_add_slave(struct w1_slave *sl) | ||
{ | ||
int err = 0; | ||
int i; | ||
|
||
for (i = 0; i < NB_SYSFS_BIN_FILES && !err; ++i) | ||
err = sysfs_create_bin_file( | ||
&sl->dev.kobj, | ||
&(w1_f12_sysfs_bin_files[i])); | ||
if (err) | ||
while (--i >= 0) | ||
sysfs_remove_bin_file(&sl->dev.kobj, | ||
&(w1_f12_sysfs_bin_files[i])); | ||
return err; | ||
} | ||
|
||
static void w1_f12_remove_slave(struct w1_slave *sl) | ||
{ | ||
int i; | ||
for (i = NB_SYSFS_BIN_FILES - 1; i >= 0; --i) | ||
sysfs_remove_bin_file(&sl->dev.kobj, | ||
&(w1_f12_sysfs_bin_files[i])); | ||
} | ||
|
||
static struct w1_family_ops w1_f12_fops = { | ||
.add_slave = w1_f12_add_slave, | ||
.remove_slave = w1_f12_remove_slave, | ||
}; | ||
|
||
static struct w1_family w1_family_12 = { | ||
.fid = W1_FAMILY_DS2406, | ||
.fops = &w1_f12_fops, | ||
}; | ||
|
||
static int __init w1_f12_init(void) | ||
{ | ||
return w1_register_family(&w1_family_12); | ||
} | ||
|
||
static void __exit w1_f12_exit(void) | ||
{ | ||
w1_unregister_family(&w1_family_12); | ||
} | ||
|
||
module_init(w1_f12_init); | ||
module_exit(w1_f12_exit); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters