|  | /* | 
|  | * w1_ds2406.c - w1 family 12 (DS2406) driver | 
|  | * based on w1_ds2413.c by Mariusz Bialonczyk <manio@skyboo.net> | 
|  | * | 
|  | * Copyright (c) 2014 Scott Alfter <scott@alfter.us> | 
|  | * | 
|  | * 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 <scott@alfter.us>"); | 
|  | 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); |