2005-04-16 15:20:36 -07:00
/*
2005-05-20 22:33:25 +04:00
* w1_therm . c
2005-04-16 15:20:36 -07:00
*
* Copyright ( c ) 2004 Evgeniy Polyakov < johnpol @ 2 ka . mipt . ru >
2005-05-20 22:33:25 +04:00
*
2005-04-16 15:20:36 -07:00
*
* 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 <asm/types.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/moduleparam.h>
2006-10-18 01:47:25 -04:00
# include <linux/sched.h>
2005-04-16 15:20:36 -07:00
# include <linux/device.h>
# include <linux/types.h>
# include <linux/delay.h>
2005-12-06 13:38:28 +03:00
# include "../w1.h"
# include "../w1_int.h"
# include "../w1_family.h"
2005-04-16 15:20:36 -07:00
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " Evgeniy Polyakov <johnpol@2ka.mipt.ru> " ) ;
MODULE_DESCRIPTION ( " Driver for 1-wire Dallas network protocol, temperature family. " ) ;
static u8 bad_roms [ ] [ 9 ] = {
2005-05-20 22:33:25 +04:00
{ 0xaa , 0x00 , 0x4b , 0x46 , 0xff , 0xff , 0x0c , 0x10 , 0x87 } ,
2005-04-16 15:20:36 -07:00
{ }
} ;
2007-06-09 13:57:22 +08:00
static ssize_t w1_therm_read_bin ( struct kobject * , struct bin_attribute * ,
char * , loff_t , size_t ) ;
2005-04-16 15:20:36 -07:00
2005-08-11 17:27:50 +04:00
static struct bin_attribute w1_therm_bin_attr = {
. attr = {
. name = " w1_slave " ,
. mode = S_IRUGO ,
} ,
. size = W1_SLAVE_DATA_SIZE ,
. read = w1_therm_read_bin ,
} ;
static int w1_therm_add_slave ( struct w1_slave * sl )
{
return sysfs_create_bin_file ( & sl - > dev . kobj , & w1_therm_bin_attr ) ;
}
static void w1_therm_remove_slave ( struct w1_slave * sl )
{
sysfs_remove_bin_file ( & sl - > dev . kobj , & w1_therm_bin_attr ) ;
}
2005-04-16 15:20:36 -07:00
static struct w1_family_ops w1_therm_fops = {
2005-08-11 17:27:50 +04:00
. add_slave = w1_therm_add_slave ,
. remove_slave = w1_therm_remove_slave ,
2005-04-16 15:20:36 -07:00
} ;
2005-04-17 22:58:14 +04:00
static struct w1_family w1_therm_family_DS18S20 = {
. fid = W1_THERM_DS18S20 ,
. fops = & w1_therm_fops ,
} ;
static struct w1_family w1_therm_family_DS18B20 = {
. fid = W1_THERM_DS18B20 ,
. fops = & w1_therm_fops ,
} ;
2005-08-11 17:27:50 +04:00
2005-04-17 22:58:14 +04:00
static struct w1_family w1_therm_family_DS1822 = {
. fid = W1_THERM_DS1822 ,
. fops = & w1_therm_fops ,
} ;
struct w1_therm_family_converter
{
u8 broken ;
u16 reserved ;
struct w1_family * f ;
int ( * convert ) ( u8 rom [ 9 ] ) ;
} ;
2008-02-06 01:38:09 -08:00
/* The return value is millidegrees Centigrade. */
2005-04-17 22:58:14 +04:00
static inline int w1_DS18B20_convert_temp ( u8 rom [ 9 ] ) ;
static inline int w1_DS18S20_convert_temp ( u8 rom [ 9 ] ) ;
static struct w1_therm_family_converter w1_therm_families [ ] = {
{
. f = & w1_therm_family_DS18S20 ,
. convert = w1_DS18S20_convert_temp
} ,
{
. f = & w1_therm_family_DS1822 ,
. convert = w1_DS18B20_convert_temp
} ,
{
. f = & w1_therm_family_DS18B20 ,
. convert = w1_DS18B20_convert_temp
} ,
} ;
static inline int w1_DS18B20_convert_temp ( u8 rom [ 9 ] )
{
2008-01-22 03:31:37 -08:00
s16 t = ( rom [ 1 ] < < 8 ) | rom [ 0 ] ;
2008-02-06 01:38:09 -08:00
t = t * 1000 / 16 ;
2005-04-17 22:58:14 +04:00
return t ;
}
static inline int w1_DS18S20_convert_temp ( u8 rom [ 9 ] )
2005-04-16 15:20:36 -07:00
{
int t , h ;
2005-04-17 22:58:14 +04:00
if ( ! rom [ 7 ] )
return 0 ;
2005-12-06 13:38:28 +03:00
2005-04-16 15:20:36 -07:00
if ( rom [ 1 ] = = 0 )
t = ( ( s32 ) rom [ 0 ] > > 1 ) * 1000 ;
else
t = 1000 * ( - 1 * ( s32 ) ( 0x100 - rom [ 0 ] ) > > 1 ) ;
2005-12-06 13:38:28 +03:00
2005-04-16 15:20:36 -07:00
t - = 250 ;
h = 1000 * ( ( s32 ) rom [ 7 ] - ( s32 ) rom [ 6 ] ) ;
h / = ( s32 ) rom [ 7 ] ;
t + = h ;
return t ;
}
2005-04-17 22:58:14 +04:00
static inline int w1_convert_temp ( u8 rom [ 9 ] , u8 fid )
{
int i ;
2007-02-12 00:52:05 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i )
2005-05-20 22:50:33 +04:00
if ( w1_therm_families [ i ] . f - > fid = = fid )
2005-04-17 22:58:14 +04:00
return w1_therm_families [ i ] . convert ( rom ) ;
return 0 ;
}
2005-04-16 15:20:36 -07:00
static int w1_therm_check_rom ( u8 rom [ 9 ] )
{
int i ;
for ( i = 0 ; i < sizeof ( bad_roms ) / 9 ; + + i )
if ( ! memcmp ( bad_roms [ i ] , rom , 9 ) )
return 1 ;
return 0 ;
}
2007-06-09 13:57:22 +08:00
static ssize_t w1_therm_read_bin ( struct kobject * kobj ,
struct bin_attribute * bin_attr ,
char * buf , loff_t off , size_t count )
2005-04-16 15:20:36 -07:00
{
2005-08-11 17:27:49 +04:00
struct w1_slave * sl = kobj_to_w1_slave ( kobj ) ;
2005-04-16 15:20:36 -07:00
struct w1_master * dev = sl - > master ;
u8 rom [ 9 ] , crc , verdict ;
int i , max_trying = 10 ;
2006-04-03 12:04:27 +04:00
mutex_lock ( & sl - > master - > mutex ) ;
2005-04-16 15:20:36 -07:00
if ( off > W1_SLAVE_DATA_SIZE ) {
count = 0 ;
goto out ;
}
if ( off + count > W1_SLAVE_DATA_SIZE ) {
count = 0 ;
goto out ;
}
memset ( buf , 0 , count ) ;
memset ( rom , 0 , sizeof ( rom ) ) ;
count = 0 ;
verdict = 0 ;
crc = 0 ;
while ( max_trying - - ) {
2005-08-11 17:27:49 +04:00
if ( ! w1_reset_select_slave ( sl ) ) {
2005-04-16 15:20:36 -07:00
int count = 0 ;
unsigned int tm = 750 ;
w1_write_8 ( dev , W1_CONVERT_TEMP ) ;
2007-06-27 14:10:04 -07:00
msleep ( tm ) ;
2005-04-16 15:20:36 -07:00
2005-08-11 17:27:49 +04:00
if ( ! w1_reset_select_slave ( sl ) ) {
2005-05-20 22:33:25 +04:00
2005-04-16 15:20:36 -07:00
w1_write_8 ( dev , W1_READ_SCRATCHPAD ) ;
if ( ( count = w1_read_block ( dev , rom , 9 ) ) ! = 9 ) {
dev_warn ( & dev - > dev , " w1_read_block() returned %d instead of 9. \n " , count ) ;
}
crc = w1_calc_crc8 ( rom , 8 ) ;
W1: w1_therm.c is flagging 0C etc as invalid
The extra rom[0] check is flagging valid temperatures as invalid when
there is already a CRC data transmission check.
w1_therm_read_bin()
if (rom[8] == crc && rom[0])
verdict = 1;
Requiring rom[0] to be non-zero will flag as invalid temperature
conversions when the low byte is zero, specifically the temperatures 0C,
16C, 32C, 48C, -16C, -32C, and -48C.
The CRC check is produced on the device for the previous 8 bytes and is
required to ensure the data integrity in transmission. I don't see why the
extra check for rom[0] being non-zero is in there. Evgeniy Polyakov didn't
know either. Just for a check I unplugged the sensor, executed a
temperature conversion, and read the results. The read was all ff's, which
also failed the CRC, so it doesn't need to protect against a disconnected
sensor.
I have more extensive patches in the work, but these two trivial ones will
do for today. I would like to hear from people who use the ds2490 USB to
one wire dongle. 1 if you would be willing to test the patches as I
currently only have the one sensor on a short parisite powered wire, 2 if
there is any cheap sources for the ds2490.
Signed-off-by: David Fries <david@fries.net>
Acked-by: Evgeniy Polyakov <johnpol@2ka.mipt.ru>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2008-01-22 03:31:39 -08:00
if ( rom [ 8 ] = = crc )
2005-04-16 15:20:36 -07:00
verdict = 1 ;
}
}
if ( ! w1_therm_check_rom ( rom ) )
break ;
}
for ( i = 0 ; i < 9 ; + + i )
count + = sprintf ( buf + count , " %02x " , rom [ i ] ) ;
count + = sprintf ( buf + count , " : crc=%02x %s \n " ,
crc , ( verdict ) ? " YES " : " NO " ) ;
if ( verdict )
memcpy ( sl - > rom , rom , sizeof ( sl - > rom ) ) ;
else
dev_warn ( & dev - > dev , " 18S20 doesn't respond to CONVERT_TEMP. \n " ) ;
for ( i = 0 ; i < 9 ; + + i )
count + = sprintf ( buf + count , " %02x " , sl - > rom [ i ] ) ;
2005-12-06 13:38:28 +03:00
2005-04-17 22:58:14 +04:00
count + = sprintf ( buf + count , " t=%d \n " , w1_convert_temp ( rom , sl - > family - > fid ) ) ;
2005-04-16 15:20:36 -07:00
out :
2006-04-03 12:04:27 +04:00
mutex_unlock ( & dev - > mutex ) ;
2005-04-16 15:20:36 -07:00
return count ;
}
static int __init w1_therm_init ( void )
{
2005-04-17 22:58:14 +04:00
int err , i ;
2007-02-12 00:52:05 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i ) {
2005-04-17 22:58:14 +04:00
err = w1_register_family ( w1_therm_families [ i ] . f ) ;
if ( err )
w1_therm_families [ i ] . broken = 1 ;
}
return 0 ;
2005-04-16 15:20:36 -07:00
}
static void __exit w1_therm_fini ( void )
{
2005-04-17 22:58:14 +04:00
int i ;
2007-02-12 00:52:05 -08:00
for ( i = 0 ; i < ARRAY_SIZE ( w1_therm_families ) ; + + i )
2005-04-17 22:58:14 +04:00
if ( ! w1_therm_families [ i ] . broken )
w1_unregister_family ( w1_therm_families [ i ] . f ) ;
2005-04-16 15:20:36 -07:00
}
module_init ( w1_therm_init ) ;
module_exit ( w1_therm_fini ) ;