169 lines
		
	
	
	
		
			3.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			169 lines
		
	
	
	
		
			3.6 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
|   | /*
 | ||
|  |  * 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); |