diff options
-rw-r--r-- | Documentation/i2c/i2c-stub | 18 | ||||
-rw-r--r-- | drivers/i2c/busses/i2c-stub.c | 79 |
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 | |||
6 | types of SMBus commands: write quick, (r/w) byte, (r/w) byte data, and | 6 | types 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 | ||
9 | You need to provide a chip address as a module parameter when loading | 9 | You need to provide chip addresses as a module parameter when loading this |
10 | this driver, which will then only react to SMBus commands to this address. | 10 | driver, which will then only react to SMBus commands to these addresses. |
11 | 11 | ||
12 | No hardware is needed nor associated with this module. It will accept write | 12 | No hardware is needed nor associated with this module. It will accept write |
13 | quick commands to one address; it will respond to the other commands (also | 13 | quick commands to the specified addresses; it will respond to the other |
14 | to one address) by reading from or writing to an array in memory. It will | 14 | commands (also to the specified addresses) by reading from or writing to |
15 | also spam the kernel logs for every command it handles. | 15 | arrays in memory. It will also spam the kernel logs for every command it |
16 | handles. | ||
16 | 17 | ||
17 | A pointer register with auto-increment is implemented for all byte | 18 | A pointer register with auto-increment is implemented for all byte |
18 | operations. This allows for continuous byte reads like those supported by | 19 | operations. This allows for continuous byte reads like those supported by |
@@ -26,8 +27,8 @@ The typical use-case is like this: | |||
26 | 27 | ||
27 | PARAMETERS: | 28 | PARAMETERS: |
28 | 29 | ||
29 | int chip_addr: | 30 | int chip_addr[10]: |
30 | The SMBus address to emulate a chip at. | 31 | The SMBus addresses to emulate chips at. |
31 | 32 | ||
32 | CAVEATS: | 33 | CAVEATS: |
33 | 34 | ||
@@ -41,9 +42,6 @@ If the hardware for your driver has banked registers (e.g. Winbond sensors | |||
41 | chips) this module will not work well - although it could be extended to | 42 | chips) this module will not work well - although it could be extended to |
42 | support that pretty easily. | 43 | support that pretty easily. |
43 | 44 | ||
44 | Only one chip address is supported - although this module could be | ||
45 | extended to support more. | ||
46 | |||
47 | If you spam it hard enough, printk can be lossy. This module really wants | 45 | If you spam it hard enough, printk can be lossy. This module really wants |
48 | something like relayfs. | 46 | something 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 | ||
30 | static unsigned short chip_addr; | 31 | #define MAX_CHIPS 10 |
31 | module_param(chip_addr, ushort, S_IRUGO); | ||
32 | MODULE_PARM_DESC(chip_addr, "Chip address (between 0x03 and 0x77)\n"); | ||
33 | 32 | ||
34 | static u8 stub_pointer; | 33 | static unsigned short chip_addr[MAX_CHIPS]; |
35 | static u8 stub_bytes[256]; | 34 | module_param_array(chip_addr, ushort, NULL, S_IRUGO); |
36 | static u16 stub_words[256]; | 35 | MODULE_PARM_DESC(chip_addr, |
36 | "Chip addresses (up to 10, between 0x03 and 0x77)\n"); | ||
37 | |||
38 | struct stub_chip { | ||
39 | u8 pointer; | ||
40 | u8 bytes[256]; | ||
41 | u16 words[256]; | ||
42 | }; | ||
43 | |||
44 | static struct stub_chip *stub_chips; | ||
37 | 45 | ||
38 | /* Return -1 on error. */ | 46 | /* Return -1 on error. */ |
39 | static s32 stub_xfer(struct i2c_adapter * adap, u16 addr, unsigned short flags, | 47 | static 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 | ||
130 | static int __init i2c_stub_init(void) | 147 | static 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 | ||
146 | static void __exit i2c_stub_exit(void) | 180 | static 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 | ||
151 | MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); | 186 | MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); |