mirror of
https://github.com/lm-sensors/lm-sensors.git
synced 2026-10-25 12:14:33 +00:00
start of a demonstration ARP client.
git-svn-id: http://lm-sensors.org/svn/lm-sensors/trunk@1392 7894878c-1315-0410-8ee3-d5d059ff63e0
This commit is contained in:
1 parent
bf992c322b
commit
e2a6466a4d
1 file changed
+467
@@ -0,0 +1,467 @@
|
||||
/*
|
||||
smbus-arp.c - Part of lm_sensors, Linux kernel modules for hardware
|
||||
monitoring
|
||||
Copyright (c) 2002 Mark D. Studebaker <mdsxyz123@yahoo.com>
|
||||
|
||||
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/version.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/i2c.h>
|
||||
#include "sensors.h"
|
||||
#include "version.h"
|
||||
#include <linux/init.h>
|
||||
|
||||
#define DEBUG 1
|
||||
|
||||
/* So that the module will still compile without i2c-2.6.4;
|
||||
if inserted, it will just put out a message and exit in that case */
|
||||
#ifdef I2C_CLIENT_PEC
|
||||
#define I2C_PEC_SUPPORTED
|
||||
#else
|
||||
#define I2C_DRIVERID_ARP 902 /* SMBus ARP Client */
|
||||
#endif
|
||||
|
||||
#ifndef THIS_MODULE
|
||||
#define THIS_MODULE NULL
|
||||
#endif
|
||||
|
||||
/* Addresses to scan */
|
||||
#define ARP_ADDRESS 0x61
|
||||
static unsigned short normal_i2c[] = { ARP_ADDRESS, SENSORS_I2C_END };
|
||||
static unsigned short normal_i2c_range[] = { SENSORS_I2C_END };
|
||||
static unsigned int normal_isa[] = { SENSORS_ISA_END };
|
||||
static unsigned int normal_isa_range[] = { SENSORS_ISA_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
SENSORS_INSMOD_1(arp);
|
||||
|
||||
/* ARP Commands */
|
||||
#define ARP_PREPARE 0x01
|
||||
#define ARP_RESET_DEV 0x02
|
||||
#define ARP_GET_UDID_GEN 0x03
|
||||
#define ARP_ASSIGN_ADDR 0x04
|
||||
|
||||
/* UDID Fields */
|
||||
#define ARP_CAPAB 0
|
||||
#define ARP_VER 1
|
||||
#define ARP_VEND 2
|
||||
#define ARP_DEV 4
|
||||
#define ARP_INT 6
|
||||
#define ARP_SUBVEND 8
|
||||
#define ARP_SUBDEV 10
|
||||
#define ARP_SPECID 12
|
||||
|
||||
#define UDID_LENGTH 0x11
|
||||
|
||||
static u8 reserved[] =
|
||||
/* As defined by SMBus Spec. Appendix C */
|
||||
{0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x28,
|
||||
0x2c, 0x2d, 0x37, 0x40, 0x41, 0x42,
|
||||
0x43, 0x44, ARP_ADDRESS,
|
||||
/* As defined by SMBus Spec. Sect. 5.2 */
|
||||
0x01, 0x02, 0x03, 0x04, 0x05,
|
||||
0x06, 0x07, 0x78, 0x79, 0x7a, 0x7b,
|
||||
0x7c, 0x7d, 0x7e, 0x7f,
|
||||
/* Common PC addresses (bad idea) */
|
||||
0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* eeproms */
|
||||
0x69, /* clock chips */
|
||||
/* Must end in 0 which is also reserved */
|
||||
0x00};
|
||||
|
||||
#define SMBUS_ADDRESS_SIZE 0x80
|
||||
#define ARP_FREE 0
|
||||
#define ARP_RESERVED 1
|
||||
#define ARP_BUSY 2
|
||||
|
||||
#define ARP_MAX_DEVICES 8
|
||||
struct arp_device {
|
||||
int status;
|
||||
u8 udid[16];
|
||||
u8 saddr;
|
||||
};
|
||||
|
||||
/* Each client has this additional data */
|
||||
struct arp_data {
|
||||
int sysctl_id;
|
||||
|
||||
struct semaphore update_lock;
|
||||
char valid; /* !=0 if following fields are valid */
|
||||
unsigned long last_updated; /* In jiffies */
|
||||
|
||||
u8 address_pool[SMBUS_ADDRESS_SIZE];
|
||||
struct arp_device dev[ARP_MAX_DEVICES];
|
||||
};
|
||||
|
||||
#ifdef MODULE
|
||||
extern int init_module(void);
|
||||
extern int cleanup_module(void);
|
||||
#endif /* MODULE */
|
||||
|
||||
#ifdef MODULE
|
||||
static
|
||||
#else
|
||||
extern
|
||||
#endif
|
||||
int __init sensors_smbusarp_init(void);
|
||||
static int __init smbusarp_cleanup(void);
|
||||
|
||||
static int smbusarp_attach_adapter(struct i2c_adapter *adapter);
|
||||
static int smbusarp_detect(struct i2c_adapter *adapter, int address,
|
||||
unsigned short flags, int kind);
|
||||
static int smbusarp_detach_client(struct i2c_client *client);
|
||||
static int smbusarp_command(struct i2c_client *client, unsigned int cmd,
|
||||
void *arg);
|
||||
|
||||
static void smbusarp_inc_use(struct i2c_client *client);
|
||||
static void smbusarp_dec_use(struct i2c_client *client);
|
||||
static int smbusarp_init_client(struct i2c_client *client);
|
||||
static void smbusarp_contents(struct i2c_client *client, int operation,
|
||||
int ctl_name, int *nrels_mag, long *results);
|
||||
static void smbusarp_update_client(struct i2c_client *client);
|
||||
|
||||
|
||||
/* This is the driver that will be inserted */
|
||||
static struct i2c_driver smbusarp_driver = {
|
||||
/* name */ "SMBUS ARP",
|
||||
/* id */ I2C_DRIVERID_ARP,
|
||||
/* flags */ I2C_DF_NOTIFY,
|
||||
/* attach_adapter */ &smbusarp_attach_adapter,
|
||||
/* detach_client */ &smbusarp_detach_client,
|
||||
/* command */ &smbusarp_command,
|
||||
/* inc_use */ &smbusarp_inc_use,
|
||||
/* dec_use */ &smbusarp_dec_use
|
||||
};
|
||||
|
||||
/* These files are created for each bus */
|
||||
static ctl_table smbusarp_dir_table_template[] = {
|
||||
{0}
|
||||
};
|
||||
|
||||
/* Used by init/cleanup */
|
||||
static int __initdata smbusarp_initialized = 0;
|
||||
|
||||
static int smbusarp_id = 0;
|
||||
|
||||
int smbusarp_attach_adapter(struct i2c_adapter *adapter)
|
||||
{
|
||||
return i2c_detect(adapter, &addr_data, smbusarp_detect);
|
||||
}
|
||||
|
||||
/* This function is called by i2c_detect */
|
||||
int smbusarp_detect(struct i2c_adapter *adapter, int address,
|
||||
unsigned short flags, int kind)
|
||||
{
|
||||
int i;
|
||||
struct i2c_client *new_client;
|
||||
struct arp_data *data;
|
||||
int err = 0;
|
||||
const char *type_name, *client_name;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BLOCK_DATA))
|
||||
return(0);
|
||||
|
||||
if (!(new_client = kmalloc(sizeof(struct i2c_client) +
|
||||
sizeof(struct arp_data),
|
||||
GFP_KERNEL))) {
|
||||
return(-ENOMEM);
|
||||
}
|
||||
|
||||
data = (struct arp_data *) (new_client + 1);
|
||||
new_client->addr = address;
|
||||
new_client->data = data;
|
||||
new_client->adapter = adapter;
|
||||
new_client->driver = &smbusarp_driver;
|
||||
#ifdef I2C_PEC_SUPPORTED
|
||||
new_client->flags = I2C_CLIENT_PEC;
|
||||
#else
|
||||
new_client->flags = 0;
|
||||
#endif
|
||||
|
||||
type_name = "arp";
|
||||
client_name = "SMBUS ARP client";
|
||||
|
||||
/* Fill in the remaining client fields and put it into the global list */
|
||||
strcpy(new_client->name, client_name);
|
||||
|
||||
new_client->id = smbusarp_id++;
|
||||
data->valid = 0;
|
||||
init_MUTEX(&data->update_lock);
|
||||
|
||||
/* Tell the I2C layer a new client has arrived */
|
||||
if ((err = i2c_attach_client(new_client)))
|
||||
goto ERROR1;
|
||||
|
||||
/* Register a new directory entry with module sensors */
|
||||
if ((i = i2c_register_entry(new_client, type_name,
|
||||
smbusarp_dir_table_template,
|
||||
THIS_MODULE)) < 0) {
|
||||
err = i;
|
||||
goto ERROR4;
|
||||
}
|
||||
data->sysctl_id = i;
|
||||
smbusarp_init_client(new_client);
|
||||
return 0;
|
||||
|
||||
ERROR4:
|
||||
i2c_detach_client(new_client);
|
||||
ERROR1:
|
||||
kfree(new_client);
|
||||
return err;
|
||||
}
|
||||
|
||||
int smbusarp_detach_client(struct i2c_client *client)
|
||||
{
|
||||
int err;
|
||||
|
||||
i2c_deregister_entry(((struct arp_data *) (client->data))->
|
||||
sysctl_id);
|
||||
|
||||
if ((err = i2c_detach_client(client))) {
|
||||
printk
|
||||
(KERN_ERR "smbus-arp.o: Client deregistration failed, client not detached.\n");
|
||||
return err;
|
||||
}
|
||||
|
||||
kfree(client);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int smbusarp_command(struct i2c_client *client, unsigned int cmd, void *arg)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void smbusarp_inc_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_INC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
void smbusarp_dec_use(struct i2c_client *client)
|
||||
{
|
||||
#ifdef MODULE
|
||||
MOD_DEC_USE_COUNT;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
u8 choose_addr(u8 * pool)
|
||||
{
|
||||
int i;
|
||||
|
||||
for(i = 0; i < 0x7f; i++) {
|
||||
if(pool[i] == ARP_FREE)
|
||||
return ((u8) i);
|
||||
}
|
||||
return 0xff;
|
||||
}
|
||||
|
||||
int smbusarp_init_client(struct i2c_client *client)
|
||||
{
|
||||
int ret = -1;
|
||||
#ifdef I2C_PEC_SUPPORTED
|
||||
struct arp_data *data = client->data;
|
||||
u8 blk[I2C_SMBUS_BLOCK_MAX];
|
||||
u8 *r;
|
||||
u8 addr;
|
||||
int i;
|
||||
int found = 0;
|
||||
int newdev = 0;
|
||||
|
||||
for(i = 0; i < SMBUS_ADDRESS_SIZE; i++)
|
||||
data->address_pool[i] = ARP_FREE;
|
||||
|
||||
r = reserved;
|
||||
do {
|
||||
data->address_pool[*r] = ARP_RESERVED;
|
||||
} while(*r++);
|
||||
|
||||
for (i = 0; i < I2C_CLIENT_MAX ; i++)
|
||||
if (client->adapter->clients[i])
|
||||
data->address_pool[client->adapter->clients[i]->addr] = ARP_BUSY;
|
||||
|
||||
ret = i2c_smbus_write_byte(client, ARP_PREPARE);
|
||||
if(ret < 0) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "smbus-arp.o: No ARP response on adapter 0x%X\n", client->adapter->id);
|
||||
#endif
|
||||
return(-1); /* Packet wasn't acked */
|
||||
}
|
||||
while(1) {
|
||||
ret = i2c_smbus_read_block_data(client, ARP_GET_UDID_GEN, blk);
|
||||
if(ret != UDID_LENGTH) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "smbus-arp.o: No/Bad UDID response %d on adapter 0x%X\n", ret, client->adapter->id);
|
||||
#endif
|
||||
if(found)
|
||||
return(found);
|
||||
else
|
||||
return(-1); /* Bad response */
|
||||
}
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG "smbus-arp.o: Good UDID response on adapter 0x%X\n", client->adapter->id);
|
||||
printk(KERN_DEBUG " Cap. 0x%02x Rev. 0x%02x Vend. 0x%02x%02x Dev. 0x%02x%02x\n", blk[0], blk[1], blk[2], blk[3], blk[4], blk[5]);
|
||||
printk(KERN_DEBUG " Int. 0x%02x%02x Subvend. 0x%02x%02x Subdev. 0x%02x%02x Spec. 0x%02x%02x%02x%02x\n", blk[6], blk[7], blk[8], blk[9], blk[10], blk[11], blk[12], blk[13], blk[14], blk[15]);
|
||||
#endif
|
||||
/* clean up this... */
|
||||
found++;
|
||||
do{
|
||||
if(data->dev[newdev].status == ARP_FREE)
|
||||
break;
|
||||
} while(++newdev < ARP_MAX_DEVICES);
|
||||
if(newdev == ARP_MAX_DEVICES) {
|
||||
printk(KERN_WARNING "smbus-arp.o: No more slots available\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
/* check device slave addr */
|
||||
addr = blk[17];
|
||||
if(addr != 0xFF) {
|
||||
addr >>= 1;
|
||||
if(blk[0] & 0xC0) {
|
||||
if(data->address_pool[addr] == ARP_FREE) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " Free Non-fixed Address 0x%02x\n", addr);
|
||||
#endif
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " Taken Non-fixed Address 0x%02x\n", addr);
|
||||
#endif
|
||||
if((addr = choose_addr(data->address_pool)) == 0xff) {
|
||||
printk(KERN_WARNING "smbus-arp.o: Address pool exhausted\n");
|
||||
return(-1);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " Fixed Address 0x%02x\n", addr);
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " No Address\n");
|
||||
#endif
|
||||
if((addr = choose_addr(data->address_pool)) == 0xff) {
|
||||
printk(KERN_WARNING "smbus-arp.o: Address pool exhausted\n");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
}
|
||||
for(i = 0; i < 16; i++)
|
||||
data->dev[newdev].saddr = addr;
|
||||
data->dev[newdev].udid[i] = blk[i];
|
||||
|
||||
blk[16] = addr << 1;
|
||||
ret = i2c_smbus_write_block_data(client, ARP_ASSIGN_ADDR,
|
||||
UDID_LENGTH, blk);
|
||||
if(ret) {
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " Bad response, address 0x%02x not assigned\n", addr);
|
||||
#endif
|
||||
} else {
|
||||
data->address_pool[addr] = ARP_BUSY;
|
||||
#ifdef DEBUG
|
||||
printk(KERN_DEBUG " Assigned address 0x%02x\n", addr);
|
||||
#endif
|
||||
}
|
||||
/* retry? */
|
||||
|
||||
} /* while 1 */
|
||||
#else
|
||||
printk(KERN_WARNING "smbus-arp.o: A client responded to the ARP address but "
|
||||
"your kernel does not support SMBus ARP/PEC!\n");
|
||||
ret = -EPERM;
|
||||
#endif
|
||||
return(ret);
|
||||
}
|
||||
|
||||
void smbusarp_update_client(struct i2c_client *client)
|
||||
{
|
||||
struct arp_data *data = client->data;
|
||||
|
||||
down(&data->update_lock);
|
||||
|
||||
if ((jiffies - data->last_updated > 300 * HZ) |
|
||||
(jiffies < data->last_updated) || !data->valid) {
|
||||
|
||||
|
||||
data->last_updated = jiffies;
|
||||
data->valid = 1;
|
||||
}
|
||||
|
||||
up(&data->update_lock);
|
||||
}
|
||||
|
||||
int __init sensors_smbusarp_init(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
printk("smbus-arp.o version %s (%s)\n", LM_VERSION, LM_DATE);
|
||||
/* magic force invocation */
|
||||
force_arp[0] = -1;
|
||||
force_arp[1] = ARP_ADDRESS;
|
||||
smbusarp_initialized = 0;
|
||||
if ((res = i2c_add_driver(&smbusarp_driver))) {
|
||||
printk
|
||||
(KERN_ERR "smbus-arp.o: Driver registration failed, module not inserted.\n");
|
||||
smbusarp_cleanup();
|
||||
return res;
|
||||
}
|
||||
smbusarp_initialized++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int __init smbusarp_cleanup(void)
|
||||
{
|
||||
int res;
|
||||
|
||||
if (smbusarp_initialized >= 1) {
|
||||
if ((res = i2c_del_driver(&smbusarp_driver))) {
|
||||
printk
|
||||
(KERN_ERR "smbus-arp.o: Driver deregistration failed, module not removed.\n");
|
||||
return res;
|
||||
}
|
||||
} else
|
||||
smbusarp_initialized--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
EXPORT_NO_SYMBOLS;
|
||||
|
||||
#ifdef MODULE
|
||||
MODULE_AUTHOR("Mark D. Studebaker <mdsxyz123@yahoo.com>");
|
||||
MODULE_DESCRIPTION("SMBUS ARP Driver");
|
||||
#ifdef MODULE_LICENSE
|
||||
MODULE_LICENSE("GPL");
|
||||
#endif
|
||||
|
||||
int init_module(void)
|
||||
{
|
||||
return sensors_smbusarp_init();
|
||||
}
|
||||
|
||||
int cleanup_module(void)
|
||||
{
|
||||
return smbusarp_cleanup();
|
||||
}
|
||||
|
||||
#endif /* MODULE */
|
||||
Reference in new issue
Block a user