mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-10-15 10:39:31 +00:00
to. git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@96 7894878c-1315-0410-8ee3-d5d059ff63e0
479 lines
14 KiB
C
479 lines
14 KiB
C
/*
|
|
i2c-proc.c - Part of lm_sensors, Linux kernel modules for hardware
|
|
monitoring
|
|
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/proc_fs.h>
|
|
|
|
#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 /* def 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,58))
|
|
static void monitor_bus_i2c(struct inode *inode, int fill);
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
|
|
static ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
|
|
loff_t *ppos);
|
|
static int read_bus_i2c(char *buf, char **start, off_t offset, int len,
|
|
int *eof , void *private);
|
|
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
|
|
static int i2cproc_bus_read(struct inode * inode, struct file * file,
|
|
char * buf, int count);
|
|
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 */ 2, /* 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
|
|
};
|
|
|
|
/* List of registered entries in /proc/bus */
|
|
static struct proc_dir_entry *i2cproc_proc_entries[I2C_ADAP_MAX];
|
|
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
|
|
/* To implement the dynamic /proc/bus/i2c-? files, we need our own
|
|
implementation of the read hook */
|
|
static struct file_operations i2cproc_operations = {
|
|
NULL,
|
|
i2cproc_bus_read,
|
|
};
|
|
|
|
static struct inode_operations i2cproc_inode_operations = {
|
|
&i2cproc_operations
|
|
};
|
|
|
|
|
|
/* 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];
|
|
|
|
/* Inodes of /dev/bus/i2c-? files */
|
|
static int i2cproc_inodes[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;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
struct proc_dir_entry *proc_bus_i2c;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
|
|
printk("i2c-proc.o version %s (%s)\n",LM_VERSION,LM_DATE);
|
|
i2cproc_initialized = 0;
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
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) {
|
|
printk("i2c-proc.o: Could not create /proc/bus/i2c, "
|
|
"module not inserted.\n");
|
|
i2cproc_cleanup();
|
|
return -ENOENT;
|
|
}
|
|
proc_bus_i2c->read_proc = &read_bus_i2c;
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
|
proc_bus_i2c->fill_inode = &monitor_bus_i2c;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
i2cproc_initialized += 2;
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
/* 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 /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
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,29))
|
|
remove_proc_entry("i2c",proc_bus);
|
|
i2cproc_initialized -= 2;
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
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 /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
|
/* Monitor access to /proc/bus/i2c*; make unloading i2c-proc.o impossible
|
|
if some process still uses it or some file in it */
|
|
void monitor_bus_i2c(struct inode *inode, int fill)
|
|
{
|
|
if (fill)
|
|
MOD_INC_USE_COUNT;
|
|
else
|
|
MOD_DEC_USE_COUNT;
|
|
}
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
|
|
|
|
|
/* This function generates the output for /proc/bus/i2c */
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
int read_bus_i2c(char *buf, char **start, off_t offset, int len, int *eof,
|
|
void *private)
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
int read_bus_i2c(char *buf, char **start, off_t offset, int len, int unused)
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
{
|
|
int i;
|
|
len = 0;
|
|
for (i = 0; i < I2C_ADAP_MAX; i++)
|
|
if (i2cproc_adapters[i])
|
|
len += sprintf(buf+len, "i2c-%d\t%s\t%-32s\t%-32s\n",
|
|
i2c_adapter_id(i2cproc_adapters[i]),
|
|
i2c_is_smbus_adapter(i2cproc_adapters[i])?"smbus":
|
|
#ifdef DEBUG
|
|
i2c_is_isa_adapter(i2cproc_adapters[i])?"isa":
|
|
#endif /* def DEBUG */
|
|
"i2c",
|
|
i2cproc_adapters[i]->name,
|
|
i2cproc_adapters[i]->algo->name);
|
|
return len;
|
|
}
|
|
|
|
/* This function generates the output for /proc/bus/i2c-? */
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
ssize_t i2cproc_bus_read(struct file * file, char * buf,size_t count,
|
|
loff_t *ppos)
|
|
{
|
|
struct inode * inode = file->f_dentry->d_inode;
|
|
#else (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29))
|
|
int i2cproc_bus_read(struct inode * inode, struct file * file,char * buf,
|
|
int count)
|
|
{
|
|
#endif (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
char *kbuf;
|
|
struct i2c_client *client;
|
|
int i,j,len=0;
|
|
|
|
if (count < 0)
|
|
return -EINVAL;
|
|
if (count > 4000)
|
|
count = 4000;
|
|
for (i = 0; i < I2C_ADAP_MAX; i++)
|
|
if (i2cproc_inodes[i] == inode->i_ino) {
|
|
if (! (kbuf = kmalloc(count,GFP_KERNEL)))
|
|
return -ENOMEM;
|
|
for (j = 0; j < I2C_CLIENT_MAX; j++)
|
|
if ((client = i2cproc_adapters[i]->clients[j]))
|
|
/* Filter out dummy clients */
|
|
#ifndef DEBUG
|
|
if (client->driver->id != I2C_DRIVERID_I2CPROC)
|
|
#endif /* ndef DEBUG */
|
|
len += sprintf(kbuf+len,"%x\t%-32s\t%-32s\n",
|
|
#ifdef DEBUG
|
|
i2c_is_isa_client(client)?
|
|
((struct isa_client *) client)->isa_addr&0xffffff:
|
|
#endif /* def DEBUG */
|
|
client->addr,
|
|
client->name,client->driver->name);
|
|
if (file->f_pos+len > count)
|
|
len = count - file->f_pos;
|
|
len = len - file->f_pos;
|
|
if (len < 0)
|
|
len = 0;
|
|
copy_to_user (buf,kbuf+file->f_pos,len);
|
|
file->f_pos += len;
|
|
kfree(kbuf);
|
|
return len;
|
|
}
|
|
return -ENOENT;
|
|
}
|
|
|
|
|
|
/* 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;
|
|
char name[8];
|
|
|
|
struct proc_dir_entry *proc_entry;
|
|
|
|
#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;
|
|
}
|
|
|
|
#ifndef DEBUG
|
|
if (! (client = kmalloc(sizeof(struct i2c_client),GFP_KERNEL))) {
|
|
#else /* def DEBUG */
|
|
if (! (client = kmalloc(sizeof(struct isa_client),GFP_KERNEL))) {
|
|
#endif
|
|
printk("i2c-proc.o: Out of memory!\n");
|
|
return -ENOMEM;
|
|
}
|
|
memcpy(client,&i2cproc_client_template,sizeof(struct i2c_client));
|
|
#ifdef DEBUG
|
|
((struct isa_client *) client) -> isa_addr = -1;
|
|
#endif /* def DEBUG */
|
|
client->adapter = adapter;
|
|
if ((res = i2c_attach_client(client))) {
|
|
printk("i2c-proc.o: Attaching client failed.\n");
|
|
kfree(client);
|
|
return res;
|
|
}
|
|
i2cproc_adapters[i] = adapter;
|
|
|
|
sprintf(name,"i2c-%d",i2c_adapter_id(adapter));
|
|
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
proc_entry = create_proc_entry(name,0,proc_bus);
|
|
if (! proc_entry) {
|
|
printk("i2c-proc.o: Could not create /proc/bus/%s\n",name);
|
|
kfree(client);
|
|
return -ENOENT;
|
|
}
|
|
proc_entry->ops = &i2cproc_inode_operations;
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58))
|
|
proc_entry->fill_inode = &monitor_bus_i2c;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,58)) */
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
if (!(proc_entry = kmalloc(sizeof(struct proc_dir_entry)+strlen(name)+1,
|
|
GFP_KERNEL))) {
|
|
printk("i2c-proc.o: Out of memory!\n");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
memset(proc_entry,0,sizeof(struct proc_dir_entry));
|
|
proc_entry->namelen = strlen(name);
|
|
proc_entry->name = (char *) (proc_entry + 1);
|
|
proc_entry->mode = S_IRUGO | S_IFREG;
|
|
proc_entry->nlink = 1;
|
|
proc_entry->ops = &i2cproc_inode_operations;
|
|
strcpy((char *) proc_entry->name,name);
|
|
|
|
if ((res = proc_register_dynamic(&proc_bus_dir, proc_entry))) {
|
|
printk("i2c-proc.o: Could not create %s.\n",name);
|
|
kfree(proc_entry);
|
|
kfree(client);
|
|
return res;
|
|
}
|
|
|
|
i2cproc_proc_entries[i] = proc_entry;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
|
|
i2cproc_inodes[i] = proc_entry->low_ino;
|
|
return 0;
|
|
}
|
|
|
|
int i2cproc_detach_client(struct i2c_client *client)
|
|
{
|
|
int i,res;
|
|
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
char name[8];
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
|
|
#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 (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29))
|
|
sprintf(name,"i2c-%d",i2c_adapter_id(i2cproc_adapters[i]));
|
|
remove_proc_entry(name,proc_bus);
|
|
#else /* (LINUX_VERSION_CODE < KERNEL_VERSION(2,1,29)) */
|
|
if ((res = proc_unregister(&proc_bus_dir,
|
|
i2cproc_proc_entries[i]->low_ino))) {
|
|
printk("i2c-proc.o: Deregistration of /proc entry failed, "
|
|
"client not detached.\n");
|
|
return res;
|
|
}
|
|
kfree(i2cproc_proc_entries[i]);
|
|
i2cproc_proc_entries[i] = NULL;
|
|
#endif /* (LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,29)) */
|
|
if ((res = i2c_detach_client(client))) {
|
|
printk("i2c-proc.o: Client deregistration failed, "
|
|
"client not detached.\n");
|
|
return res;
|
|
}
|
|
i2cproc_adapters[i] = NULL;
|
|
i2cproc_inodes[i] = 0;
|
|
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 <frodol@dds.nl>");
|
|
MODULE_DESCRIPTION("I2C /proc/bus entries driver");
|
|
|
|
int init_module(void)
|
|
{
|
|
return i2cproc_init();
|
|
}
|
|
|
|
int cleanup_module(void)
|
|
{
|
|
return i2cproc_cleanup();
|
|
}
|
|
|
|
#endif /* def MODULE */
|
|
|