From 85e409d32285ebef2246355c1ee8422dd560a58d Mon Sep 17 00:00:00 2001 From: Frodo Looijaard Date: Fri, 27 Nov 1998 22:02:09 +0000 Subject: [PATCH] i2c-proc now creates complete /proc/bus/i2c * i2c-bus is renamed to i2c-proc * small patch in i2c-core.c (see correspondence with Simon Vogl) * sensors.h contains i2c constants * It seems lm78.[ch] was not yet committed?!? git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@16 7894878c-1315-0410-8ee3-d5d059ff63e0 --- Makefile | 5 + i2c/MODIFICATIONS | 12 ++ i2c/i2c-core.c | 6 +- kernel/Module.mk | 2 +- kernel/chips/lm78.c | 416 +++++++++++++++++++++++++++++++++++++++ kernel/i2c-proc.c | 318 ++++++++++++++++++++++++++++++ kernel/include/sensors.h | 27 +++ src/Module.mk | 2 +- src/i2c-bus.c | 178 ----------------- src/i2c-proc.c | 318 ++++++++++++++++++++++++++++++ src/lm78.c | 416 +++++++++++++++++++++++++++++++++++++++ src/lm78.h | 30 +++ src/sensors.h | 27 +++ 13 files changed, 1574 insertions(+), 183 deletions(-) create mode 100644 kernel/chips/lm78.c create mode 100644 kernel/i2c-proc.c create mode 100644 kernel/include/sensors.h delete mode 100644 src/i2c-bus.c create mode 100644 src/i2c-proc.c create mode 100644 src/lm78.c create mode 100644 src/lm78.h create mode 100644 src/sensors.h diff --git a/Makefile b/Makefile index dedc75d5..3240ff04 100644 --- a/Makefile +++ b/Makefile @@ -86,6 +86,11 @@ CC := gcc CFLAGS := -D__KERNEL__ -DMODULE -I. -Ii2c -O2 -fomit-frame-pointer EXCFLAGS := -I. -O2 -Ii2c +ifeq ($(DEBUG),1) +CFLAGS += -DDEBUG +EXCFLAGS += -DDEBUG +endif + ifeq ($(SMP),1) CFLAGS += -D__SMP__ endif diff --git a/i2c/MODIFICATIONS b/i2c/MODIFICATIONS index 9c118cea..560a31cd 100644 --- a/i2c/MODIFICATIONS +++ b/i2c/MODIFICATIONS @@ -3,6 +3,12 @@ by Simon Vogl, and the code included in and beneath this directory. None of these changes are important; you can still use a self-compiled version of the i2c code and not compile the version in here. +! Changes marked with an exclamation mark *are* important, and *must* + be applied! If there are changes like this in this list, you *must* + use our version of the i2c module! + +Here come the changes: + * rscid declarations are changed This is an unfortunate interaction between our CVS server and Simon's RCS (CVS?) server. As these ids do not seem to be accessed anywhere, @@ -31,3 +37,9 @@ of the i2c code and not compile the version in here. * Removed files: object files and executables Everywhere, remakeable files are removed, to keep the source distribution small and lean. + +! i2c-core: i2c_del_adapter() change + Replaced + i2c_detach_client(client); + with: + client->driver->detach_client(client); diff --git a/i2c/i2c-core.c b/i2c/i2c-core.c index d2aa4e52..9aaf1b6d 100644 --- a/i2c/i2c-core.c +++ b/i2c/i2c-core.c @@ -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.2 1998/11/03 03:48:44 phil Exp $" +#define RCSID "$Id: i2c-core.c,v 1.3 1998/11/19 03:07:18 frodo Exp $" /* ------------------------------------------------------------------------- */ #include @@ -173,7 +173,7 @@ int i2c_del_adapter(struct i2c_adapter *adap) * flag, as _all_ clients that reside on the adapter * must be deleted, as this would cause invalid states. */ - i2c_detach_client(client); + client->driver->detach_client(client); } /* all done, now unregister */ adapters[i] = NULL; @@ -248,7 +248,7 @@ int i2c_del_driver(struct i2c_driver *driver) if (client != NULL && client->driver == driver) { DEB2(printk("i2c: detaching client %s:\n", client->name)); - /*i2c_detach_client(client);*/ + i2c_detach_client(client); driver->detach_client(client); } } diff --git a/kernel/Module.mk b/kernel/Module.mk index 174372c3..454d3bac 100644 --- a/kernel/Module.mk +++ b/kernel/Module.mk @@ -23,7 +23,7 @@ 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-bus.o + $(MODULE_DIR)/lm78.o $(MODULE_DIR)/i2c-proc.o # Include all dependency files INCLUDEFILES += $(SRCTARGETS:.o=.d) diff --git a/kernel/chips/lm78.c b/kernel/chips/lm78.c new file mode 100644 index 00000000..8a469713 --- /dev/null +++ b/kernel/chips/lm78.c @@ -0,0 +1,416 @@ +/* + lm78.c - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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 +#include +#include +#include +#include +#include +#include +#include "lm78.h" +#include "smbus.h" +#include "version.h" +#include "isa.h" +#include "sensors.h" + + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, LM78 chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + LM78 chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the LM78 at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +static int lm78_init(void); +static int lm78_cleanup(void); + +static int lm78_attach_adapter(struct i2c_adapter *adapter); +static int lm78_detect_isa(struct isa_adapter *adapter); +static int lm78_detect_smbus(struct i2c_adapter *adapter); +static int lm78_detach_client(struct i2c_client *client); +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); + +/* 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 */ +#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 */ +static struct i2c_driver lm78_driver = { + /* name */ "LM78 sensor chip driver", + /* id */ I2C_DRIVERID_LM78, + /* flags */ DF_NOTIFY, + /* attach_adapter */ &lm78_attach_adapter, + /* detach_client */ &lm78_detach_client, + /* command */ &lm78_command, + /* inc_use */ &lm78_inc_use, + /* dec_use */ &lm78_dec_use +}; + +/* Used by lm78_init/cleanup */ +static int lm78_initialized = 0; + +/* This function is called when: + * lm78_driver is inserted, for each available adapter + * when a new adapter is inserted (and lm78_driver is still present) */ +int lm78_attach_adapter(struct i2c_adapter *adapter) +{ + if (i2c_is_isa_adapter(adapter)) + return lm78_detect_isa((struct isa_adapter *) adapter); + else + return lm78_detect_smbus(adapter); +} + +/* This function is called whenever a client should be removed */ +int lm78_detach_client(struct i2c_client *client) +{ + if (i2c_is_isa_client(client)) + return lm78_detach_isa((struct isa_client *) client); + else + return lm78_detach_smbus(client); +} + +/* Detect whether there is a LM78 on the ISA bus, register and initialize + it. */ +int lm78_detect_isa(struct isa_adapter *adapter) +{ + int address,err; + struct isa_client *new_client; + + /* OK, this is no detection. I know. It will do for now, though. */ + + err = 0; + for (address = 0x290; (! err) && (address <= 0x290); address += 0x08) { + if (check_region(address, LM78_EXTENT)) + continue; + + if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff) { + outb_p(0x00,address + LM78_ADDR_REG_OFFSET); + if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff) + continue; + } + + /* Real detection code goes here */ + + request_region(address, LM78_EXTENT, "lm78"); + + if (! (new_client = kmalloc(sizeof(struct isa_client), GFP_KERNEL))) + { + release_region(address,LM78_EXTENT); + err=-ENOMEM; + continue; + } + 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; + } + lm78_init_client((struct i2c_client *) new_client); + } + return err; +} + +/* Deregister and remove a LM78 client */ +int lm78_detach_isa(struct isa_client *client) +{ + int err; + 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); + return 0; +} + +int lm78_detect_smbus(struct i2c_adapter *adapter) +{ + int address,err; + struct i2c_client *new_client; + + /* OK, this is no detection. I know. It will do for now, though. */ + err = 0; + for (address = 0x20; (! err) && (address <= 0x2f); address ++) { + + /* Later on, we will keep a list of registered addresses for each + adapter, and check whether they are used here */ + + if (smbus_read_byte_data(adapter,address,1) == 0xff) + continue; + + /* Real detection code goes here */ + + new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + 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; + } + lm78_init_client(new_client); + } + return err; +} + +int lm78_detach_smbus(struct i2c_client *client) +{ + int err; + if ((err = i2c_detach_client(client))) { + printk("lm78.o: Client deregistration failed, client not detached.\n"); + return err; + } + lm78_remove_client(client); + kfree(client); + return 0; +} + + +/* Find a free slot, and initialize most of the fields */ +int lm78_new_client(struct i2c_adapter *adapter, + struct i2c_client *new_client) +{ + int i; + + /* First, seek out an empty slot */ + for(i = 0; i < MAX_LM78_NR; i++) + if (! lm78_list[i]) + break; + if (i == MAX_LM78_NR) { + printk("lm78.o: No empty slots left, recompile and heighten " + "MAX_LM78_NR!\n"); + return -ENOMEM; + } + + 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; + return 0; +} + +void lm78_remove_client(struct i2c_client *client) +{ + int i; + for (i = 0; i < MAX_LM78_NR; 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) +{ + return 0; +} + +/* Nothing here yet */ +void lm78_inc_use (struct i2c_client *client) +{ +} + +/* Nothing here yet */ +void lm78_dec_use (struct i2c_client *client) +{ +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down((struct semaphore *) (client->data)); + outb_p(reg,(((struct isa_client *) client)->isa_addr) + + LM78_ADDR_REG_OFFSET); + res = inb_p((((struct isa_client *) client)->isa_addr) + + LM78_DATA_REG_OFFSET); + up((struct semaphore *) (client->data)); + return res; + } else + return smbus_read_byte_data(client->adapter,client->addr, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down((struct semaphore *) (client->data)); + outb_p(reg,((struct isa_client *) client)->isa_addr + LM78_ADDR_REG_OFFSET); + outb_p(value,((struct isa_client *) client)->isa_addr + LM78_DATA_REG_OFFSET); + up((struct semaphore *) (client->data)); + return 0; + } else + 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) +{ + 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; +} + +/* 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 + }; + + +int lm78_init(void) +{ + int res; + + 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(); + return res; + } + lm78_initialized ++; + return 0; +} + +int lm78_cleanup(void) +{ + int res; + + if (lm78_initialized >= 2) { + if ((res = i2c_del_driver(&lm78_driver))) { + printk("lm78.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } 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; +} + + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM78 driver"); + +int init_module(void) +{ + return lm78_init(); +} + +int cleanup_module(void) +{ + return lm78_cleanup(); +} + +#endif /* MODULE */ + diff --git a/kernel/i2c-proc.c b/kernel/i2c-proc.c new file mode 100644 index 00000000..75c29208 --- /dev/null +++ b/kernel/i2c-proc.c @@ -0,0 +1,318 @@ +/* + i2c-proc.c - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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 +#include + +#include "i2c.h" +#include "smbus.h" +#include "isa.h" +#include "version.h" +#include "compat.h" +#include "sensors.h" + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); +static int i2cproc_attach_adapter(struct i2c_adapter *adapter); +static int i2cproc_detach_client(struct i2c_client *client); +static int i2cproc_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void i2cproc_inc_use(struct i2c_client *client); +static void i2cproc_dec_use(struct i2c_client *client); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +static struct proc_dir_entry *proc_bus_i2c; + +#else /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ + +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int unused); + +static struct proc_dir_entry proc_bus_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "bus", + /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, + /* nlink */ 1, /* Corrected by proc_register[_dynamic] */ + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ &proc_dir_inode_operations, + }; + +static struct proc_dir_entry proc_bus_i2c_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "i2c", + /* mode */ S_IRUGO | S_IFREG, + /* nlink */ 1, + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ NULL, + /* get_info */ &read_bus_i2c + }; + +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ + +/* Used by init/cleanup */ +static int i2cproc_initialized; + +/* This is a sorted list of all adapters that will have entries in /proc/bus */ +static struct i2c_adapter *i2cproc_adapters[I2C_ADAP_MAX]; + +/* We will use a nasty trick: we register a driver, that will be notified + for each adapter. Then, we register a dummy client on the adapter, that + will get notified if the adapter is removed. This is the same trick as + used in i2c/i2c-dev.c */ +static struct i2c_driver i2cproc_driver = { + /* name */ "i2c-proc dummy driver", + /* id */ I2C_DRIVERID_I2CPROC, + /* flags */ DF_NOTIFY, + /* attach_adapter */ &i2cproc_attach_adapter, + /* detach_client */ &i2cproc_detach_client, + /* command */ &i2cproc_command, + /* inc_use */ &i2cproc_inc_use, + /* dec_use */ &i2cproc_dec_use +}; + +static struct i2c_client i2cproc_client_template = { + /* name */ "i2c-proc dummy client", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cproc_driver, + /* data */ NULL +}; + +int i2cproc_init(void) +{ + int res; + + printk("i2c-proc.o version %s (%s)\n",LM_VERSION,LM_DATE); + i2cproc_initialized = 0; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + if (! proc_bus) { + printk("i2c-proc.o: /proc/bus/ does not exist, module not inserted.\n"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (proc_bus_i2c) + proc_bus_i2c->read_proc = &read_bus_i2c; + else { + printk("i2c-proc.o: Could not create /proc/bus/i2c, " + "module not inserted.\n"); + i2cproc_cleanup(); + return -ENOENT; + } + i2cproc_initialized += 2; +#else + /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module + introduced it, or we are fucked. */ + if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { + printk("i2c-proc.o: Could not create /proc/bus/, module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { + printk("i2c-proc.o: Could not create /proc/bus/i2c, " + "module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; +#endif + if ((res = i2c_add_driver(&i2cproc_driver))) { + printk("i2c-proc.o: Driver registration failed, module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + return 0; +} + +int i2cproc_cleanup(void) +{ + int res; + + if (i2cproc_initialized >= 3) { + if ((res = i2c_del_driver(&i2cproc_driver))) { + printk("i2c-proc.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cproc_initialized--; + } + if (i2cproc_initialized >= 1) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + if ((res = remove_proc_entry("i2c",proc_bus))) { + printk("i2c-proc.o: could not delete /proc/bus/i2c, module not removed."); + return res; + } + i2cproc_initialized -= 2; +#else + if (i2cproc_initialized >= 2) { + if ((res = proc_unregister(&proc_bus_dir,proc_bus_i2c_dir.low_ino))) { + printk("i2c-proc.o: could not delete /proc/bus/i2c, " + "module not removed."); + return res; + } + i2cproc_initialized --; + } + if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { + printk("i2c-proc.o: could not delete /proc/bus/, " + "module not removed."); + return res; + } + i2cproc_initialized --; +#endif + } + return 0; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +#else +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) +#endif +{ + int i; + len = 0; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (i2cproc_adapters[i]) + len += sprintf(buf, "/dev/i2c-%d\t%s\t%-32s\t%-32s\n",i, + i2c_is_smbus_adapter(i2cproc_adapters[i])?"smbus": +#ifdef DEBUG + i2c_is_isa_adapter(i2cproc_adapters[i])?"isa": +#endif + "i2c", + i2cproc_adapters[i]->name, + i2cproc_adapters[i]->algo->name); + return len; +} + +/* We need to add the adapter to i2cproc_adapters, if it is interesting + enough */ +int i2cproc_attach_adapter(struct i2c_adapter *adapter) +{ + struct i2c_client *client; + int i,res; + +#ifndef DEBUG + if (i2c_is_isa_adapter(adapter)) + return 0; +#endif /* ndef DEBUG */ + + for (i = 0; i < I2C_ADAP_MAX; i++) + if(!i2cproc_adapters[i]) + break; + if (i == I2C_ADAP_MAX) { + printk("i2c-proc.o: Too many adapters!\n"); + return -ENOMEM; + } + + if (! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) { + printk("i2c-proc.o: Out of memory!\n"); + return -ENOMEM; + } + memcpy(client,&i2cproc_client_template,sizeof(struct i2c_client)); + client->adapter = adapter; + if ((res = i2c_attach_client(client))) { + printk("i2c-proc.o: Attaching client failed.\n"); + return res; + } + i2cproc_adapters[i] = adapter; + return 0; +} + +int i2cproc_detach_client(struct i2c_client *client) +{ + int i,res; + + printk("OK!\n"); +#ifndef DEBUG + if (i2c_is_isa_client(client)) + return 0; +#endif /* ndef DEBUG */ + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (client->adapter == i2cproc_adapters[i]) { + if ((res = i2c_detach_client(client))) { + printk("i2c-bus.o: Client deregistration failed, " + "client not detached.\n"); + return res; + } + i2cproc_adapters[i] = NULL; + kfree(client); + return 0; + } + return -ENOENT; +} + +/* Nothing here yet */ +int i2cproc_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +/* Nothing here yet */ +void i2cproc_inc_use(struct i2c_client *client) +{ +} + +/* Nothing here yet */ +void i2cproc_dec_use(struct i2c_client *client) +{ +} + + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("I2C /proc/bus entries driver"); + +int init_module(void) +{ + return i2cproc_init(); +} + +int cleanup_module(void) +{ + return i2cproc_cleanup(); +} + +#endif /* MODULE */ + diff --git a/kernel/include/sensors.h b/kernel/include/sensors.h new file mode 100644 index 00000000..3b276aa1 --- /dev/null +++ b/kernel/include/sensors.h @@ -0,0 +1,27 @@ +/* + sensors.h - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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_SENSORS_H +#define SENSORS_SENSORS_H + +/* Driver IDs */ +#define I2C_DRIVERID_LM78 2 +#define I2C_DRIVERID_I2CPROC 1 + +#endif /* def SENSORS_SENSORS_H */ diff --git a/src/Module.mk b/src/Module.mk index 174372c3..454d3bac 100644 --- a/src/Module.mk +++ b/src/Module.mk @@ -23,7 +23,7 @@ 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-bus.o + $(MODULE_DIR)/lm78.o $(MODULE_DIR)/i2c-proc.o # Include all dependency files INCLUDEFILES += $(SRCTARGETS:.o=.d) diff --git a/src/i2c-bus.c b/src/i2c-bus.c deleted file mode 100644 index 8499b151..00000000 --- a/src/i2c-bus.c +++ /dev/null @@ -1,178 +0,0 @@ -/* - i2c-bus.c - A Linux module for reading sensor data. - Copyright (c) 1998 Frodo Looijaard - - 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 -#include - -#include "version.h" -#include "compat.h" - -#ifdef MODULE -extern int init_module(void); -extern int cleanup_module(void); -#endif /* MODULE */o - -static int i2cbus_init(void); -static int i2cbus_cleanup(void); - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) - -static int read_bus_i2c(char *buf, char **start, off_t offset, int len, - int *eof , void *private); - -static struct proc_dir_entry *proc_bus_i2c; - -#else /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ - -static int read_bus_i2c(char *buf, char **start, off_t offset, int len, - int unused); - -static struct proc_dir_entry proc_bus_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "bus", - /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, - /* nlink */ 1, /* Corrected by proc_register[_dynamic] */ - /* uid */ 0, - /* gid */ 0, - /* size */ 0, - /* ops */ &proc_dir_inode_operations, - }; - -static struct proc_dir_entry proc_bus_i2c_dir = - { - /* low_ino */ 0, /* Set by proc_register_dynamic */ - /* namelen */ 3, - /* name */ "i2c", - /* mode */ S_IRUGO | S_IFREG, - /* nlink */ 1, - /* uid */ 0, - /* gid */ 0, - /* size */ 0, - /* ops */ NULL, - /* get_info */ &read_bus_i2c - }; - -#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ - -static int i2cbus_initialized; - -int i2cbus_init(void) -{ - int res; - - printk("i2c-bus.o version %s (%s)\n",LM_VERSION,LM_DATE); - i2cbus_initialized = 0; - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) - if (! proc_bus) { - printk("i2c-bus.o: /proc/bus/ does not exist, module not inserted.\n"); - i2cbus_cleanup(); - return -ENOENT; - } - proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); - if (proc_bus_i2c) - proc_bus_i2c->read_proc = &read_bus_i2c; - else { - printk("i2c-bus.o: Could not create /proc/bus/i2c, module not inserted.\n"); - i2cbus_cleanup(); - return -ENOENT; - } - i2cbus_initialized += 2; -#else - /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module - introduced it, or we are fucked. */ - if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { - printk("i2c-bus.o: Could not create /proc/bus/, module not inserted.\n"); - i2cbus_cleanup(); - return res; - } - i2cbus_initialized ++; - if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { - printk("i2c-bus.o: Could not create /proc/bus/i2c, module not inserted.\n"); - i2cbus_cleanup(); - return res; - } - i2cbus_initialized ++; -#endif - return 0; -} - -int i2cbus_cleanup(void) -{ - int res; - - if (i2cbus_initialized >= 1) { -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) - if ((res = remove_proc_entry("i2c",proc_bus))) { - printk("i2c-bus.o: could not delete /proc/bus/i2c, module not removed."); - return res; - } - i2cbus_initialized -= 2; -#else - if (i2cbus_initialized >= 2) { - if ((res = proc_unregister(&proc_bus_dir,proc_bus_i2c_dir.low_ino))) { - printk("i2c-bus.o: could not delete /proc/bus/i2c, " - "module not removed."); - return res; - } - i2cbus_initialized --; - } - if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { - printk("i2c-bus.o: could not delete /proc/bus/, " - "module not removed."); - return res; - } - i2cbus_initialized --; -#endif - } - return 0; -} - -#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) -int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, - void *private) -#else -int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) -#endif -{ - len = 0; - len += sprintf(buf,"A very nice test, indeed\n"); - return len; -} - - -#ifdef MODULE - -MODULE_AUTHOR("Frodo Looijaard "); -MODULE_DESCRIPTION("I2C /proc/bus entries driver"); - -int init_module(void) -{ - return i2cbus_init(); -} - -int cleanup_module(void) -{ - return i2cbus_cleanup(); -} - -#endif /* MODULE */ - diff --git a/src/i2c-proc.c b/src/i2c-proc.c new file mode 100644 index 00000000..75c29208 --- /dev/null +++ b/src/i2c-proc.c @@ -0,0 +1,318 @@ +/* + i2c-proc.c - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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 +#include + +#include "i2c.h" +#include "smbus.h" +#include "isa.h" +#include "version.h" +#include "compat.h" +#include "sensors.h" + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +static int i2cproc_init(void); +static int i2cproc_cleanup(void); +static int i2cproc_attach_adapter(struct i2c_adapter *adapter); +static int i2cproc_detach_client(struct i2c_client *client); +static int i2cproc_command(struct i2c_client *client, unsigned int cmd, + void *arg); +static void i2cproc_inc_use(struct i2c_client *client); +static void i2cproc_dec_use(struct i2c_client *client); + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int *eof , void *private); + +static struct proc_dir_entry *proc_bus_i2c; + +#else /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ + +static int read_bus_i2c(char *buf, char **start, off_t offset, int len, + int unused); + +static struct proc_dir_entry proc_bus_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "bus", + /* mode */ S_IRUGO | S_IXUGO | S_IFDIR, + /* nlink */ 1, /* Corrected by proc_register[_dynamic] */ + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ &proc_dir_inode_operations, + }; + +static struct proc_dir_entry proc_bus_i2c_dir = + { + /* low_ino */ 0, /* Set by proc_register_dynamic */ + /* namelen */ 3, + /* name */ "i2c", + /* mode */ S_IRUGO | S_IFREG, + /* nlink */ 1, + /* uid */ 0, + /* gid */ 0, + /* size */ 0, + /* ops */ NULL, + /* get_info */ &read_bus_i2c + }; + +#endif /* (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) */ + +/* Used by init/cleanup */ +static int i2cproc_initialized; + +/* This is a sorted list of all adapters that will have entries in /proc/bus */ +static struct i2c_adapter *i2cproc_adapters[I2C_ADAP_MAX]; + +/* We will use a nasty trick: we register a driver, that will be notified + for each adapter. Then, we register a dummy client on the adapter, that + will get notified if the adapter is removed. This is the same trick as + used in i2c/i2c-dev.c */ +static struct i2c_driver i2cproc_driver = { + /* name */ "i2c-proc dummy driver", + /* id */ I2C_DRIVERID_I2CPROC, + /* flags */ DF_NOTIFY, + /* attach_adapter */ &i2cproc_attach_adapter, + /* detach_client */ &i2cproc_detach_client, + /* command */ &i2cproc_command, + /* inc_use */ &i2cproc_inc_use, + /* dec_use */ &i2cproc_dec_use +}; + +static struct i2c_client i2cproc_client_template = { + /* name */ "i2c-proc dummy client", + /* id */ 1, + /* flags */ 0, + /* addr */ -1, + /* adapter */ NULL, + /* driver */ &i2cproc_driver, + /* data */ NULL +}; + +int i2cproc_init(void) +{ + int res; + + printk("i2c-proc.o version %s (%s)\n",LM_VERSION,LM_DATE); + i2cproc_initialized = 0; + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + if (! proc_bus) { + printk("i2c-proc.o: /proc/bus/ does not exist, module not inserted.\n"); + i2cproc_cleanup(); + return -ENOENT; + } + proc_bus_i2c = create_proc_entry("i2c",0,proc_bus); + if (proc_bus_i2c) + proc_bus_i2c->read_proc = &read_bus_i2c; + else { + printk("i2c-proc.o: Could not create /proc/bus/i2c, " + "module not inserted.\n"); + i2cproc_cleanup(); + return -ENOENT; + } + i2cproc_initialized += 2; +#else + /* In Linux 2.0.x, there is no /proc/bus! But I hope no other module + introduced it, or we are fucked. */ + if ((res = proc_register_dynamic(&proc_root, &proc_bus_dir))) { + printk("i2c-proc.o: Could not create /proc/bus/, module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + if ((res = proc_register_dynamic(&proc_bus_dir, &proc_bus_i2c_dir))) { + printk("i2c-proc.o: Could not create /proc/bus/i2c, " + "module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; +#endif + if ((res = i2c_add_driver(&i2cproc_driver))) { + printk("i2c-proc.o: Driver registration failed, module not inserted.\n"); + i2cproc_cleanup(); + return res; + } + i2cproc_initialized ++; + return 0; +} + +int i2cproc_cleanup(void) +{ + int res; + + if (i2cproc_initialized >= 3) { + if ((res = i2c_del_driver(&i2cproc_driver))) { + printk("i2c-proc.o: Driver deregistration failed, " + "module not removed.\n"); + return res; + } + i2cproc_initialized--; + } + if (i2cproc_initialized >= 1) { +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) + if ((res = remove_proc_entry("i2c",proc_bus))) { + printk("i2c-proc.o: could not delete /proc/bus/i2c, module not removed."); + return res; + } + i2cproc_initialized -= 2; +#else + if (i2cproc_initialized >= 2) { + if ((res = proc_unregister(&proc_bus_dir,proc_bus_i2c_dir.low_ino))) { + printk("i2c-proc.o: could not delete /proc/bus/i2c, " + "module not removed."); + return res; + } + i2cproc_initialized --; + } + if ((res = proc_unregister(&proc_root,proc_bus_dir.low_ino))) { + printk("i2c-proc.o: could not delete /proc/bus/, " + "module not removed."); + return res; + } + i2cproc_initialized --; +#endif + } + return 0; +} + +#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,1,0)) +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof, + void *private) +#else +int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused) +#endif +{ + int i; + len = 0; + for (i = 0; i < I2C_ADAP_MAX; i++) + if (i2cproc_adapters[i]) + len += sprintf(buf, "/dev/i2c-%d\t%s\t%-32s\t%-32s\n",i, + i2c_is_smbus_adapter(i2cproc_adapters[i])?"smbus": +#ifdef DEBUG + i2c_is_isa_adapter(i2cproc_adapters[i])?"isa": +#endif + "i2c", + i2cproc_adapters[i]->name, + i2cproc_adapters[i]->algo->name); + return len; +} + +/* We need to add the adapter to i2cproc_adapters, if it is interesting + enough */ +int i2cproc_attach_adapter(struct i2c_adapter *adapter) +{ + struct i2c_client *client; + int i,res; + +#ifndef DEBUG + if (i2c_is_isa_adapter(adapter)) + return 0; +#endif /* ndef DEBUG */ + + for (i = 0; i < I2C_ADAP_MAX; i++) + if(!i2cproc_adapters[i]) + break; + if (i == I2C_ADAP_MAX) { + printk("i2c-proc.o: Too many adapters!\n"); + return -ENOMEM; + } + + if (! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) { + printk("i2c-proc.o: Out of memory!\n"); + return -ENOMEM; + } + memcpy(client,&i2cproc_client_template,sizeof(struct i2c_client)); + client->adapter = adapter; + if ((res = i2c_attach_client(client))) { + printk("i2c-proc.o: Attaching client failed.\n"); + return res; + } + i2cproc_adapters[i] = adapter; + return 0; +} + +int i2cproc_detach_client(struct i2c_client *client) +{ + int i,res; + + printk("OK!\n"); +#ifndef DEBUG + if (i2c_is_isa_client(client)) + return 0; +#endif /* ndef DEBUG */ + + for (i = 0; i < I2C_ADAP_MAX; i++) + if (client->adapter == i2cproc_adapters[i]) { + if ((res = i2c_detach_client(client))) { + printk("i2c-bus.o: Client deregistration failed, " + "client not detached.\n"); + return res; + } + i2cproc_adapters[i] = NULL; + kfree(client); + return 0; + } + return -ENOENT; +} + +/* Nothing here yet */ +int i2cproc_command(struct i2c_client *client, unsigned int cmd, + void *arg) +{ + return -1; +} + +/* Nothing here yet */ +void i2cproc_inc_use(struct i2c_client *client) +{ +} + +/* Nothing here yet */ +void i2cproc_dec_use(struct i2c_client *client) +{ +} + + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("I2C /proc/bus entries driver"); + +int init_module(void) +{ + return i2cproc_init(); +} + +int cleanup_module(void) +{ + return i2cproc_cleanup(); +} + +#endif /* MODULE */ + diff --git a/src/lm78.c b/src/lm78.c new file mode 100644 index 00000000..8a469713 --- /dev/null +++ b/src/lm78.c @@ -0,0 +1,416 @@ +/* + lm78.c - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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 +#include +#include +#include +#include +#include +#include +#include "lm78.h" +#include "smbus.h" +#include "version.h" +#include "isa.h" +#include "sensors.h" + + +#ifdef MODULE +extern int init_module(void); +extern int cleanup_module(void); +#endif /* MODULE */ + +/* There are some complications in a module like this. First off, LM78 chips + may be both present on the SMBus and the ISA bus, and we have to handle + those cases separately at some places. Second, there might be several + LM78 chips available (well, actually, that is probably never done; but + it is a clean illustration of how to handle a case like that). Finally, + a specific chip may be attached to *both* ISA and SMBus, and we would + not like to detect it double. Fortunately, in the case of the LM78 at + least, a register tells us what SMBus address we are on, so that helps + a bit - except if there could be more than one SMBus. Groan. No solution + for this yet. */ + +/* This module may seem overly long and complicated. In fact, it is not so + bad. Quite a lot of bookkeeping is done. A real driver can often cut + some corners. */ + +static int lm78_init(void); +static int lm78_cleanup(void); + +static int lm78_attach_adapter(struct i2c_adapter *adapter); +static int lm78_detect_isa(struct isa_adapter *adapter); +static int lm78_detect_smbus(struct i2c_adapter *adapter); +static int lm78_detach_client(struct i2c_client *client); +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); + +/* 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 */ +#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 */ +static struct i2c_driver lm78_driver = { + /* name */ "LM78 sensor chip driver", + /* id */ I2C_DRIVERID_LM78, + /* flags */ DF_NOTIFY, + /* attach_adapter */ &lm78_attach_adapter, + /* detach_client */ &lm78_detach_client, + /* command */ &lm78_command, + /* inc_use */ &lm78_inc_use, + /* dec_use */ &lm78_dec_use +}; + +/* Used by lm78_init/cleanup */ +static int lm78_initialized = 0; + +/* This function is called when: + * lm78_driver is inserted, for each available adapter + * when a new adapter is inserted (and lm78_driver is still present) */ +int lm78_attach_adapter(struct i2c_adapter *adapter) +{ + if (i2c_is_isa_adapter(adapter)) + return lm78_detect_isa((struct isa_adapter *) adapter); + else + return lm78_detect_smbus(adapter); +} + +/* This function is called whenever a client should be removed */ +int lm78_detach_client(struct i2c_client *client) +{ + if (i2c_is_isa_client(client)) + return lm78_detach_isa((struct isa_client *) client); + else + return lm78_detach_smbus(client); +} + +/* Detect whether there is a LM78 on the ISA bus, register and initialize + it. */ +int lm78_detect_isa(struct isa_adapter *adapter) +{ + int address,err; + struct isa_client *new_client; + + /* OK, this is no detection. I know. It will do for now, though. */ + + err = 0; + for (address = 0x290; (! err) && (address <= 0x290); address += 0x08) { + if (check_region(address, LM78_EXTENT)) + continue; + + if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff) { + outb_p(0x00,address + LM78_ADDR_REG_OFFSET); + if (inb_p(address + LM78_ADDR_REG_OFFSET) == 0xff) + continue; + } + + /* Real detection code goes here */ + + request_region(address, LM78_EXTENT, "lm78"); + + if (! (new_client = kmalloc(sizeof(struct isa_client), GFP_KERNEL))) + { + release_region(address,LM78_EXTENT); + err=-ENOMEM; + continue; + } + 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; + } + lm78_init_client((struct i2c_client *) new_client); + } + return err; +} + +/* Deregister and remove a LM78 client */ +int lm78_detach_isa(struct isa_client *client) +{ + int err; + 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); + return 0; +} + +int lm78_detect_smbus(struct i2c_adapter *adapter) +{ + int address,err; + struct i2c_client *new_client; + + /* OK, this is no detection. I know. It will do for now, though. */ + err = 0; + for (address = 0x20; (! err) && (address <= 0x2f); address ++) { + + /* Later on, we will keep a list of registered addresses for each + adapter, and check whether they are used here */ + + if (smbus_read_byte_data(adapter,address,1) == 0xff) + continue; + + /* Real detection code goes here */ + + new_client = kmalloc(sizeof(struct i2c_client), GFP_KERNEL); + 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; + } + lm78_init_client(new_client); + } + return err; +} + +int lm78_detach_smbus(struct i2c_client *client) +{ + int err; + if ((err = i2c_detach_client(client))) { + printk("lm78.o: Client deregistration failed, client not detached.\n"); + return err; + } + lm78_remove_client(client); + kfree(client); + return 0; +} + + +/* Find a free slot, and initialize most of the fields */ +int lm78_new_client(struct i2c_adapter *adapter, + struct i2c_client *new_client) +{ + int i; + + /* First, seek out an empty slot */ + for(i = 0; i < MAX_LM78_NR; i++) + if (! lm78_list[i]) + break; + if (i == MAX_LM78_NR) { + printk("lm78.o: No empty slots left, recompile and heighten " + "MAX_LM78_NR!\n"); + return -ENOMEM; + } + + 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; + return 0; +} + +void lm78_remove_client(struct i2c_client *client) +{ + int i; + for (i = 0; i < MAX_LM78_NR; 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) +{ + return 0; +} + +/* Nothing here yet */ +void lm78_inc_use (struct i2c_client *client) +{ +} + +/* Nothing here yet */ +void lm78_dec_use (struct i2c_client *client) +{ +} + + +/* The SMBus locks itself, but ISA access must be locked explicitely! + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_read_value(struct i2c_client *client, u8 reg) +{ + int res; + if (i2c_is_isa_client(client)) { + down((struct semaphore *) (client->data)); + outb_p(reg,(((struct isa_client *) client)->isa_addr) + + LM78_ADDR_REG_OFFSET); + res = inb_p((((struct isa_client *) client)->isa_addr) + + LM78_DATA_REG_OFFSET); + up((struct semaphore *) (client->data)); + return res; + } else + return smbus_read_byte_data(client->adapter,client->addr, reg); +} + +/* The SMBus locks itself, but ISA access muse be locked explicitely! + We ignore the LM78 BUSY flag at this moment - it could lead to deadlocks, + would slow down the LM78 access and should not be necessary. + There are some ugly typecasts here, but the good new is - they should + nowhere else be necessary! */ +int lm78_write_value(struct i2c_client *client, u8 reg, u8 value) +{ + if (i2c_is_isa_client(client)) { + down((struct semaphore *) (client->data)); + outb_p(reg,((struct isa_client *) client)->isa_addr + LM78_ADDR_REG_OFFSET); + outb_p(value,((struct isa_client *) client)->isa_addr + LM78_DATA_REG_OFFSET); + up((struct semaphore *) (client->data)); + return 0; + } else + 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) +{ + 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; +} + +/* 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 + }; + + +int lm78_init(void) +{ + int res; + + 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(); + return res; + } + lm78_initialized ++; + return 0; +} + +int lm78_cleanup(void) +{ + int res; + + if (lm78_initialized >= 2) { + if ((res = i2c_del_driver(&lm78_driver))) { + printk("lm78.o: Driver deregistration failed, module not removed.\n"); + return res; + } + } 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; +} + + +#ifdef MODULE + +MODULE_AUTHOR("Frodo Looijaard "); +MODULE_DESCRIPTION("LM78 driver"); + +int init_module(void) +{ + return lm78_init(); +} + +int cleanup_module(void) +{ + return lm78_cleanup(); +} + +#endif /* MODULE */ + diff --git a/src/lm78.h b/src/lm78.h new file mode 100644 index 00000000..47a3ee39 --- /dev/null +++ b/src/lm78.h @@ -0,0 +1,30 @@ +/* + lm78.h - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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 */ diff --git a/src/sensors.h b/src/sensors.h new file mode 100644 index 00000000..3b276aa1 --- /dev/null +++ b/src/sensors.h @@ -0,0 +1,27 @@ +/* + sensors.h - A Linux module for reading sensor data. + Copyright (c) 1998 Frodo Looijaard + + 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_SENSORS_H +#define SENSORS_SENSORS_H + +/* Driver IDs */ +#define I2C_DRIVERID_LM78 2 +#define I2C_DRIVERID_I2CPROC 1 + +#endif /* def SENSORS_SENSORS_H */