2
0
mirror of https://github.com/lm-sensors/lm-sensors synced 2025-08-31 14:25:39 +00:00

Many things, most notable the lm78 module

* Some Makefile changes. doc/makefiles documents most of them.
* The lm78 module now compiles, but it will still crash.
* New module sensors, with general code usable by chip driver modules
* i2c-core: added function i2c_adapter_id(), which returns a (low)
  unique i2c-bus ID.
* lm78.h stuff moved into lm78.c or sensors.h


git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@19 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
Frodo Looijaard
1998-12-01 21:11:32 +00:00
parent dcf30092e1
commit 6ea68ba7a2
21 changed files with 2169 additions and 224 deletions

5
TODO
View File

@@ -1,11 +1,14 @@
Many, many things. Most notably:
* 2.1 kernel tests; at this moment, only 2.0 compiles have been tried.
* Make it SMP-safe: there are no spinlocks yet. They are needed at many
places, probably (everywhere where global vars are accessed).
* Write smbus_access_i2c in smbus.c
* Write piix4_* in piix4.c
* Complete lm78 driver
* Extend dev interface for SMBus
* Create entries in /proc/bus: a list of all registered busses, and for
each bus, a file with registered clients.
each bus, a file with registered clients (partially done)
* Write all other drivers
* How to make drivers detect thing *not*; for example, if a Winbond is
present, the LM75 driver should not try to access its simulated

54
doc/makefiles Normal file
View File

@@ -0,0 +1,54 @@
The Makefiles in this package are rather advanced. They are partially based
on the article "Recursive Make considered Harmful", written by Peter Miller.
See doc/useful_addresses.html for a link to this article.
There is one big Makefile in the root of this package. It includes many
other files; one for each directory in which source code is found. These
included files are called 'Module.mk'. There is a separate set of Makefiles
in the i2c/ directories; these are Simon Vogl's original Makefiles, and
not referred by the main package Makefile.
There are several interesting targets defined through this Makefile:
* all
Create everything in all directories.
* all-i2c, all-src, ...
Create everything in the designated directory.
* install
Install everything from all directories.
* install-i2c, install-src, ...
Install everything in the designated directory.
* clean
Remove anything which can be regenerated from all directories. A call
of 'make clean' (without any other targets) will ignore any .d files;
this is useful when they are out of date (and prevent the calling of
any other target).
* clean-i2c, clean-src, ...
Remove anything which can be regenerated from the designated directory.
* dep
Do nothing, but generate any missing .d files. The command
'make clean && make dep' canbe useful in this case ('make clean dep'
will not work, due to technical reasons!).
* version
Regenerate version.h, using the current date for the date-stamp, and
a user-supplied version number.
* package
Create a .tar.gz file containing everything except the CVS directories.
* src/lm78.o, i2c/i2c-core.o, ...
You can of course also specify one or more targets to make.
The best way to understand the Module.mk subfiles is to examine one of them,
for example src/Module.mk. They are not too difficult to understand.
There are several variables which can be set in the main Makefile. You can
also specify them on the command-line; this overrules any definitions
within the Makefile. For example: 'make all WARN=1' will enable all warnings.
Examine main Makefile to see which ones are available. The most important
ones for developers:
* WARN
Set to 1 to enable many compiler warnings.
* DEBUG
Set to 1 to enable any debugging code. Note that debugging code should
only output more information, and never make the code mis-behave.
There are lots of comments within the main Makefile. Please read them if
you want to know more.

View File

@@ -43,3 +43,5 @@ Here come the changes:
i2c_detach_client(client);
with:
client->driver->detach_client(client);
! i2c-core: function i2c_adapter_id added

View File

@@ -29,11 +29,14 @@ I2CTARGETS := $(MODULE_DIR)/i2c-core.o $(MODULE_DIR)/algo-bit.o \
# Include all dependency files
INCLUDEFILES += $(I2CTARGETS:.o=.d)
all :: $(I2CTARGETS)
all-i2c: $(I2CTARGETS)
all :: all-i2c
install ::
install-i2c:
$(MKDIR) $(MODDIR)
install -o root -g root -m 644 $(I2CTARGETS) $(MODDIR)
install :: install-i2c
clean ::
clean-i2c:
$(RM) $(I2CTARGETS) $(I2CTARGETS:.o=.d)
clean :: clean-i2c

View File

@@ -28,13 +28,15 @@ I2CDETECTSOURCES := $(MODULE_DIR)/detect.c
# Include all dependency files
INCLUDEFILES += $(I2CDETECTSOURCES:.c=.d)
all :: $(I2CDETECTTARGETS)
all-i2c-detect: $(I2CDETECTTARGETS)
all :: all-i2c-detect
# No install rule
clean ::
clean-i2c-detect:
$(RM) $(I2CDETECTSOURCES:.c=.d) $(I2CDETECTSOURCES:.c=.o) \
$(I2CDETECTTARGETS)
clean :: clean-i2c-detect
# The targets
$(MODULE_DIR)/detect: $(MODULE_DIR)/detect.o

View File

@@ -27,11 +27,14 @@ I2CDRIVERTARGETS := $(MODULE_DIR)/eeprom.o
# Include all dependency files
INCLUDEFILES += $(I2CDRIVERTARGETS:.o=.d)
all :: $(I2CDRIVERTARGETS)
all-i2c-drivers: $(I2CDRIVERTARGETS)
all :: all-i2c-drivers
install ::
install-i2c-drivers:
$(MKDIR) $(MODDIR)
install -o root -g root -m 644 $(I2CDRIVERTARGETS) $(MODDIR)
install :: install-i2c-drivers
clean ::
clean-i2c-drivers:
$(RM) $(I2CDRIVERTARGETS) $(I2CDRIVERTARGETS:.o=.d)
clean :: clean-i2c-drivers

View File

@@ -28,13 +28,15 @@ I2CEEPROMSOURCES := $(MODULE_DIR)/eeprom.c
# Include all dependency files
INCLUDEFILES += $(I2CEEPROMSOURCES:.c=.d)
all :: $(I2CEEPROMTARGETS)
all-i2c-eeprom: $(I2CEEPROMTARGETS)
all :: all-i2c-eeprom
# No install rule
clean ::
clean-i2c-eeprom:
$(RM) $(I2CEEPROMSOURCES:.c=.d) $(I2CEEPROMSOURCES:.c=.o) \
$(I2CEEPROMTARGETS)
clean :: clean-i2c-eeprom
# The targets
$(MODULE_DIR)/eeprom: $(MODULE_DIR)/eeprom.o

View File

@@ -17,7 +17,7 @@
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
/* ------------------------------------------------------------------------- */
#define RCSID "$Id: i2c-core.c,v 1.3 1998/11/19 03:07:18 frodo Exp $"
#define RCSID "$Id: i2c-core.c,v 1.4 1998/11/27 22:02:09 frodo Exp $"
/* ------------------------------------------------------------------------- */
#include <linux/module.h>
@@ -435,6 +435,16 @@ int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr)
return (i <= hi_addr) ? i : -1;
}
int i2c_adapter_id(struct i2c_adapter *adap)
{
int i;
for (i = 0; i < I2C_ADAP_MAX; i++)
if (adap == adapters[i])
return i;
return -1;
}
#ifdef MODULE
MODULE_AUTHOR("Simon G. Vogl <simon@tk.uni-linz.ac.at>");
MODULE_DESCRIPTION("I2C-Bus main module");

View File

@@ -254,6 +254,10 @@ extern int i2c_probe(struct i2c_client *client, int low_addr, int hi_addr);
extern int i2c_control(struct i2c_client *,unsigned int, unsigned long);
/* This call returns a unique low identifier for each registered adapter,
or -1 if the adapter was not regisitered. */
extern int i2c_adapter_id(struct i2c_adapter *adap);
#endif /* __KERNEL__ */

View File

@@ -23,16 +23,23 @@ MODULE_DIR := src
# Regrettably, even 'simply expanded variables' will not put their currently
# defined value verbatim into the command-list of rules...
SRCTARGETS := $(MODULE_DIR)/smbus.o $(MODULE_DIR)/piix4.o $(MODULE_DIR)/isa.o \
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/i2c-proc.o
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/sensors.o \
$(MODULE_DIR)/i2c-proc.o
HEADERFILES := $(MODULE_DIR)/sensors.h
# Include all dependency files
INCLUDEFILES += $(SRCTARGETS:.o=.d)
all :: $(SRCTARGETS)
all-src: $(SRCTARGETS)
all :: all-src
install ::
install-src:
$(MKDIR) $(MODDIR)
install -o root -g root -m 644 $(SRCTARGETS) $(MODDIR)
install -o root -g root -m 644 $(HEADERFILES) $(INCLUDEDIR)
install :: install-src
clean ::
clean-src:
$(RM) $(SRCTARGETS) $(SRCTARGETS:.o=.d)
clean :: clean-src

View File

@@ -1,3 +1,4 @@
/*
lm78.c - A Linux module for reading sensor data.
Copyright (c) 1998 Frodo Looijaard <frodol@dds.nl>
@@ -21,15 +22,112 @@
#include <linux/malloc.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <linux/types.h>
#include "lm78.h"
#include "smbus.h"
#include "version.h"
#include "isa.h"
#include "sensors.h"
#include "i2c.h"
#include "compat.h"
/* Many LM78 constants needed below */
/* Length of ISA address segment */
#define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define LM78_ADDR_REG_OFFSET 5
#define LM78_DATA_REG_OFFSET 6
/* The LM78 registers */
#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define LM78_REG_IN(nr) (0x20 + (nr))
#define LM78_REG_FAN_MIN(nr) (0x3a + (nr))
#define LM78_REG_FAN(nr) (0x27 + (nr))
#define LM78_REG_TEMP 0x27
#define LM78_REG_TEMP_OVER 0x39
#define LM78_REG_TEMP_HYST 0x3a
#define LM78_REG_ALARM1 0x41
#define LM78_REG_ALARM2 0x42
#define LM78_REG_VID_FANDIV 0x47
#define LM78_REG_CONFIG 0x40
/* Conversions */
static int lm78_in_conv[7] = {10000, 10000, 10000, 16892, 38000,
-34768, -15050 };
#define IN_TO_REG(val,nr) ((((val) * 100000 / lm78_in_conv[nr]) + 8) / 16)
#define IN_FROM_REG(val,nr) (((val) * 16 * lm78_in_conv[nr]) / 100000)
#define FAN_TO_REG(val) (((val)==0)?255:((1350000+(val))/((val)*2)))
#define FAN_FROM_REG(val) (((val)==0)?-1:\
((val)==255)?0:(1350000 + (val))/((val)*2))
#define TEMP_TO_REG(val) ((val)<0?(val)&0xff:(val))
#define TEMP_FROM_REG(val) ((val)>0x80?(val)-0x100:(val));
#define VID_FROM_REG(val) ((val) == 0x0f?0:350-(val)*10)
#define ALARMS_FROM_REG(val) (val)
#define DIV_FROM_REG(val) (1 >> (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?1:2)
/* Initial limits */
#define LM78_INIT_IN_0 280
#define LM78_INIT_IN_1 280
#define LM78_INIT_IN_2 330
#define LM78_INIT_IN_3 500
#define LM78_INIT_IN_4 1200
#define LM78_INIT_IN_5 -1200
#define LM78_INIT_IN_6 -500
#define LM78_INIT_IN_PERCENTAGE 100
#define LM78_INIT_IN_MIN_0 \
(LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_0 \
(LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_1 \
(LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_1 \
(LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_2 \
(LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_2 \
(LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_3 \
(LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_3 \
(LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_4 \
(LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_4 \
(LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_5 \
(LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_5 \
(LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_6 \
(LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_6 \
(LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_FAN_MIN_1 3000
#define LM78_INIT_FAN_MIN_2 3000
#define LM78_INIT_FAN_MIN_3 3000
#define LM78_INIT_TEMP_OVER 60
#define LM78_INIT_TEMP_HYST 50
#ifdef MODULE
extern int init_module(void);
@@ -51,6 +149,32 @@ extern int cleanup_module(void);
bad. Quite a lot of bookkeeping is done. A real driver can often cut
some corners. */
/* For each registered LM78, we need to keep some data in memory. That
data is pointed to by lm78_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm78 client is
allocated. */
struct lm78_data {
struct semaphore lock;
int sysctl_id;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp; /* Register value */
u8 temp_over; /* Register value */
u8 temp_hyst; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u16 alarms; /* Register encoding, combined */
};
static int lm78_init(void);
static int lm78_cleanup(void);
@@ -62,21 +186,44 @@ static int lm78_detach_isa(struct isa_client *client);
static int lm78_detach_smbus(struct i2c_client *client);
static int lm78_new_client(struct i2c_adapter *adapter,
struct i2c_client *new_client);
static void lm78_init_client(struct i2c_client *client);
static void lm78_remove_client(struct i2c_client *client);
static int lm78_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void lm78_inc_use (struct i2c_client *client);
static void lm78_dec_use (struct i2c_client *client);
static int lm78_read_value(struct i2c_client *client, u8 register);
static int lm78_write_value(struct i2c_client *client, u8 register, u8 value);
static void lm78_update_client(struct i2c_client *client);
static void lm78_init_client(struct i2c_client *client);
static int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval,
size_t *oldlenp, void *newval, size_t newlen,
void **context);
static int lm78_proc (ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp);
static void write_in(struct i2c_client *client, int nr, int nrels,
long *results);
static void read_in(struct i2c_client *client, int nr, long *results);
static void write_fan(struct i2c_client *client, int nr, int nrels,
long *results);
static void read_fan(struct i2c_client *client, int nr, long *results);
static void write_temp(struct i2c_client *client, int nrels, long *results);
static void read_temp(struct i2c_client *client, long *results);
static void read_vid(struct i2c_client *client, long *results);
static void read_alarms(struct i2c_client *client, long *results);
static void write_fan_div(struct i2c_client *client, int nrels, long *results);
static void read_fan_div(struct i2c_client *client, long *results);
/* I choose here for semi-static LM78 allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructures take now */
take more memory than the datastructure takes now. */
#define MAX_LM78_NR 4
static struct i2c_client *lm78_list[MAX_LM78_NR];
static struct semaphore lm78_semaphores[MAX_LM78_NR];
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
@@ -94,8 +241,34 @@ static struct i2c_driver lm78_driver = {
/* Used by lm78_init/cleanup */
static int lm78_initialized = 0;
/* The /proc/sys entries */
/* These files are created for each detected LM78. This is just a template;
though at first sight, you might think we could use a statically
allocated list, we need some way to get back to the parent - which
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table lm78_dir_table_template[] = {
{ LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN2, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN3, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }
};
/* This function is called when:
* lm78_driver is inserted, for each available adapter
* lm78_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and lm78_driver is still present) */
int lm78_attach_adapter(struct i2c_adapter *adapter)
{
@@ -105,7 +278,10 @@ int lm78_attach_adapter(struct i2c_adapter *adapter)
return lm78_detect_smbus(adapter);
}
/* This function is called whenever a client should be removed */
/* This function is called whenever a client should be removed:
* lm78_driver is removed (when this module is unloaded)
* when an adapter is removed which has a lm78 client (and lm78_driver
is still present). */
int lm78_detach_client(struct i2c_client *client)
{
if (i2c_is_isa_client(client))
@@ -138,42 +314,74 @@ int lm78_detect_isa(struct isa_adapter *adapter)
request_region(address, LM78_EXTENT, "lm78");
if (! (new_client = kmalloc(sizeof(struct isa_client), GFP_KERNEL)))
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct isa_client) +
sizeof(struct lm78_data),
GFP_KERNEL)))
{
release_region(address,LM78_EXTENT);
err=-ENOMEM;
continue;
}
goto ERROR1;
}
/* Fill the new client structure with data */
new_client->data = (struct lm78_data *) (new_client + 1);
new_client->addr = 0;
new_client->isa_addr = address;
if ((err = lm78_new_client((struct i2c_adapter *) adapter,
(struct i2c_client *) new_client))) {
release_region(address, LM78_EXTENT);
kfree(new_client);
continue;
}
if ((err = isa_attach_client(new_client))) {
release_region(address, LM78_EXTENT);
lm78_remove_client((struct i2c_client *) new_client);
kfree(new_client);
continue;
}
(struct i2c_client *) new_client)))
goto ERROR2;
/* Tell i2c-core a new client has arrived */
if ((err = isa_attach_client(new_client)))
goto ERROR3;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry((struct i2c_client *) new_client,"lm78",
lm78_dir_table_template) < 0))
goto ERROR4;
((struct lm78_data *) (new_client->data)) -> sysctl_id = err;
/* Initialize the LM78 chip */
lm78_init_client((struct i2c_client *) new_client);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR4:
isa_detach_client(new_client);
ERROR3:
lm78_remove_client((struct i2c_client *) new_client);
ERROR2:
kfree(new_client);
ERROR1:
release_region(address, LM78_EXTENT);
}
return err;
}
/* Deregister and remove a LM78 client */
int lm78_detach_isa(struct isa_client *client)
{
int err;
int err,i;
for (i = 0; i < MAX_LM78_NR; i++)
if ((client == (struct isa_client *) (lm78_list[i])))
break;
if (i == MAX_LM78_NR) {
printk("lm78.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
if ((err = isa_detach_client(client))) {
printk("lm78.o: Client deregistration failed, client not detached.\n");
return err;
}
release_region(client->isa_addr,LM78_EXTENT);
lm78_remove_client((struct i2c_client *) client);
kfree(client);
release_region(client->isa_addr,LM78_EXTENT);
return 0;
}
@@ -194,25 +402,59 @@ int lm78_detect_smbus(struct i2c_adapter *adapter)
/* Real detection code goes here */
new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct lm78_data),
GFP_KERNEL))) {
err = -ENOMEM;
continue;
}
/* Fill the new client structure with data */
new_client->data = (struct lm78_data *) (new_client + 1);
new_client->addr = address;
if ((err = lm78_new_client(adapter,new_client))) {
kfree(new_client);
continue;
}
if ((err = i2c_attach_client(new_client))) {
lm78_remove_client(new_client);
kfree(new_client);
continue;
}
if ((err = lm78_new_client(adapter,new_client)))
goto ERROR2;
/* Tell i2c-core a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR3;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry(new_client,"lm78",
lm78_dir_table_template) < 0))
goto ERROR4;
((struct lm78_data *) (new_client->data))->sysctl_id = err;
/* Initialize the LM78 chip */
lm78_init_client(new_client);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR4:
i2c_detach_client(new_client);
ERROR3:
lm78_remove_client((struct i2c_client *) new_client);
ERROR2:
kfree(new_client);
}
return err;
}
int lm78_detach_smbus(struct i2c_client *client)
{
int err;
int err,i;
for (i = 0; i < MAX_LM78_NR; i++)
if (client == lm78_list[i])
break;
if ((i == MAX_LM78_NR)) {
printk("lm78.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("lm78.o: Client deregistration failed, client not detached.\n");
return err;
@@ -228,6 +470,7 @@ int lm78_new_client(struct i2c_adapter *adapter,
struct i2c_client *new_client)
{
int i;
struct lm78_data *data;
/* First, seek out an empty slot */
for(i = 0; i < MAX_LM78_NR; i++)
@@ -240,28 +483,26 @@ int lm78_new_client(struct i2c_adapter *adapter,
}
lm78_list[i] = new_client;
lm78_semaphores[i] = MUTEX;
new_client->data = &lm78_semaphores[i];
strcpy(new_client->name,"LM78 chip");
new_client->id = i;
new_client->adapter = adapter;
new_client->driver = &lm78_driver;
data = new_client->data;
data->valid = 0;
data->lock = MUTEX;
data->update_lock = MUTEX;
return 0;
}
/* Inverse of lm78_new_client */
void lm78_remove_client(struct i2c_client *client)
{
int i;
for (i = 0; i < MAX_LM78_NR; i++)
if (client == lm78_list[i])
if (client == lm78_list[i])
lm78_list[i] = NULL;
}
/* Called when we have found a new LM78. It should set limits, etc. */
void lm78_init_client(struct i2c_client *client)
{
}
/* No commands defined yet */
int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
@@ -316,37 +557,346 @@ int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
return smbus_write_byte_data(client->adapter, client->addr, reg,value);
}
/* ANYTHING BELOW IS JUST AN EXAMPLE. IGNORE IF YOU WANT TO BASE A DRIVER
ON THE CODE IN THIS FILE */
/* Stupid entry in /proc */
static int proc_function(char *buf, char **start, off_t offset, int len,
int unused)
/* Called when we have found a new LM78. It should set limits, etc. */
void lm78_init_client(struct i2c_client *client)
{
int i;
len = 0;
for (i = 0; i < MAX_LM78_NR; i++)
if(lm78_list[i]) {
if(i2c_is_isa_client(lm78_list[i]))
len += sprintf(buf+len,"(isa) %d: address=%x\n",i,
((struct isa_client *) (lm78_list[i]))->isa_addr);
else
len += sprintf(buf+len,"(i2c) %d: address=%x\n",i,
lm78_list[i]->addr);
}
return len;
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
lm78_write_value(client,LM78_REG_CONFIG,0x80);
lm78_write_value(client,LM78_REG_IN_MIN(0),IN_TO_REG(LM78_INIT_IN_MIN_0,0));
lm78_write_value(client,LM78_REG_IN_MAX(0),IN_TO_REG(LM78_INIT_IN_MAX_0,0));
lm78_write_value(client,LM78_REG_IN_MIN(1),IN_TO_REG(LM78_INIT_IN_MIN_1,1));
lm78_write_value(client,LM78_REG_IN_MAX(1),IN_TO_REG(LM78_INIT_IN_MAX_1,1));
lm78_write_value(client,LM78_REG_IN_MIN(2),IN_TO_REG(LM78_INIT_IN_MIN_2,2));
lm78_write_value(client,LM78_REG_IN_MAX(2),IN_TO_REG(LM78_INIT_IN_MAX_2,2));
lm78_write_value(client,LM78_REG_IN_MIN(3),IN_TO_REG(LM78_INIT_IN_MIN_3,3));
lm78_write_value(client,LM78_REG_IN_MAX(3),IN_TO_REG(LM78_INIT_IN_MAX_3,3));
lm78_write_value(client,LM78_REG_IN_MIN(4),IN_TO_REG(LM78_INIT_IN_MIN_4,4));
lm78_write_value(client,LM78_REG_IN_MAX(4),IN_TO_REG(LM78_INIT_IN_MAX_4,4));
lm78_write_value(client,LM78_REG_IN_MIN(5),IN_TO_REG(LM78_INIT_IN_MIN_5,5));
lm78_write_value(client,LM78_REG_IN_MAX(5),IN_TO_REG(LM78_INIT_IN_MAX_5,5));
lm78_write_value(client,LM78_REG_IN_MIN(6),IN_TO_REG(LM78_INIT_IN_MIN_6,6));
lm78_write_value(client,LM78_REG_IN_MAX(6),IN_TO_REG(LM78_INIT_IN_MAX_6,6));
lm78_write_value(client,LM78_REG_FAN_MIN(1),FAN_TO_REG(LM78_INIT_FAN_MIN_1));
lm78_write_value(client,LM78_REG_FAN_MIN(2),FAN_TO_REG(LM78_INIT_FAN_MIN_2));
lm78_write_value(client,LM78_REG_FAN_MIN(3),FAN_TO_REG(LM78_INIT_FAN_MIN_3));
lm78_write_value(client,LM78_REG_TEMP_OVER,TEMP_TO_REG(LM78_INIT_TEMP_OVER));
lm78_write_value(client,LM78_REG_TEMP_HYST,TEMP_TO_REG(LM78_INIT_TEMP_HYST));
/* Start monitoring */
lm78_write_value(client,LM78_REG_CONFIG,
(lm78_read_value(client,LM78_REG_CONFIG) & 0xf7) | 0x01);
}
/* OK, this is a test entry. Just ignore, it is not important. */
static struct proc_dir_entry proc_entry =
{
0,12,"sensors-test",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL,
&proc_function
};
void lm78_update_client(struct i2c_client *client)
{
struct lm78_data *data = client->data;
int i;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
(jiffies < data->last_updated) || ! data->valid) {
for (i = 0; i <= 6; i++) {
data->in[i] = lm78_read_value(client,LM78_REG_IN(i));
data->in_min[i] = lm78_read_value(client,LM78_REG_IN_MIN(i));
data->in_max[i] = lm78_read_value(client,LM78_REG_IN_MAX(i));
}
for (i = 1; i <= 3; i++) {
data->fan[i-1] = lm78_read_value(client,LM78_REG_FAN(i));
data->fan_min[i-1] = lm78_read_value(client,LM78_REG_FAN_MIN(i));
}
data->temp = lm78_read_value(client,LM78_REG_TEMP);
data->temp_over = lm78_read_value(client,LM78_REG_TEMP_OVER);
data->temp_hyst = lm78_read_value(client,LM78_REG_TEMP_HYST);
i = lm78_read_value(client,LM78_REG_VID_FANDIV);
data->vid = i & 0x0f;
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = lm78_read_value(client,LM78_REG_ALARM1) +
(lm78_read_value(client,LM78_REG_ALARM2) >> 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
/* This function is called when /proc/sys/dev/lm78-???/... is accessed */
int lm78_proc (ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
int nrels,mag;
long results[7];
struct i2c_client *client = ctl -> extra1;
/* If buffer is size 0, or we try to read when not at the start, we
return nothing. Note that I think writing when not at the start
does not work either, but anyway, this is straight from the kernel
sources. */
if (!*lenp || (filp->f_pos && !write)) {
*lenp = 0;
return 0;
}
/* How many numbers are found within these files, and how to scale them? */
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2:
case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5:
case LM78_SYSCTL_IN6:
nrels=3;
mag=2;
break;
case LM78_SYSCTL_FAN_DIV: case LM78_SYSCTL_TEMP:
nrels=3;
mag=0;
break;
case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3:
nrels=2;
mag=0;
break;
case LM78_SYSCTL_VID:
nrels=1;
mag=2;
break;
case LM78_SYSCTL_ALARMS:
nrels=1;
mag=0;
break;
default: /* Should never be called */
return -EINVAL;
}
/* OK, try writing stuff. */
if (write) {
sensors_parse_reals(&nrels,buffer,*lenp,results,mag);
if (nrels == 0)
return 0;
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break;
case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break;
case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break;
case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break;
case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break;
case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break;
case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break;
case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break;
case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break;
case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break;
case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break;
case LM78_SYSCTL_TEMP: write_temp(client,nrels,results);break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break;
default: /* Should never be called */ *lenp=0; return -EINVAL; break;
}
filp->f_pos += *lenp;
return 0;
} else { /* read */
/* Update all values in LM_Sensor_Data */
lm78_update_client((struct i2c_client *) (ctl->extra1));
/* Read the values to print into results */
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: read_in(client,0,results);break;
case LM78_SYSCTL_IN1: read_in(client,1,results);break;
case LM78_SYSCTL_IN2: read_in(client,2,results);break;
case LM78_SYSCTL_IN3: read_in(client,3,results);break;
case LM78_SYSCTL_IN4: read_in(client,4,results);break;
case LM78_SYSCTL_IN5: read_in(client,5,results);break;
case LM78_SYSCTL_IN6: read_in(client,6,results);break;
case LM78_SYSCTL_FAN1: read_fan(client,1,results);break;
case LM78_SYSCTL_FAN2: read_fan(client,2,results);break;
case LM78_SYSCTL_FAN3: read_fan(client,3,results);break;
case LM78_SYSCTL_TEMP: read_temp(client,results);break;
case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break;
case LM78_SYSCTL_VID: read_vid(client,results);break;
case LM78_SYSCTL_ALARMS: read_alarms(client,results);break;
default: /* Should never be called */ return -EINVAL;
}
/* OK, print it now */
sensors_write_reals(nrels,buffer,lenp,results,mag);
filp->f_pos += *lenp;
return 0;
}
}
/* This function is called when a sysctl on a lm78 file is done */
int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval,
size_t *oldlenp, void *newval, size_t newlen,
void **context)
{
long results[7];
int nrels,oldlen;
struct i2c_client *client = table -> extra1;
/* How many numbers are found within these files, and how to scale them? */
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2:
case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5:
case LM78_SYSCTL_IN6: case LM78_SYSCTL_TEMP: case LM78_SYSCTL_FAN_DIV:
nrels=3;
break;
case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3:
nrels=2;
break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS:
nrels=1;
break;
default: /* Should never be called */
return -EINVAL;
}
/* Check if we need to output the old values */
if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
/* Update all values in LM_Sensor_Data */
lm78_update_client((struct i2c_client *) (table->extra1));
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: read_in(client,0,results);break;
case LM78_SYSCTL_IN1: read_in(client,1,results);break;
case LM78_SYSCTL_IN2: read_in(client,2,results);break;
case LM78_SYSCTL_IN3: read_in(client,3,results);break;
case LM78_SYSCTL_IN4: read_in(client,4,results);break;
case LM78_SYSCTL_IN5: read_in(client,5,results);break;
case LM78_SYSCTL_IN6: read_in(client,6,results);break;
case LM78_SYSCTL_FAN1: read_fan(client,1,results);break;
case LM78_SYSCTL_FAN2: read_fan(client,2,results);break;
case LM78_SYSCTL_FAN3: read_fan(client,3,results);break;
case LM78_SYSCTL_TEMP: read_temp(client,results);break;
case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break;
case LM78_SYSCTL_VID: read_vid(client,results);break;
case LM78_SYSCTL_ALARMS: read_alarms(client,results);break;
default: /* Should never be called */ return -EINVAL;
}
/* Note the rounding factor! */
if (nrels * sizeof(long) < oldlen)
oldlen = nrels * sizeof(long);
oldlen = (oldlen / sizeof(long)) * sizeof(long);
copy_to_user(oldval,results,oldlen);
put_user(oldlen,oldlenp);
}
/* Check to see whether we need to read the new values */
if (newval && newlen) {
if (nrels * sizeof(long) < newlen)
newlen = nrels * sizeof(long);
nrels = newlen / sizeof(long);
newlen = (newlen / sizeof(long)) * sizeof(long);
copy_from_user(results,newval,newlen);
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break;
case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break;
case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break;
case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break;
case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break;
case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break;
case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break;
case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break;
case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break;
case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break;
case LM78_SYSCTL_TEMP: write_temp(client,nrels,results); break;
case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break;
default: /* Should never be called */ return -EINVAL; break;
}
}
return 1; /* We have done all the work */
}
void write_in(struct i2c_client *client, int nr, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->in_min[nr] = IN_TO_REG(results[0],nr);
lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]);
}
if (nrels >= 2) {
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
}
void read_in(struct i2c_client *client, int nr, long *results)
{
struct lm78_data *data = client->data;
results[0] = IN_FROM_REG(data->in_min[nr],nr);
results[1] = IN_FROM_REG(data->in_min[nr],nr);
results[2] = IN_FROM_REG(data->in_min[nr],nr);
}
void write_fan(struct i2c_client *client, int nr, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->fan_min[nr-1] = FAN_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]);
}
}
void read_fan(struct i2c_client *client, int nr, long *results)
{
struct lm78_data *data = client->data;
results[0] = FAN_FROM_REG(data->fan_min[nr-1]);
results[1] = FAN_FROM_REG(data->fan[nr-1]);
}
void write_temp(struct i2c_client *client, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->temp_over = TEMP_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over);
}
if (nrels >= 2) {
data->temp_hyst = TEMP_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst);
}
}
void read_temp(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = TEMP_FROM_REG(data->temp_over);
results[1] = TEMP_FROM_REG(data->temp_hyst);
results[2] = TEMP_FROM_REG(data->temp);
}
void read_vid(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = VID_FROM_REG(data->vid);
}
void read_alarms(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = ALARMS_FROM_REG(data->alarms);
}
void read_fan_div(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
}
void write_fan_div(struct i2c_client *client, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 2)
data->fan_div[1] = DIV_TO_REG(results[1]);
if (nrels >= 1) {
data->fan_div[0] = DIV_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_VID_FANDIV,
(data->fan_div[0] >> 4) | (data->fan_div[1] >> 6));
}
}
int lm78_init(void)
{
int res;
@@ -354,16 +904,6 @@ int lm78_init(void)
printk("lm78.o version %s (%s)\n",LM_VERSION,LM_DATE);
lm78_initialized = 0;
/* OK, we register some stupid proc file here. This is *just* *temporary*,
for test purposes. Ignore if you want. Only works for kernels 2.0.x. */
if ((res = proc_register_dynamic(&proc_root,&proc_entry))) {
printk("lm78.o: Couldn't create /proc/sensors-test, "
"module not inserted.\n");
lm78_cleanup();
return res;
}
lm78_initialized ++;
if ((res =i2c_add_driver(&lm78_driver))) {
printk("lm78.o: Driver registration failed, module not inserted.\n");
lm78_cleanup();
@@ -377,7 +917,7 @@ int lm78_cleanup(void)
{
int res;
if (lm78_initialized >= 2) {
if (lm78_initialized >= 1) {
if ((res = i2c_del_driver(&lm78_driver))) {
printk("lm78.o: Driver deregistration failed, module not removed.\n");
return res;
@@ -385,14 +925,6 @@ int lm78_cleanup(void)
} else
lm78_initialized --;
if (lm78_initialized >= 1) {
if((res = proc_unregister(&proc_root,proc_entry.low_ino))) {
printk("lm78.o: Deregistration of /proc/sensors_test failed, "
"module not removed.\n");
return res;
}
} else
lm78_initialized --;
return 0;
}

View File

@@ -37,4 +37,20 @@
#endif
#endif /* def MODULE */
/* copy_from/to_usr is called memcpy_from/to_fs in 2.0 kernels; perhaps in
some early 2.1 kernels too? */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define copy_from_user memcpy_fromfs
#define copy_to_user memcpy_tofs
#endif
/* get_user was redefined in 2.1 kernels to use two arguments, and returns
an error code */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define get_user_data(to,from) ((to) = get_user(from),0)
#else
#define get_user_data(to,from) get_user(to,from)
#endif
#endif /* SENSORS_COMPAT_H */

View File

@@ -37,4 +37,20 @@
#endif
#endif /* def MODULE */
/* copy_from/to_usr is called memcpy_from/to_fs in 2.0 kernels; perhaps in
some early 2.1 kernels too? */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define copy_from_user memcpy_fromfs
#define copy_to_user memcpy_tofs
#endif
/* get_user was redefined in 2.1 kernels to use two arguments, and returns
an error code */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define get_user_data(to,from) ((to) = get_user(from),0)
#else
#define get_user_data(to,from) get_user(to,from)
#endif
#endif /* SENSORS_COMPAT_H */

View File

@@ -20,8 +20,45 @@
#ifndef SENSORS_SENSORS_H
#define SENSORS_SENSORS_H
#include <linux/sysctl.h>
#ifdef __KERNEL__
extern int sensors_register_entry(struct i2c_client *client,
const char *prefix, ctl_table *ctl_template);
extern void sensors_deregister_entry(int id);
extern void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
long *results, int magnitude);
extern void sensors_write_reals(int nrels,void *buffer,int *bufsize,
long *results, int magnitude);
#endif /* def __KERNEL__ */
/* Driver IDs */
#define I2C_DRIVERID_I2CPROC 1001
#define I2C_DRIVERID_LM78 1002
/* Sysctl IDs */
#ifdef DEV_HWMON
#define DEV_SENSORS DEV_HWMON
#else /* ndef DEV_HWMOM */
#define DEV_SENSORS 2 /* The id of the lm_sensors directory within the
dev table */
#endif /* def DEV_HWMON */
#define LM78_SYSCTL_IN0 1000
#define LM78_SYSCTL_IN1 1001
#define LM78_SYSCTL_IN2 1002
#define LM78_SYSCTL_IN3 1003
#define LM78_SYSCTL_IN4 1004
#define LM78_SYSCTL_IN5 1005
#define LM78_SYSCTL_IN6 1006
#define LM78_SYSCTL_FAN1 1101
#define LM78_SYSCTL_FAN2 1102
#define LM78_SYSCTL_FAN3 1103
#define LM78_SYSCTL_TEMP 1200
#define LM78_SYSCTL_VID 1300
#define LM78_SYSCTL_FAN_DIV 2000
#define LM78_SYSCTL_ALARMS 2001
#endif /* def SENSORS_SENSORS_H */

346
kernel/sensors.c Normal file
View File

@@ -0,0 +1,346 @@
/*
sensors.c - A Linux module for reading sensor data.
Copyright (c) 1998 Frodo Looijaard <frodol@dds.nl>
This program is free software; you can redistribute it and/or modify
it under the terms 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include "version.h"
#include "compat.h"
#include "i2c.h"
#include "isa.h"
#include "sensors.h"
#ifdef MODULE
extern int init_module(void);
extern int cleanup_module(void);
#endif /* MODULE */
int sensors_register_entry(struct i2c_client *client ,const char *prefix,
ctl_table *ctl_template);
static int sensors_create_name(char **name, const char *prefix,
struct i2c_adapter * adapter, int addr);
static int sensors_init(void);
static int sensors_cleanup(void);
#define SENSORS_ENTRY_MAX 20
static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX];
static ctl_table sysctl_table[] = {
{ CTL_DEV, "dev", NULL, 0, 0555 },
{ 0 },
{ DEV_SENSORS, "sensors", NULL, 0, 0555 },
{ 0 },
{ 0, NULL, NULL, 0, 0555 },
{ 0 }
};
/* This returns a nice name for a new directory; for example lm78-isa-0310
(for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
a LM75 chip on the third i2c bus at address 0x4e).
name is allocated first. */
int sensors_create_name(char **name, const char *prefix,
struct i2c_adapter * adapter, int addr)
{
char name_buffer[50];
int id;
if (i2c_is_isa_adapter(adapter))
sprintf(name_buffer,"%s-isa-%d",prefix,addr);
else {
if ((id = i2c_adapter_id(adapter)) < 0);
return -ENOENT;
sprintf(name_buffer,"%s-i2c-%d-%d",prefix,id,addr);
}
*name = kmalloc(strlen(name_buffer)+1,GFP_KERNEL);
strcpy(*name,name_buffer);
return 0;
}
/* This rather complex function must be called when you want to add an entry
to /proc/sys/dev/sensors/chips (not yet implemented). It also creates
a new directory within /proc/sys/dev/sensors/.
ctl_template should be a template of the newly created directory. It is
copied in memory. The extra1 field of each file is set to point to client.
If any driver wants subdirectories within the newly created directory,
this function must be updated! */
int sensors_register_entry(struct i2c_client *client ,const char *prefix,
ctl_table *ctl_template)
{
int i,res,len,id;
ctl_table *new_table;
char *name;
struct ctl_table_header *new_header;
if ((res = sensors_create_name(&name,prefix,client->adapter,
i2c_is_isa_client(client)?
((struct isa_client *) client)->isa_addr:
client->addr)))
return res;
for (id = 0; id < SENSORS_ENTRY_MAX; i++)
if (! sensors_entries[id]) {
break;
}
if (id == SENSORS_ENTRY_MAX) {
kfree(name);
return -ENOMEM;
}
id += 256;
len = 0;
while (ctl_template[len].procname)
len++;
len += 7;
if (! (new_table = kmalloc(sizeof(ctl_table) * len,GFP_KERNEL))) {
kfree(name);
return -ENOMEM;
}
memcpy(new_table,sysctl_table,6 * sizeof(ctl_table));
new_table[0].child = &new_table[2];
new_table[2].child = &new_table[4];
new_table[4].child = &new_table[6];
new_table[4].procname = name;
new_table[4].ctl_name = id;
memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table));
for (i = 6; i < len; i++)
new_table[i].extra1 = client;
if (! (new_header = register_sysctl_table(new_table,0))) {
kfree(new_table);
kfree(name);
return -ENOMEM;
}
sensors_entries[i] = new_header;
return id;
}
void sensors_deregister_entry(int id)
{
ctl_table *table;
id -= 256;
if (sensors_entries[id]) {
table = sensors_entries[id]->ctl_table;
unregister_sysctl_table(sensors_entries[id]);
kfree((void *) (table->procname));
kfree(table);
sensors_entries[id] = 0;
}
}
/* nrels contains initially the maximum number of elements which can be
put in results, and finally the number of elements actually put there.
A magnitude of 1 will multiply everything with 10; etc.
buffer, bufsize is the character buffer we read from and its length.
results will finally contain the parsed integers.
Buffer should contain several reals, separated by whitespace. A real
has the following syntax:
[ Minus ] Digit* [ Dot Digit* ]
(everything between [] is optional; * means zero or more).
When the next character is unparsable, everything is skipped until the
next whitespace.
WARNING! This is tricky code. I have tested it, but there may still be
hidden bugs in it, even leading to crashes and things!
*/
void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
long *results, int magnitude)
{
int maxels,min,mag;
long res;
char nextchar=0;
maxels = *nrels;
*nrels = 0;
while (bufsize && (*nrels < maxels)) {
/* Skip spaces at the start */
while (bufsize && ! get_user_data(nextchar,(char *) buffer) &&
isspace((int) nextchar)) {
bufsize --;
((char *) buffer)++;
}
/* Well, we may be done now */
if (! bufsize)
return;
/* New defaults for our result */
min = 0;
res = 0;
mag = magnitude;
/* Check for a minus */
if (! get_user_data(nextchar,(char *) buffer) && (nextchar == '-')) {
min=1;
bufsize--;
((char *) buffer)++;
}
/* Digits before a decimal dot */
while (bufsize && !get_user_data(nextchar,(char *) buffer) &&
isdigit((int) nextchar)) {
res = res * 10 + nextchar - '0';
bufsize--;
((char *) buffer)++;
}
/* If mag < 0, we must actually divide here! */
while (mag < 0) {
res = res / 10;
mag++;
}
if (bufsize && (nextchar == '.')) {
/* Skip the dot */
bufsize--;
((char *) buffer)++;
/* Read digits while they are significant */
while(bufsize && (mag > 0) &&
!get_user_data(nextchar,(char *) buffer) &&
isdigit((int) nextchar)) {
res = res * 10 + nextchar - '0';
mag--;
bufsize--;
((char *) buffer)++;
}
}
/* If we are out of data, but mag > 0, we need to scale here */
while (mag > 0) {
res = res * 10;
mag --;
}
/* Skip everything until we hit whitespace */
while(bufsize && !get_user_data(nextchar,(char *) buffer) &&
isspace ((int) nextchar)) {
bufsize --;
((char *) buffer) ++;
}
/* Put res in results */
results[*nrels] = (min?-1:1)*res;
(*nrels)++;
}
/* Well, there may be more in the buffer, but we need no more data.
Ignore anything that is left. */
return;
}
void sensors_write_reals(int nrels,void *buffer,int *bufsize,long *results,
int magnitude)
{
#define BUFLEN 20
char BUF[BUFLEN+1]; /* An individual representation should fit in here! */
char printfstr[10];
int nr=0;
int buflen,mag,times;
int curbufsize=0;
while ((nr < nrels) && (curbufsize < *bufsize)) {
mag=magnitude;
if (nr != 0) {
put_user(' ', (char *) buffer);
curbufsize ++;
((char *) buffer) ++;
}
/* Fill BUF with the representation of the next string */
if (mag <= 0) {
buflen=sprintf(BUF,"%ld",results[nr]);
if (buflen < 0) { /* Oops, a sprintf error! */
*bufsize=0;
return;
}
while ((mag < 0) && (buflen < BUFLEN)) {
BUF[buflen++]='0';
mag++;
}
BUF[buflen]=0;
} else {
times=1;
for (times=1; mag-- > 0; times *= 10);
if (results[nr] < 0) {
BUF[0] = '-';
buflen = 1;
} else
buflen=0;
strcpy(printfstr,"%ld.%0Xld");
printfstr[6]=magnitude+'0';
buflen+=sprintf(BUF+buflen,printfstr,abs(results[nr])/times,
abs(results[nr])%times);
if (buflen < 0) { /* Oops, a sprintf error! */
*bufsize=0;
return;
}
}
/* Now copy it to the user-space buffer */
if (buflen + curbufsize > *bufsize)
buflen=*bufsize-curbufsize;
copy_to_user(buffer,BUF,buflen);
curbufsize += buflen;
(char *) buffer += buflen;
nr ++;
}
if (curbufsize < *bufsize) {
put_user('\n', (char *) buffer);
curbufsize ++;
}
*bufsize=curbufsize;
}
int sensors_init(void)
{
printk("sensors.o version %s (%s)\n",LM_VERSION,LM_DATE);
return 0;
}
int sensors_cleanup(void)
{
return 0;
}
#ifdef MODULE
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM78 driver");
int init_module(void)
{
return sensors_init();
}
int cleanup_module(void)
{
return sensors_cleanup();
}
#endif /* MODULE */

View File

@@ -23,16 +23,23 @@ MODULE_DIR := src
# Regrettably, even 'simply expanded variables' will not put their currently
# defined value verbatim into the command-list of rules...
SRCTARGETS := $(MODULE_DIR)/smbus.o $(MODULE_DIR)/piix4.o $(MODULE_DIR)/isa.o \
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/i2c-proc.o
$(MODULE_DIR)/lm78.o $(MODULE_DIR)/sensors.o \
$(MODULE_DIR)/i2c-proc.o
HEADERFILES := $(MODULE_DIR)/sensors.h
# Include all dependency files
INCLUDEFILES += $(SRCTARGETS:.o=.d)
all :: $(SRCTARGETS)
all-src: $(SRCTARGETS)
all :: all-src
install ::
install-src:
$(MKDIR) $(MODDIR)
install -o root -g root -m 644 $(SRCTARGETS) $(MODDIR)
install -o root -g root -m 644 $(HEADERFILES) $(INCLUDEDIR)
install :: install-src
clean ::
clean-src:
$(RM) $(SRCTARGETS) $(SRCTARGETS:.o=.d)
clean :: clean-src

View File

@@ -37,4 +37,20 @@
#endif
#endif /* def MODULE */
/* copy_from/to_usr is called memcpy_from/to_fs in 2.0 kernels; perhaps in
some early 2.1 kernels too? */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define copy_from_user memcpy_fromfs
#define copy_to_user memcpy_tofs
#endif
/* get_user was redefined in 2.1 kernels to use two arguments, and returns
an error code */
#if (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,4))
#define get_user_data(to,from) ((to) = get_user(from),0)
#else
#define get_user_data(to,from) get_user(to,from)
#endif
#endif /* SENSORS_COMPAT_H */

View File

@@ -1,3 +1,4 @@
/*
lm78.c - A Linux module for reading sensor data.
Copyright (c) 1998 Frodo Looijaard <frodol@dds.nl>
@@ -21,15 +22,112 @@
#include <linux/malloc.h>
#include <linux/proc_fs.h>
#include <linux/ioport.h>
#include <linux/sysctl.h>
#include <asm/errno.h>
#include <asm/io.h>
#include <linux/types.h>
#include "lm78.h"
#include "smbus.h"
#include "version.h"
#include "isa.h"
#include "sensors.h"
#include "i2c.h"
#include "compat.h"
/* Many LM78 constants needed below */
/* Length of ISA address segment */
#define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define LM78_ADDR_REG_OFFSET 5
#define LM78_DATA_REG_OFFSET 6
/* The LM78 registers */
#define LM78_REG_IN_MAX(nr) (0x2b + (nr) * 2)
#define LM78_REG_IN_MIN(nr) (0x2c + (nr) * 2)
#define LM78_REG_IN(nr) (0x20 + (nr))
#define LM78_REG_FAN_MIN(nr) (0x3a + (nr))
#define LM78_REG_FAN(nr) (0x27 + (nr))
#define LM78_REG_TEMP 0x27
#define LM78_REG_TEMP_OVER 0x39
#define LM78_REG_TEMP_HYST 0x3a
#define LM78_REG_ALARM1 0x41
#define LM78_REG_ALARM2 0x42
#define LM78_REG_VID_FANDIV 0x47
#define LM78_REG_CONFIG 0x40
/* Conversions */
static int lm78_in_conv[7] = {10000, 10000, 10000, 16892, 38000,
-34768, -15050 };
#define IN_TO_REG(val,nr) ((((val) * 100000 / lm78_in_conv[nr]) + 8) / 16)
#define IN_FROM_REG(val,nr) (((val) * 16 * lm78_in_conv[nr]) / 100000)
#define FAN_TO_REG(val) (((val)==0)?255:((1350000+(val))/((val)*2)))
#define FAN_FROM_REG(val) (((val)==0)?-1:\
((val)==255)?0:(1350000 + (val))/((val)*2))
#define TEMP_TO_REG(val) ((val)<0?(val)&0xff:(val))
#define TEMP_FROM_REG(val) ((val)>0x80?(val)-0x100:(val));
#define VID_FROM_REG(val) ((val) == 0x0f?0:350-(val)*10)
#define ALARMS_FROM_REG(val) (val)
#define DIV_FROM_REG(val) (1 >> (val))
#define DIV_TO_REG(val) ((val)==8?3:(val)==4?2:(val)==1?1:2)
/* Initial limits */
#define LM78_INIT_IN_0 280
#define LM78_INIT_IN_1 280
#define LM78_INIT_IN_2 330
#define LM78_INIT_IN_3 500
#define LM78_INIT_IN_4 1200
#define LM78_INIT_IN_5 -1200
#define LM78_INIT_IN_6 -500
#define LM78_INIT_IN_PERCENTAGE 100
#define LM78_INIT_IN_MIN_0 \
(LM78_INIT_IN_0 - LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_0 \
(LM78_INIT_IN_0 + LM78_INIT_IN_0 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_1 \
(LM78_INIT_IN_1 - LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_1 \
(LM78_INIT_IN_1 + LM78_INIT_IN_1 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_2 \
(LM78_INIT_IN_2 - LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_2 \
(LM78_INIT_IN_2 + LM78_INIT_IN_2 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_3 \
(LM78_INIT_IN_3 - LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_3 \
(LM78_INIT_IN_3 + LM78_INIT_IN_3 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_4 \
(LM78_INIT_IN_4 - LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_4 \
(LM78_INIT_IN_4 + LM78_INIT_IN_4 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_5 \
(LM78_INIT_IN_5 - LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_5 \
(LM78_INIT_IN_5 + LM78_INIT_IN_5 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MIN_6 \
(LM78_INIT_IN_6 - LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_IN_MAX_6 \
(LM78_INIT_IN_6 + LM78_INIT_IN_6 * LM78_INIT_IN_PERCENTAGE / 100)
#define LM78_INIT_FAN_MIN_1 3000
#define LM78_INIT_FAN_MIN_2 3000
#define LM78_INIT_FAN_MIN_3 3000
#define LM78_INIT_TEMP_OVER 60
#define LM78_INIT_TEMP_HYST 50
#ifdef MODULE
extern int init_module(void);
@@ -51,6 +149,32 @@ extern int cleanup_module(void);
bad. Quite a lot of bookkeeping is done. A real driver can often cut
some corners. */
/* For each registered LM78, we need to keep some data in memory. That
data is pointed to by lm78_list[NR]->data. The structure itself is
dynamically allocated, at the same time when a new lm78 client is
allocated. */
struct lm78_data {
struct semaphore lock;
int sysctl_id;
struct semaphore update_lock;
char valid; /* !=0 if following fields are valid */
unsigned long last_updated; /* In jiffies */
u8 in[7]; /* Register value */
u8 in_max[7]; /* Register value */
u8 in_min[7]; /* Register value */
u8 fan[3]; /* Register value */
u8 fan_min[3]; /* Register value */
u8 temp; /* Register value */
u8 temp_over; /* Register value */
u8 temp_hyst; /* Register value */
u8 fan_div[2]; /* Register encoding, shifted right */
u8 vid; /* Register encoding, combined */
u16 alarms; /* Register encoding, combined */
};
static int lm78_init(void);
static int lm78_cleanup(void);
@@ -62,21 +186,44 @@ static int lm78_detach_isa(struct isa_client *client);
static int lm78_detach_smbus(struct i2c_client *client);
static int lm78_new_client(struct i2c_adapter *adapter,
struct i2c_client *new_client);
static void lm78_init_client(struct i2c_client *client);
static void lm78_remove_client(struct i2c_client *client);
static int lm78_command(struct i2c_client *client, unsigned int cmd,
void *arg);
static void lm78_inc_use (struct i2c_client *client);
static void lm78_dec_use (struct i2c_client *client);
static int lm78_read_value(struct i2c_client *client, u8 register);
static int lm78_write_value(struct i2c_client *client, u8 register, u8 value);
static void lm78_update_client(struct i2c_client *client);
static void lm78_init_client(struct i2c_client *client);
static int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval,
size_t *oldlenp, void *newval, size_t newlen,
void **context);
static int lm78_proc (ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp);
static void write_in(struct i2c_client *client, int nr, int nrels,
long *results);
static void read_in(struct i2c_client *client, int nr, long *results);
static void write_fan(struct i2c_client *client, int nr, int nrels,
long *results);
static void read_fan(struct i2c_client *client, int nr, long *results);
static void write_temp(struct i2c_client *client, int nrels, long *results);
static void read_temp(struct i2c_client *client, long *results);
static void read_vid(struct i2c_client *client, long *results);
static void read_alarms(struct i2c_client *client, long *results);
static void write_fan_div(struct i2c_client *client, int nrels, long *results);
static void read_fan_div(struct i2c_client *client, long *results);
/* I choose here for semi-static LM78 allocation. Complete dynamic
allocation could also be used; the code needed for this would probably
take more memory than the datastructures take now */
take more memory than the datastructure takes now. */
#define MAX_LM78_NR 4
static struct i2c_client *lm78_list[MAX_LM78_NR];
static struct semaphore lm78_semaphores[MAX_LM78_NR];
/* The driver. I choose to use type i2c_driver, as at is identical to both
smbus_driver and isa_driver, and clients could be of either kind */
@@ -94,8 +241,34 @@ static struct i2c_driver lm78_driver = {
/* Used by lm78_init/cleanup */
static int lm78_initialized = 0;
/* The /proc/sys entries */
/* These files are created for each detected LM78. This is just a template;
though at first sight, you might think we could use a statically
allocated list, we need some way to get back to the parent - which
is done through one of the 'extra' fields which are initialized
when a new copy is allocated. */
static ctl_table lm78_dir_table_template[] = {
{ LM78_SYSCTL_IN0, "in0", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN1, "in1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN2, "in2", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN3, "in3", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN4, "in4", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN5, "in5", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_IN6, "in6", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN1, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN2, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN3, "fan1", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_TEMP, "temp", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_VID, "vid", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_FAN_DIV, "fan_div", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl },
{ LM78_SYSCTL_ALARMS, "alarms", NULL, 0, 0644, NULL, &lm78_proc, &lm78_sysctl }
};
/* This function is called when:
* lm78_driver is inserted, for each available adapter
* lm78_driver is inserted (when this module is loaded), for each
available adapter
* when a new adapter is inserted (and lm78_driver is still present) */
int lm78_attach_adapter(struct i2c_adapter *adapter)
{
@@ -105,7 +278,10 @@ int lm78_attach_adapter(struct i2c_adapter *adapter)
return lm78_detect_smbus(adapter);
}
/* This function is called whenever a client should be removed */
/* This function is called whenever a client should be removed:
* lm78_driver is removed (when this module is unloaded)
* when an adapter is removed which has a lm78 client (and lm78_driver
is still present). */
int lm78_detach_client(struct i2c_client *client)
{
if (i2c_is_isa_client(client))
@@ -138,42 +314,74 @@ int lm78_detect_isa(struct isa_adapter *adapter)
request_region(address, LM78_EXTENT, "lm78");
if (! (new_client = kmalloc(sizeof(struct isa_client), GFP_KERNEL)))
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct isa_client) +
sizeof(struct lm78_data),
GFP_KERNEL)))
{
release_region(address,LM78_EXTENT);
err=-ENOMEM;
continue;
}
goto ERROR1;
}
/* Fill the new client structure with data */
new_client->data = (struct lm78_data *) (new_client + 1);
new_client->addr = 0;
new_client->isa_addr = address;
if ((err = lm78_new_client((struct i2c_adapter *) adapter,
(struct i2c_client *) new_client))) {
release_region(address, LM78_EXTENT);
kfree(new_client);
continue;
}
if ((err = isa_attach_client(new_client))) {
release_region(address, LM78_EXTENT);
lm78_remove_client((struct i2c_client *) new_client);
kfree(new_client);
continue;
}
(struct i2c_client *) new_client)))
goto ERROR2;
/* Tell i2c-core a new client has arrived */
if ((err = isa_attach_client(new_client)))
goto ERROR3;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry((struct i2c_client *) new_client,"lm78",
lm78_dir_table_template) < 0))
goto ERROR4;
((struct lm78_data *) (new_client->data)) -> sysctl_id = err;
/* Initialize the LM78 chip */
lm78_init_client((struct i2c_client *) new_client);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR4:
isa_detach_client(new_client);
ERROR3:
lm78_remove_client((struct i2c_client *) new_client);
ERROR2:
kfree(new_client);
ERROR1:
release_region(address, LM78_EXTENT);
}
return err;
}
/* Deregister and remove a LM78 client */
int lm78_detach_isa(struct isa_client *client)
{
int err;
int err,i;
for (i = 0; i < MAX_LM78_NR; i++)
if ((client == (struct isa_client *) (lm78_list[i])))
break;
if (i == MAX_LM78_NR) {
printk("lm78.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
if ((err = isa_detach_client(client))) {
printk("lm78.o: Client deregistration failed, client not detached.\n");
return err;
}
release_region(client->isa_addr,LM78_EXTENT);
lm78_remove_client((struct i2c_client *) client);
kfree(client);
release_region(client->isa_addr,LM78_EXTENT);
return 0;
}
@@ -194,25 +402,59 @@ int lm78_detect_smbus(struct i2c_adapter *adapter)
/* Real detection code goes here */
new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL);
/* Allocate space for a new client structure */
if (! (new_client = kmalloc(sizeof(struct i2c_client) +
sizeof(struct lm78_data),
GFP_KERNEL))) {
err = -ENOMEM;
continue;
}
/* Fill the new client structure with data */
new_client->data = (struct lm78_data *) (new_client + 1);
new_client->addr = address;
if ((err = lm78_new_client(adapter,new_client))) {
kfree(new_client);
continue;
}
if ((err = i2c_attach_client(new_client))) {
lm78_remove_client(new_client);
kfree(new_client);
continue;
}
if ((err = lm78_new_client(adapter,new_client)))
goto ERROR2;
/* Tell i2c-core a new client has arrived */
if ((err = i2c_attach_client(new_client)))
goto ERROR3;
/* Register a new directory entry with module sensors */
if ((err = sensors_register_entry(new_client,"lm78",
lm78_dir_table_template) < 0))
goto ERROR4;
((struct lm78_data *) (new_client->data))->sysctl_id = err;
/* Initialize the LM78 chip */
lm78_init_client(new_client);
continue;
/* OK, this is not exactly good programming practice, usually. But it is
very code-efficient in this case. */
ERROR4:
i2c_detach_client(new_client);
ERROR3:
lm78_remove_client((struct i2c_client *) new_client);
ERROR2:
kfree(new_client);
}
return err;
}
int lm78_detach_smbus(struct i2c_client *client)
{
int err;
int err,i;
for (i = 0; i < MAX_LM78_NR; i++)
if (client == lm78_list[i])
break;
if ((i == MAX_LM78_NR)) {
printk("lm78.o: Client to detach not found.\n");
return -ENOENT;
}
sensors_deregister_entry(((struct lm78_data *)(client->data))->sysctl_id);
if ((err = i2c_detach_client(client))) {
printk("lm78.o: Client deregistration failed, client not detached.\n");
return err;
@@ -228,6 +470,7 @@ int lm78_new_client(struct i2c_adapter *adapter,
struct i2c_client *new_client)
{
int i;
struct lm78_data *data;
/* First, seek out an empty slot */
for(i = 0; i < MAX_LM78_NR; i++)
@@ -240,28 +483,26 @@ int lm78_new_client(struct i2c_adapter *adapter,
}
lm78_list[i] = new_client;
lm78_semaphores[i] = MUTEX;
new_client->data = &lm78_semaphores[i];
strcpy(new_client->name,"LM78 chip");
new_client->id = i;
new_client->adapter = adapter;
new_client->driver = &lm78_driver;
data = new_client->data;
data->valid = 0;
data->lock = MUTEX;
data->update_lock = MUTEX;
return 0;
}
/* Inverse of lm78_new_client */
void lm78_remove_client(struct i2c_client *client)
{
int i;
for (i = 0; i < MAX_LM78_NR; i++)
if (client == lm78_list[i])
if (client == lm78_list[i])
lm78_list[i] = NULL;
}
/* Called when we have found a new LM78. It should set limits, etc. */
void lm78_init_client(struct i2c_client *client)
{
}
/* No commands defined yet */
int lm78_command(struct i2c_client *client, unsigned int cmd, void *arg)
{
@@ -316,37 +557,346 @@ int lm78_write_value(struct i2c_client *client, u8 reg, u8 value)
return smbus_write_byte_data(client->adapter, client->addr, reg,value);
}
/* ANYTHING BELOW IS JUST AN EXAMPLE. IGNORE IF YOU WANT TO BASE A DRIVER
ON THE CODE IN THIS FILE */
/* Stupid entry in /proc */
static int proc_function(char *buf, char **start, off_t offset, int len,
int unused)
/* Called when we have found a new LM78. It should set limits, etc. */
void lm78_init_client(struct i2c_client *client)
{
int i;
len = 0;
for (i = 0; i < MAX_LM78_NR; i++)
if(lm78_list[i]) {
if(i2c_is_isa_client(lm78_list[i]))
len += sprintf(buf+len,"(isa) %d: address=%x\n",i,
((struct isa_client *) (lm78_list[i]))->isa_addr);
else
len += sprintf(buf+len,"(i2c) %d: address=%x\n",i,
lm78_list[i]->addr);
}
return len;
/* Reset all except Watchdog values and last conversion values
This sets fan-divs to 2, among others */
lm78_write_value(client,LM78_REG_CONFIG,0x80);
lm78_write_value(client,LM78_REG_IN_MIN(0),IN_TO_REG(LM78_INIT_IN_MIN_0,0));
lm78_write_value(client,LM78_REG_IN_MAX(0),IN_TO_REG(LM78_INIT_IN_MAX_0,0));
lm78_write_value(client,LM78_REG_IN_MIN(1),IN_TO_REG(LM78_INIT_IN_MIN_1,1));
lm78_write_value(client,LM78_REG_IN_MAX(1),IN_TO_REG(LM78_INIT_IN_MAX_1,1));
lm78_write_value(client,LM78_REG_IN_MIN(2),IN_TO_REG(LM78_INIT_IN_MIN_2,2));
lm78_write_value(client,LM78_REG_IN_MAX(2),IN_TO_REG(LM78_INIT_IN_MAX_2,2));
lm78_write_value(client,LM78_REG_IN_MIN(3),IN_TO_REG(LM78_INIT_IN_MIN_3,3));
lm78_write_value(client,LM78_REG_IN_MAX(3),IN_TO_REG(LM78_INIT_IN_MAX_3,3));
lm78_write_value(client,LM78_REG_IN_MIN(4),IN_TO_REG(LM78_INIT_IN_MIN_4,4));
lm78_write_value(client,LM78_REG_IN_MAX(4),IN_TO_REG(LM78_INIT_IN_MAX_4,4));
lm78_write_value(client,LM78_REG_IN_MIN(5),IN_TO_REG(LM78_INIT_IN_MIN_5,5));
lm78_write_value(client,LM78_REG_IN_MAX(5),IN_TO_REG(LM78_INIT_IN_MAX_5,5));
lm78_write_value(client,LM78_REG_IN_MIN(6),IN_TO_REG(LM78_INIT_IN_MIN_6,6));
lm78_write_value(client,LM78_REG_IN_MAX(6),IN_TO_REG(LM78_INIT_IN_MAX_6,6));
lm78_write_value(client,LM78_REG_FAN_MIN(1),FAN_TO_REG(LM78_INIT_FAN_MIN_1));
lm78_write_value(client,LM78_REG_FAN_MIN(2),FAN_TO_REG(LM78_INIT_FAN_MIN_2));
lm78_write_value(client,LM78_REG_FAN_MIN(3),FAN_TO_REG(LM78_INIT_FAN_MIN_3));
lm78_write_value(client,LM78_REG_TEMP_OVER,TEMP_TO_REG(LM78_INIT_TEMP_OVER));
lm78_write_value(client,LM78_REG_TEMP_HYST,TEMP_TO_REG(LM78_INIT_TEMP_HYST));
/* Start monitoring */
lm78_write_value(client,LM78_REG_CONFIG,
(lm78_read_value(client,LM78_REG_CONFIG) & 0xf7) | 0x01);
}
/* OK, this is a test entry. Just ignore, it is not important. */
static struct proc_dir_entry proc_entry =
{
0,12,"sensors-test",
S_IFREG | S_IRUGO, 1, 0, 0,
0, NULL,
&proc_function
};
void lm78_update_client(struct i2c_client *client)
{
struct lm78_data *data = client->data;
int i;
down(&data->update_lock);
if ((jiffies - data->last_updated > HZ+HZ/2 ) ||
(jiffies < data->last_updated) || ! data->valid) {
for (i = 0; i <= 6; i++) {
data->in[i] = lm78_read_value(client,LM78_REG_IN(i));
data->in_min[i] = lm78_read_value(client,LM78_REG_IN_MIN(i));
data->in_max[i] = lm78_read_value(client,LM78_REG_IN_MAX(i));
}
for (i = 1; i <= 3; i++) {
data->fan[i-1] = lm78_read_value(client,LM78_REG_FAN(i));
data->fan_min[i-1] = lm78_read_value(client,LM78_REG_FAN_MIN(i));
}
data->temp = lm78_read_value(client,LM78_REG_TEMP);
data->temp_over = lm78_read_value(client,LM78_REG_TEMP_OVER);
data->temp_hyst = lm78_read_value(client,LM78_REG_TEMP_HYST);
i = lm78_read_value(client,LM78_REG_VID_FANDIV);
data->vid = i & 0x0f;
data->fan_div[0] = (i >> 4) & 0x03;
data->fan_div[1] = i >> 6;
data->alarms = lm78_read_value(client,LM78_REG_ALARM1) +
(lm78_read_value(client,LM78_REG_ALARM2) >> 8);
data->last_updated = jiffies;
data->valid = 1;
}
up(&data->update_lock);
}
/* This function is called when /proc/sys/dev/lm78-???/... is accessed */
int lm78_proc (ctl_table *ctl, int write, struct file * filp,
void *buffer, size_t *lenp)
{
int nrels,mag;
long results[7];
struct i2c_client *client = ctl -> extra1;
/* If buffer is size 0, or we try to read when not at the start, we
return nothing. Note that I think writing when not at the start
does not work either, but anyway, this is straight from the kernel
sources. */
if (!*lenp || (filp->f_pos && !write)) {
*lenp = 0;
return 0;
}
/* How many numbers are found within these files, and how to scale them? */
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2:
case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5:
case LM78_SYSCTL_IN6:
nrels=3;
mag=2;
break;
case LM78_SYSCTL_FAN_DIV: case LM78_SYSCTL_TEMP:
nrels=3;
mag=0;
break;
case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3:
nrels=2;
mag=0;
break;
case LM78_SYSCTL_VID:
nrels=1;
mag=2;
break;
case LM78_SYSCTL_ALARMS:
nrels=1;
mag=0;
break;
default: /* Should never be called */
return -EINVAL;
}
/* OK, try writing stuff. */
if (write) {
sensors_parse_reals(&nrels,buffer,*lenp,results,mag);
if (nrels == 0)
return 0;
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break;
case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break;
case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break;
case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break;
case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break;
case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break;
case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break;
case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break;
case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break;
case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break;
case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break;
case LM78_SYSCTL_TEMP: write_temp(client,nrels,results);break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break;
default: /* Should never be called */ *lenp=0; return -EINVAL; break;
}
filp->f_pos += *lenp;
return 0;
} else { /* read */
/* Update all values in LM_Sensor_Data */
lm78_update_client((struct i2c_client *) (ctl->extra1));
/* Read the values to print into results */
switch (ctl->ctl_name) {
case LM78_SYSCTL_IN0: read_in(client,0,results);break;
case LM78_SYSCTL_IN1: read_in(client,1,results);break;
case LM78_SYSCTL_IN2: read_in(client,2,results);break;
case LM78_SYSCTL_IN3: read_in(client,3,results);break;
case LM78_SYSCTL_IN4: read_in(client,4,results);break;
case LM78_SYSCTL_IN5: read_in(client,5,results);break;
case LM78_SYSCTL_IN6: read_in(client,6,results);break;
case LM78_SYSCTL_FAN1: read_fan(client,1,results);break;
case LM78_SYSCTL_FAN2: read_fan(client,2,results);break;
case LM78_SYSCTL_FAN3: read_fan(client,3,results);break;
case LM78_SYSCTL_TEMP: read_temp(client,results);break;
case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break;
case LM78_SYSCTL_VID: read_vid(client,results);break;
case LM78_SYSCTL_ALARMS: read_alarms(client,results);break;
default: /* Should never be called */ return -EINVAL;
}
/* OK, print it now */
sensors_write_reals(nrels,buffer,lenp,results,mag);
filp->f_pos += *lenp;
return 0;
}
}
/* This function is called when a sysctl on a lm78 file is done */
int lm78_sysctl (ctl_table *table, int *name, int nlen, void *oldval,
size_t *oldlenp, void *newval, size_t newlen,
void **context)
{
long results[7];
int nrels,oldlen;
struct i2c_client *client = table -> extra1;
/* How many numbers are found within these files, and how to scale them? */
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: case LM78_SYSCTL_IN1: case LM78_SYSCTL_IN2:
case LM78_SYSCTL_IN3: case LM78_SYSCTL_IN4: case LM78_SYSCTL_IN5:
case LM78_SYSCTL_IN6: case LM78_SYSCTL_TEMP: case LM78_SYSCTL_FAN_DIV:
nrels=3;
break;
case LM78_SYSCTL_FAN1: case LM78_SYSCTL_FAN2: case LM78_SYSCTL_FAN3:
nrels=2;
break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS:
nrels=1;
break;
default: /* Should never be called */
return -EINVAL;
}
/* Check if we need to output the old values */
if (oldval && oldlenp && ! get_user_data(oldlen,oldlenp) && oldlen) {
/* Update all values in LM_Sensor_Data */
lm78_update_client((struct i2c_client *) (table->extra1));
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: read_in(client,0,results);break;
case LM78_SYSCTL_IN1: read_in(client,1,results);break;
case LM78_SYSCTL_IN2: read_in(client,2,results);break;
case LM78_SYSCTL_IN3: read_in(client,3,results);break;
case LM78_SYSCTL_IN4: read_in(client,4,results);break;
case LM78_SYSCTL_IN5: read_in(client,5,results);break;
case LM78_SYSCTL_IN6: read_in(client,6,results);break;
case LM78_SYSCTL_FAN1: read_fan(client,1,results);break;
case LM78_SYSCTL_FAN2: read_fan(client,2,results);break;
case LM78_SYSCTL_FAN3: read_fan(client,3,results);break;
case LM78_SYSCTL_TEMP: read_temp(client,results);break;
case LM78_SYSCTL_FAN_DIV: read_fan_div(client,results);break;
case LM78_SYSCTL_VID: read_vid(client,results);break;
case LM78_SYSCTL_ALARMS: read_alarms(client,results);break;
default: /* Should never be called */ return -EINVAL;
}
/* Note the rounding factor! */
if (nrels * sizeof(long) < oldlen)
oldlen = nrels * sizeof(long);
oldlen = (oldlen / sizeof(long)) * sizeof(long);
copy_to_user(oldval,results,oldlen);
put_user(oldlen,oldlenp);
}
/* Check to see whether we need to read the new values */
if (newval && newlen) {
if (nrels * sizeof(long) < newlen)
newlen = nrels * sizeof(long);
nrels = newlen / sizeof(long);
newlen = (newlen / sizeof(long)) * sizeof(long);
copy_from_user(results,newval,newlen);
switch (table->ctl_name) {
case LM78_SYSCTL_IN0: write_in(client,0,nrels,results); break;
case LM78_SYSCTL_IN1: write_in(client,1,nrels,results); break;
case LM78_SYSCTL_IN2: write_in(client,2,nrels,results); break;
case LM78_SYSCTL_IN3: write_in(client,3,nrels,results); break;
case LM78_SYSCTL_IN4: write_in(client,4,nrels,results); break;
case LM78_SYSCTL_IN5: write_in(client,5,nrels,results); break;
case LM78_SYSCTL_IN6: write_in(client,6,nrels,results); break;
case LM78_SYSCTL_FAN1: write_fan(client,1,nrels,results); break;
case LM78_SYSCTL_FAN2: write_fan(client,2,nrels,results); break;
case LM78_SYSCTL_FAN3: write_fan(client,3,nrels,results); break;
case LM78_SYSCTL_TEMP: write_temp(client,nrels,results); break;
case LM78_SYSCTL_FAN_DIV: write_fan_div(client,nrels,results);break;
case LM78_SYSCTL_VID: case LM78_SYSCTL_ALARMS: break;
default: /* Should never be called */ return -EINVAL; break;
}
}
return 1; /* We have done all the work */
}
void write_in(struct i2c_client *client, int nr, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->in_min[nr] = IN_TO_REG(results[0],nr);
lm78_write_value(client,LM78_REG_IN_MIN(nr),data->in_min[nr]);
}
if (nrels >= 2) {
data->in_max[nr] = IN_TO_REG(results[1],nr);
lm78_write_value(client,LM78_REG_IN_MAX(nr),data->in_max[nr]);
}
}
void read_in(struct i2c_client *client, int nr, long *results)
{
struct lm78_data *data = client->data;
results[0] = IN_FROM_REG(data->in_min[nr],nr);
results[1] = IN_FROM_REG(data->in_min[nr],nr);
results[2] = IN_FROM_REG(data->in_min[nr],nr);
}
void write_fan(struct i2c_client *client, int nr, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->fan_min[nr-1] = FAN_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_FAN_MIN(nr),data->fan_min[nr-1]);
}
}
void read_fan(struct i2c_client *client, int nr, long *results)
{
struct lm78_data *data = client->data;
results[0] = FAN_FROM_REG(data->fan_min[nr-1]);
results[1] = FAN_FROM_REG(data->fan[nr-1]);
}
void write_temp(struct i2c_client *client, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 1) {
data->temp_over = TEMP_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_TEMP_OVER,data->temp_over);
}
if (nrels >= 2) {
data->temp_hyst = TEMP_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_TEMP_HYST,data->temp_hyst);
}
}
void read_temp(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = TEMP_FROM_REG(data->temp_over);
results[1] = TEMP_FROM_REG(data->temp_hyst);
results[2] = TEMP_FROM_REG(data->temp);
}
void read_vid(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = VID_FROM_REG(data->vid);
}
void read_alarms(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = ALARMS_FROM_REG(data->alarms);
}
void read_fan_div(struct i2c_client *client, long *results)
{
struct lm78_data *data = client->data;
results[0] = DIV_FROM_REG(data->fan_div[0]);
results[1] = DIV_FROM_REG(data->fan_div[1]);
}
void write_fan_div(struct i2c_client *client, int nrels, long *results)
{
struct lm78_data *data = client->data;
if (nrels >= 2)
data->fan_div[1] = DIV_TO_REG(results[1]);
if (nrels >= 1) {
data->fan_div[0] = DIV_TO_REG(results[0]);
lm78_write_value(client,LM78_REG_VID_FANDIV,
(data->fan_div[0] >> 4) | (data->fan_div[1] >> 6));
}
}
int lm78_init(void)
{
int res;
@@ -354,16 +904,6 @@ int lm78_init(void)
printk("lm78.o version %s (%s)\n",LM_VERSION,LM_DATE);
lm78_initialized = 0;
/* OK, we register some stupid proc file here. This is *just* *temporary*,
for test purposes. Ignore if you want. Only works for kernels 2.0.x. */
if ((res = proc_register_dynamic(&proc_root,&proc_entry))) {
printk("lm78.o: Couldn't create /proc/sensors-test, "
"module not inserted.\n");
lm78_cleanup();
return res;
}
lm78_initialized ++;
if ((res =i2c_add_driver(&lm78_driver))) {
printk("lm78.o: Driver registration failed, module not inserted.\n");
lm78_cleanup();
@@ -377,7 +917,7 @@ int lm78_cleanup(void)
{
int res;
if (lm78_initialized >= 2) {
if (lm78_initialized >= 1) {
if ((res = i2c_del_driver(&lm78_driver))) {
printk("lm78.o: Driver deregistration failed, module not removed.\n");
return res;
@@ -385,14 +925,6 @@ int lm78_cleanup(void)
} else
lm78_initialized --;
if (lm78_initialized >= 1) {
if((res = proc_unregister(&proc_root,proc_entry.low_ino))) {
printk("lm78.o: Deregistration of /proc/sensors_test failed, "
"module not removed.\n");
return res;
}
} else
lm78_initialized --;
return 0;
}

View File

@@ -1,30 +0,0 @@
/*
lm78.h - A Linux module for reading sensor data.
Copyright (c) 1998 Frodo Looijaard <frodol@dds.nl>
This program is free software; you can redistribute it and/or modify
it under the terms 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef SENSORS_SRC_LM78
#define SENSORS_SRC_LM78
/* Length of ISA address segment */
#define LM78_EXTENT 8
/* Where are the ISA address/data registers relative to the base address */
#define LM78_ADDR_REG_OFFSET 5
#define LM78_DATA_REG_OFFSET 6
#endif /* ndef SENSORS_SRC_LM78 */

346
src/sensors.c Normal file
View File

@@ -0,0 +1,346 @@
/*
sensors.c - A Linux module for reading sensor data.
Copyright (c) 1998 Frodo Looijaard <frodol@dds.nl>
This program is free software; you can redistribute it and/or modify
it under the terms 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., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/malloc.h>
#include <linux/ctype.h>
#include <linux/sysctl.h>
#include "version.h"
#include "compat.h"
#include "i2c.h"
#include "isa.h"
#include "sensors.h"
#ifdef MODULE
extern int init_module(void);
extern int cleanup_module(void);
#endif /* MODULE */
int sensors_register_entry(struct i2c_client *client ,const char *prefix,
ctl_table *ctl_template);
static int sensors_create_name(char **name, const char *prefix,
struct i2c_adapter * adapter, int addr);
static int sensors_init(void);
static int sensors_cleanup(void);
#define SENSORS_ENTRY_MAX 20
static struct ctl_table_header *sensors_entries[SENSORS_ENTRY_MAX];
static ctl_table sysctl_table[] = {
{ CTL_DEV, "dev", NULL, 0, 0555 },
{ 0 },
{ DEV_SENSORS, "sensors", NULL, 0, 0555 },
{ 0 },
{ 0, NULL, NULL, 0, 0555 },
{ 0 }
};
/* This returns a nice name for a new directory; for example lm78-isa-0310
(for a LM78 chip on the ISA bus at port 0x310), or lm75-i2c-3-4e (for
a LM75 chip on the third i2c bus at address 0x4e).
name is allocated first. */
int sensors_create_name(char **name, const char *prefix,
struct i2c_adapter * adapter, int addr)
{
char name_buffer[50];
int id;
if (i2c_is_isa_adapter(adapter))
sprintf(name_buffer,"%s-isa-%d",prefix,addr);
else {
if ((id = i2c_adapter_id(adapter)) < 0);
return -ENOENT;
sprintf(name_buffer,"%s-i2c-%d-%d",prefix,id,addr);
}
*name = kmalloc(strlen(name_buffer)+1,GFP_KERNEL);
strcpy(*name,name_buffer);
return 0;
}
/* This rather complex function must be called when you want to add an entry
to /proc/sys/dev/sensors/chips (not yet implemented). It also creates
a new directory within /proc/sys/dev/sensors/.
ctl_template should be a template of the newly created directory. It is
copied in memory. The extra1 field of each file is set to point to client.
If any driver wants subdirectories within the newly created directory,
this function must be updated! */
int sensors_register_entry(struct i2c_client *client ,const char *prefix,
ctl_table *ctl_template)
{
int i,res,len,id;
ctl_table *new_table;
char *name;
struct ctl_table_header *new_header;
if ((res = sensors_create_name(&name,prefix,client->adapter,
i2c_is_isa_client(client)?
((struct isa_client *) client)->isa_addr:
client->addr)))
return res;
for (id = 0; id < SENSORS_ENTRY_MAX; i++)
if (! sensors_entries[id]) {
break;
}
if (id == SENSORS_ENTRY_MAX) {
kfree(name);
return -ENOMEM;
}
id += 256;
len = 0;
while (ctl_template[len].procname)
len++;
len += 7;
if (! (new_table = kmalloc(sizeof(ctl_table) * len,GFP_KERNEL))) {
kfree(name);
return -ENOMEM;
}
memcpy(new_table,sysctl_table,6 * sizeof(ctl_table));
new_table[0].child = &new_table[2];
new_table[2].child = &new_table[4];
new_table[4].child = &new_table[6];
new_table[4].procname = name;
new_table[4].ctl_name = id;
memcpy(new_table+6,ctl_template,(len-6) * sizeof(ctl_table));
for (i = 6; i < len; i++)
new_table[i].extra1 = client;
if (! (new_header = register_sysctl_table(new_table,0))) {
kfree(new_table);
kfree(name);
return -ENOMEM;
}
sensors_entries[i] = new_header;
return id;
}
void sensors_deregister_entry(int id)
{
ctl_table *table;
id -= 256;
if (sensors_entries[id]) {
table = sensors_entries[id]->ctl_table;
unregister_sysctl_table(sensors_entries[id]);
kfree((void *) (table->procname));
kfree(table);
sensors_entries[id] = 0;
}
}
/* nrels contains initially the maximum number of elements which can be
put in results, and finally the number of elements actually put there.
A magnitude of 1 will multiply everything with 10; etc.
buffer, bufsize is the character buffer we read from and its length.
results will finally contain the parsed integers.
Buffer should contain several reals, separated by whitespace. A real
has the following syntax:
[ Minus ] Digit* [ Dot Digit* ]
(everything between [] is optional; * means zero or more).
When the next character is unparsable, everything is skipped until the
next whitespace.
WARNING! This is tricky code. I have tested it, but there may still be
hidden bugs in it, even leading to crashes and things!
*/
void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
long *results, int magnitude)
{
int maxels,min,mag;
long res;
char nextchar=0;
maxels = *nrels;
*nrels = 0;
while (bufsize && (*nrels < maxels)) {
/* Skip spaces at the start */
while (bufsize && ! get_user_data(nextchar,(char *) buffer) &&
isspace((int) nextchar)) {
bufsize --;
((char *) buffer)++;
}
/* Well, we may be done now */
if (! bufsize)
return;
/* New defaults for our result */
min = 0;
res = 0;
mag = magnitude;
/* Check for a minus */
if (! get_user_data(nextchar,(char *) buffer) && (nextchar == '-')) {
min=1;
bufsize--;
((char *) buffer)++;
}
/* Digits before a decimal dot */
while (bufsize && !get_user_data(nextchar,(char *) buffer) &&
isdigit((int) nextchar)) {
res = res * 10 + nextchar - '0';
bufsize--;
((char *) buffer)++;
}
/* If mag < 0, we must actually divide here! */
while (mag < 0) {
res = res / 10;
mag++;
}
if (bufsize && (nextchar == '.')) {
/* Skip the dot */
bufsize--;
((char *) buffer)++;
/* Read digits while they are significant */
while(bufsize && (mag > 0) &&
!get_user_data(nextchar,(char *) buffer) &&
isdigit((int) nextchar)) {
res = res * 10 + nextchar - '0';
mag--;
bufsize--;
((char *) buffer)++;
}
}
/* If we are out of data, but mag > 0, we need to scale here */
while (mag > 0) {
res = res * 10;
mag --;
}
/* Skip everything until we hit whitespace */
while(bufsize && !get_user_data(nextchar,(char *) buffer) &&
isspace ((int) nextchar)) {
bufsize --;
((char *) buffer) ++;
}
/* Put res in results */
results[*nrels] = (min?-1:1)*res;
(*nrels)++;
}
/* Well, there may be more in the buffer, but we need no more data.
Ignore anything that is left. */
return;
}
void sensors_write_reals(int nrels,void *buffer,int *bufsize,long *results,
int magnitude)
{
#define BUFLEN 20
char BUF[BUFLEN+1]; /* An individual representation should fit in here! */
char printfstr[10];
int nr=0;
int buflen,mag,times;
int curbufsize=0;
while ((nr < nrels) && (curbufsize < *bufsize)) {
mag=magnitude;
if (nr != 0) {
put_user(' ', (char *) buffer);
curbufsize ++;
((char *) buffer) ++;
}
/* Fill BUF with the representation of the next string */
if (mag <= 0) {
buflen=sprintf(BUF,"%ld",results[nr]);
if (buflen < 0) { /* Oops, a sprintf error! */
*bufsize=0;
return;
}
while ((mag < 0) && (buflen < BUFLEN)) {
BUF[buflen++]='0';
mag++;
}
BUF[buflen]=0;
} else {
times=1;
for (times=1; mag-- > 0; times *= 10);
if (results[nr] < 0) {
BUF[0] = '-';
buflen = 1;
} else
buflen=0;
strcpy(printfstr,"%ld.%0Xld");
printfstr[6]=magnitude+'0';
buflen+=sprintf(BUF+buflen,printfstr,abs(results[nr])/times,
abs(results[nr])%times);
if (buflen < 0) { /* Oops, a sprintf error! */
*bufsize=0;
return;
}
}
/* Now copy it to the user-space buffer */
if (buflen + curbufsize > *bufsize)
buflen=*bufsize-curbufsize;
copy_to_user(buffer,BUF,buflen);
curbufsize += buflen;
(char *) buffer += buflen;
nr ++;
}
if (curbufsize < *bufsize) {
put_user('\n', (char *) buffer);
curbufsize ++;
}
*bufsize=curbufsize;
}
int sensors_init(void)
{
printk("sensors.o version %s (%s)\n",LM_VERSION,LM_DATE);
return 0;
}
int sensors_cleanup(void)
{
return 0;
}
#ifdef MODULE
MODULE_AUTHOR("Frodo Looijaard <frodol@dds.nl>");
MODULE_DESCRIPTION("LM78 driver");
int init_module(void)
{
return sensors_init();
}
int cleanup_module(void)
{
return sensors_cleanup();
}
#endif /* MODULE */

View File

@@ -20,8 +20,45 @@
#ifndef SENSORS_SENSORS_H
#define SENSORS_SENSORS_H
#include <linux/sysctl.h>
#ifdef __KERNEL__
extern int sensors_register_entry(struct i2c_client *client,
const char *prefix, ctl_table *ctl_template);
extern void sensors_deregister_entry(int id);
extern void sensors_parse_reals(int *nrels, void *buffer, int bufsize,
long *results, int magnitude);
extern void sensors_write_reals(int nrels,void *buffer,int *bufsize,
long *results, int magnitude);
#endif /* def __KERNEL__ */
/* Driver IDs */
#define I2C_DRIVERID_I2CPROC 1001
#define I2C_DRIVERID_LM78 1002
/* Sysctl IDs */
#ifdef DEV_HWMON
#define DEV_SENSORS DEV_HWMON
#else /* ndef DEV_HWMOM */
#define DEV_SENSORS 2 /* The id of the lm_sensors directory within the
dev table */
#endif /* def DEV_HWMON */
#define LM78_SYSCTL_IN0 1000
#define LM78_SYSCTL_IN1 1001
#define LM78_SYSCTL_IN2 1002
#define LM78_SYSCTL_IN3 1003
#define LM78_SYSCTL_IN4 1004
#define LM78_SYSCTL_IN5 1005
#define LM78_SYSCTL_IN6 1006
#define LM78_SYSCTL_FAN1 1101
#define LM78_SYSCTL_FAN2 1102
#define LM78_SYSCTL_FAN3 1103
#define LM78_SYSCTL_TEMP 1200
#define LM78_SYSCTL_VID 1300
#define LM78_SYSCTL_FAN_DIV 2000
#define LM78_SYSCTL_ALARMS 2001
#endif /* def SENSORS_SENSORS_H */