aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2007-10-13 17:56:31 -0400
committerJean Delvare <khali@hyperion.delvare>2007-10-13 17:56:31 -0400
commit9d90c1fd9bdbffb456d1b1ef05215343503fd0b0 (patch)
tree7569f3402bdbb23c000ac59ad81522d3944f53f8
parent567a244b8b431ee8399bb43c7e65df605bdd5458 (diff)
i2c-stub: Support multiple chips
Add support for multiple chips to i2c-stub. I've changed the memory allocation scheme from static to dynamic, so that we don't waste too much memory. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Mark M. Hoffman <mhoffman@lightlink.com>
-rw-r--r--Documentation/i2c/i2c-stub18
-rw-r--r--drivers/i2c/busses/i2c-stub.c79
2 files changed, 65 insertions, 32 deletions
diff --git a/Documentation/i2c/i2c-stub b/Documentation/i2c/i2c-stub
index 9cc081e69764..89e69ad3436c 100644
--- a/Documentation/i2c/i2c-stub
+++ b/Documentation/i2c/i2c-stub
@@ -6,13 +6,14 @@ This module is a very simple fake I2C/SMBus driver. It implements four
6types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, and 6types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, and
7(r/w) word data. 7(r/w) word data.
8 8
9You need to provide a chip address as a module parameter when loading 9You need to provide chip addresses as a module parameter when loading this
10this driver, which will then only react to SMBus commands to this address. 10driver, which will then only react to SMBus commands to these addresses.
11 11
12No hardware is needed nor associated with this module. It will accept write 12No hardware is needed nor associated with this module. It will accept write
13quick commands to one address; it will respond to the other commands (also 13quick commands to the specified addresses; it will respond to the other
14to one address) by reading from or writing to an array in memory. It will 14commands (also to the specified addresses) by reading from or writing to
15also spam the kernel logs for every command it handles. 15arrays in memory. It will also spam the kernel logs for every command it
16handles.
16 17
17A pointer register with auto-increment is implemented for all byte 18A pointer register with auto-increment is implemented for all byte
18operations. This allows for continuous byte reads like those supported by 19operations. This allows for continuous byte reads like those supported by
@@ -26,8 +27,8 @@ The typical use-case is like this:
26 27
27PARAMETERS: 28PARAMETERS:
28 29
29int chip_addr: 30int chip_addr[10]:
30 The SMBus address to emulate a chip at. 31 The SMBus addresses to emulate chips at.
31 32
32CAVEATS: 33CAVEATS:
33 34
@@ -41,9 +42,6 @@ If the hardware for your driver has banked registers (e.g. Winbond sensors
41chips) this module will not work well - although it could be extended to 42chips) this module will not work well - although it could be extended to
42support that pretty easily. 43support that pretty easily.
43 44
44Only one chip address is supported - although this module could be
45extended to support more.
46
47If you spam it hard enough, printk can be lossy. This module really wants 45If you spam it hard enough, printk can be lossy. This module really wants
48something like relayfs. 46something like relayfs.
49 47
diff --git a/drivers/i2c/busses/i2c-stub.c b/drivers/i2c/busses/i2c-stub.c
index a54adc50d162..84df29da1ddc 100644
--- a/drivers/i2c/busses/i2c-stub.c
+++ b/drivers/i2c/busses/i2c-stub.c
@@ -24,24 +24,41 @@
24#include <linux/init.h> 24#include <linux/init.h>
25#include <linux/module.h> 25#include <linux/module.h>
26#include <linux/kernel.h> 26#include <linux/kernel.h>
27#include <linux/slab.h>
27#include <linux/errno.h> 28#include <linux/errno.h>
28#include <linux/i2c.h> 29#include <linux/i2c.h>
29 30
30static unsigned short chip_addr; 31#define MAX_CHIPS 10
31module_param(chip_addr, ushort, S_IRUGO);
32MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n");
33 32
34static u8 stub_pointer; 33static unsigned short chip_addr[MAX_CHIPS];
35static u8 stub_bytes[256]; 34module_param_array(chip_addr, ushort, NULL, S_IRUGO);
36static u16 stub_words[256]; 35MODULE_PARM_DESC(chip_addr,
36 "Chip addresses (up to 10, between 0x03 and 0x77)\n");
37
38struct stub_chip {
39 u8 pointer;
40 u8 bytes[256];
41 u16 words[256];
42};
43
44static struct stub_chip *stub_chips;
37 45
38/* Return -1 on error. */ 46/* Return -1 on error. */
39static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, 47static 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) 48 char read_write, u8 command, int size, union i2c_smbus_data * data)
41{ 49{
42 s32 ret; 50 s32 ret;
43 51 int i;
44 if (addr != chip_addr) 52 struct stub_chip *chip = NULL;
53
54 /* Search for the right chip */
55 for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
56 if (addr == chip_addr[i]) {
57 chip = stub_chips + i;
58 break;
59 }
60 }
61 if (!chip)
45 return -ENODEV; 62 return -ENODEV;
46 63
47 switch (size) { 64 switch (size) {
@@ -53,12 +70,12 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
53 70
54 case I2C_SMBUS_BYTE: 71 case I2C_SMBUS_BYTE:
55 if (read_write == I2C_SMBUS_WRITE) { 72 if (read_write == I2C_SMBUS_WRITE) {
56 stub_pointer = command; 73 chip->pointer = command;
57 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " 74 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, "
58 "wrote 0x%02x.\n", 75 "wrote 0x%02x.\n",
59 addr, command); 76 addr, command);
60 } else { 77 } else {
61 data->byte = stub_bytes[stub_pointer++]; 78 data->byte = chip->bytes[chip->pointer++];
62 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " 79 dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, "
63 "read 0x%02x.\n", 80 "read 0x%02x.\n",
64 addr, data->byte); 81 addr, data->byte);
@@ -69,29 +86,29 @@ static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags,
69 86
70 case I2C_SMBUS_BYTE_DATA: 87 case I2C_SMBUS_BYTE_DATA:
71 if (read_write == I2C_SMBUS_WRITE) { 88 if (read_write == I2C_SMBUS_WRITE) {
72 stub_bytes[command] = data->byte; 89 chip->bytes[command] = data->byte;
73 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " 90 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
74 "wrote 0x%02x at 0x%02x.\n", 91 "wrote 0x%02x at 0x%02x.\n",
75 addr, data->byte, command); 92 addr, data->byte, command);
76 } else { 93 } else {
77 data->byte = stub_bytes[command]; 94 data->byte = chip->bytes[command];
78 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " 95 dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, "
79 "read 0x%02x at 0x%02x.\n", 96 "read 0x%02x at 0x%02x.\n",
80 addr, data->byte, command); 97 addr, data->byte, command);
81 } 98 }
82 stub_pointer = command + 1; 99 chip->pointer = command + 1;
83 100
84 ret = 0; 101 ret = 0;
85 break; 102 break;
86 103
87 case I2C_SMBUS_WORD_DATA: 104 case I2C_SMBUS_WORD_DATA:
88 if (read_write == I2C_SMBUS_WRITE) { 105 if (read_write == I2C_SMBUS_WRITE) {
89 stub_words[command] = data->word; 106 chip->words[command] = data->word;
90 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " 107 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
91 "wrote 0x%04x at 0x%02x.\n", 108 "wrote 0x%04x at 0x%02x.\n",
92 addr, data->word, command); 109 addr, data->word, command);
93 } else { 110 } else {
94 data->word = stub_words[command]; 111 data->word = chip->words[command];
95 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " 112 dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, "
96 "read 0x%04x at 0x%02x.\n", 113 "read 0x%04x at 0x%02x.\n",
97 addr, data->word, command); 114 addr, data->word, command);
@@ -129,23 +146,41 @@ static struct i2c_adapter stub_adapter = {
129 146
130static int __init i2c_stub_init(void) 147static int __init i2c_stub_init(void)
131{ 148{
132 if (!chip_addr) { 149 int i, ret;
150
151 if (!chip_addr[0]) {
133 printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); 152 printk(KERN_ERR "i2c-stub: Please specify a chip address\n");
134 return -ENODEV; 153 return -ENODEV;
135 } 154 }
136 if (chip_addr < 0x03 || chip_addr > 0x77) { 155
137 printk(KERN_ERR "i2c-stub: Invalid chip address 0x%02x\n", 156 for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
138 chip_addr); 157 if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) {
139 return -EINVAL; 158 printk(KERN_ERR "i2c-stub: Invalid chip address "
159 "0x%02x\n", chip_addr[i]);
160 return -EINVAL;
161 }
162
163 printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n",
164 chip_addr[i]);
140 } 165 }
141 166
142 printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", chip_addr); 167 /* Allocate memory for all chips at once */
143 return i2c_add_adapter(&stub_adapter); 168 stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL);
169 if (!stub_chips) {
170 printk(KERN_ERR "i2c-stub: Out of memory\n");
171 return -ENOMEM;
172 }
173
174 ret = i2c_add_adapter(&stub_adapter);
175 if (ret)
176 kfree(stub_chips);
177 return ret;
144} 178}
145 179
146static void __exit i2c_stub_exit(void) 180static void __exit i2c_stub_exit(void)
147{ 181{
148 i2c_del_adapter(&stub_adapter); 182 i2c_del_adapter(&stub_adapter);
183 kfree(stub_chips);
149} 184}
150 185
151MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); 186MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");