1/* 2 i2c-stub.c - Part of lm_sensors, Linux kernel modules for hardware 3 monitoring 4 5 Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> 6 7 This program is free software; you can redistribute it and/or modify 8 it under the terms of the GNU General Public License as published by 9 the Free Software Foundation; either version 2 of the License, or 10 (at your option) any later version. 11 12 This program is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 GNU General Public License for more details. 16 17 You should have received a copy of the GNU General Public License 18 along with this program; if not, write to the Free Software 19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 20*/ 21 22#define DEBUG 1 23 24#include <linux/init.h> 25#include <linux/module.h> 26#include <linux/kernel.h> 27#include <linux/errno.h> 28#include <linux/i2c.h> 29 30static unsigned short chip_addr; 31module_param(chip_addr, ushort, S_IRUGO); 32MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n"); 33 34static u8 stub_pointer; 35static u8 stub_bytes[256]; 36static u16 stub_words[256]; 37 38/* Return -1 on error. */ 39static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, 40 char read_write, u8 command, int size, union i2c_smbus_data * data) 41{ 42 s32 ret; 43 44 if (addr != chip_addr) 45 return -ENODEV; 46 47 switch (size) { 48 49 case I2C_SMBUS_QUICK: 50 dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); 51 ret = 0; 52 break; 53 54 case I2C_SMBUS_BYTE: 55 if (read_write == I2C_SMBUS_WRITE) { 56 stub_pointer = command; 57 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " 58 "wrote 0x%02x.\n", 59 addr, command); 60 } else { 61 data->byte = stub_bytes[stub_pointer++]; 62 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " 63 "read 0x%02x.\n", 64 addr, data->byte); 65 } 66 67 ret = 0; 68 break; 69 70 case I2C_SMBUS_BYTE_DATA: 71 if (read_write == I2C_SMBUS_WRITE) { 72 stub_bytes[command] = data->byte; 73 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " 74 "wrote 0x%02x at 0x%02x.\n", 75 addr, data->byte, command); 76 } else { 77 data->byte = stub_bytes[command]; 78 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " 79 "read 0x%02x at 0x%02x.\n", 80 addr, data->byte, command); 81 } 82 stub_pointer = command + 1; 83 84 ret = 0; 85 break; 86 87 case I2C_SMBUS_WORD_DATA: 88 if (read_write == I2C_SMBUS_WRITE) { 89 stub_words[command] = data->word; 90 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " 91 "wrote 0x%04x at 0x%02x.\n", 92 addr, data->word, command); 93 } else { 94 data->word = stub_words[command]; 95 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " 96 "read 0x%04x at 0x%02x.\n", 97 addr, data->word, command); 98 } 99 100 ret = 0; 101 break; 102 103 default: 104 dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); 105 ret = -1; 106 break; 107 } /* switch (size) */ 108 109 return ret; 110} 111 112static u32 stub_func(struct i2c_adapter *adapter) 113{ 114 return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | 115 I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; 116} 117 118static const struct i2c_algorithm smbus_algorithm = { 119 .functionality = stub_func, 120 .smbus_xfer = stub_xfer, 121}; 122 123static struct i2c_adapter stub_adapter = { 124 .owner = THIS_MODULE, 125 .class = I2C_CLASS_HWMON, 126 .algo = &smbus_algorithm, 127 .name = "SMBus stub driver", 128}; 129 130static int __init i2c_stub_init(void) 131{ 132 if (!chip_addr) { 133 printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); 134 return -ENODEV; 135 } 136 if (chip_addr < 0x03 || chip_addr > 0x77) { 137 printk(KERN_ERR "i2c-stub: Invalid chip address 0x%02x\n", 138 chip_addr); 139 return -EINVAL; 140 } 141 142 printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", chip_addr); 143 return i2c_add_adapter(&stub_adapter); 144} 145 146static void __exit i2c_stub_exit(void) 147{ 148 i2c_del_adapter(&stub_adapter); 149} 150 151MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 152MODULE_DESCRIPTION("I2C stub driver"); 153MODULE_LICENSE("GPL"); 154 155module_init(i2c_stub_init); 156module_exit(i2c_stub_exit); 157