Commit 569be443e3c1329fc6725988004f5d8a32fe3be5
Committed by
Jean Delvare
1 parent
b3af547e19
Exists in
master
and in
7 other branches
i2c-stub: Use a single array for byte and word operations
This mimics the behavior of actual SMBus chips better. Signed-off-by: Jean Delvare <khali@linux-fr.org> Cc: Mark M. Hoffman <mhoffman@lightlink.com>
Showing 2 changed files with 8 additions and 10 deletions Inline Diff
Documentation/i2c/i2c-stub
1 | MODULE: i2c-stub | 1 | MODULE: i2c-stub |
2 | 2 | ||
3 | DESCRIPTION: | 3 | DESCRIPTION: |
4 | 4 | ||
5 | This module is a very simple fake I2C/SMBus driver. It implements four | 5 | 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 chip addresses as a module parameter when loading this | 9 | You need to provide chip addresses as a module parameter when loading this |
10 | driver, which will then only react to SMBus commands to these addresses. | 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 the specified addresses; it will respond to the other | 13 | quick commands to the specified addresses; it will respond to the other |
14 | commands (also to the specified addresses) by reading from or writing to | 14 | commands (also to the specified addresses) by reading from or writing to |
15 | arrays in memory. It will also spam the kernel logs for every command it | 15 | arrays in memory. It will also spam the kernel logs for every command it |
16 | handles. | 16 | handles. |
17 | 17 | ||
18 | A pointer register with auto-increment is implemented for all byte | 18 | A pointer register with auto-increment is implemented for all byte |
19 | operations. This allows for continuous byte reads like those supported by | 19 | operations. This allows for continuous byte reads like those supported by |
20 | EEPROMs, among others. | 20 | EEPROMs, among others. |
21 | 21 | ||
22 | The typical use-case is like this: | 22 | The typical use-case is like this: |
23 | 1. load this module | 23 | 1. load this module |
24 | 2. use i2cset (from lm_sensors project) to pre-load some data | 24 | 2. use i2cset (from lm_sensors project) to pre-load some data |
25 | 3. load the target sensors chip driver module | 25 | 3. load the target sensors chip driver module |
26 | 4. observe its behavior in the kernel log | 26 | 4. observe its behavior in the kernel log |
27 | 27 | ||
28 | There's a script named i2c-stub-from-dump in the i2c-tools package which | 28 | There's a script named i2c-stub-from-dump in the i2c-tools package which |
29 | can load register values automatically from a chip dump. | 29 | can load register values automatically from a chip dump. |
30 | 30 | ||
31 | PARAMETERS: | 31 | PARAMETERS: |
32 | 32 | ||
33 | int chip_addr[10]: | 33 | int chip_addr[10]: |
34 | The SMBus addresses to emulate chips at. | 34 | The SMBus addresses to emulate chips at. |
35 | 35 | ||
36 | CAVEATS: | 36 | CAVEATS: |
37 | 37 | ||
38 | There are independent arrays for byte/data and word/data commands. Depending | ||
39 | on if/how a target driver mixes them, you'll need to be careful. | ||
40 | |||
41 | If your target driver polls some byte or word waiting for it to change, the | 38 | If your target driver polls some byte or word waiting for it to change, the |
42 | stub could lock it up. Use i2cset to unlock it. | 39 | stub could lock it up. Use i2cset to unlock it. |
43 | 40 | ||
44 | If the hardware for your driver has banked registers (e.g. Winbond sensors | 41 | If the hardware for your driver has banked registers (e.g. Winbond sensors |
45 | 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 |
46 | support that pretty easily. | 43 | support that pretty easily. |
47 | 44 | ||
48 | 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 |
49 | something like relayfs. | 46 | something like relayfs. |
50 | 47 | ||
51 | 48 |
drivers/i2c/busses/i2c-stub.c
1 | /* | 1 | /* |
2 | i2c-stub.c - Part of lm_sensors, Linux kernel modules for hardware | 2 | i2c-stub.c - I2C/SMBus chip emulator |
3 | monitoring | ||
4 | 3 | ||
5 | Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> | 4 | Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com> |
5 | Copyright (C) 2007 Jean Delvare <khali@linux-fr.org> | ||
6 | 6 | ||
7 | This program is free software; you can redistribute it and/or modify | 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 | 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 | 9 | the Free Software Foundation; either version 2 of the License, or |
10 | (at your option) any later version. | 10 | (at your option) any later version. |
11 | 11 | ||
12 | This program is distributed in the hope that it will be useful, | 12 | This program is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | GNU General Public License for more details. | 15 | GNU General Public License for more details. |
16 | 16 | ||
17 | You should have received a copy of the GNU General Public License | 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 | 18 | along with this program; if not, write to the Free Software |
19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. | 19 | Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. |
20 | */ | 20 | */ |
21 | 21 | ||
22 | #define DEBUG 1 | 22 | #define DEBUG 1 |
23 | 23 | ||
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/slab.h> |
28 | #include <linux/errno.h> | 28 | #include <linux/errno.h> |
29 | #include <linux/i2c.h> | 29 | #include <linux/i2c.h> |
30 | 30 | ||
31 | #define MAX_CHIPS 10 | 31 | #define MAX_CHIPS 10 |
32 | 32 | ||
33 | static unsigned short chip_addr[MAX_CHIPS]; | 33 | static unsigned short chip_addr[MAX_CHIPS]; |
34 | module_param_array(chip_addr, ushort, NULL, S_IRUGO); | 34 | module_param_array(chip_addr, ushort, NULL, S_IRUGO); |
35 | MODULE_PARM_DESC(chip_addr, | 35 | MODULE_PARM_DESC(chip_addr, |
36 | "Chip addresses (up to 10, between 0x03 and 0x77)\n"); | 36 | "Chip addresses (up to 10, between 0x03 and 0x77)\n"); |
37 | 37 | ||
38 | struct stub_chip { | 38 | struct stub_chip { |
39 | u8 pointer; | 39 | u8 pointer; |
40 | u8 bytes[256]; | 40 | u16 words[256]; /* Byte operations use the LSB as per SMBus |
41 | u16 words[256]; | 41 | specification */ |
42 | }; | 42 | }; |
43 | 43 | ||
44 | static struct stub_chip *stub_chips; | 44 | static struct stub_chip *stub_chips; |
45 | 45 | ||
46 | /* Return -1 on error. */ | 46 | /* Return -1 on error. */ |
47 | 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, |
48 | 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) |
49 | { | 49 | { |
50 | s32 ret; | 50 | s32 ret; |
51 | int i; | 51 | int i; |
52 | struct stub_chip *chip = NULL; | 52 | struct stub_chip *chip = NULL; |
53 | 53 | ||
54 | /* Search for the right chip */ | 54 | /* Search for the right chip */ |
55 | for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { | 55 | for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { |
56 | if (addr == chip_addr[i]) { | 56 | if (addr == chip_addr[i]) { |
57 | chip = stub_chips + i; | 57 | chip = stub_chips + i; |
58 | break; | 58 | break; |
59 | } | 59 | } |
60 | } | 60 | } |
61 | if (!chip) | 61 | if (!chip) |
62 | return -ENODEV; | 62 | return -ENODEV; |
63 | 63 | ||
64 | switch (size) { | 64 | switch (size) { |
65 | 65 | ||
66 | case I2C_SMBUS_QUICK: | 66 | case I2C_SMBUS_QUICK: |
67 | dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); | 67 | dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr); |
68 | ret = 0; | 68 | ret = 0; |
69 | break; | 69 | break; |
70 | 70 | ||
71 | case I2C_SMBUS_BYTE: | 71 | case I2C_SMBUS_BYTE: |
72 | if (read_write == I2C_SMBUS_WRITE) { | 72 | if (read_write == I2C_SMBUS_WRITE) { |
73 | chip->pointer = command; | 73 | chip->pointer = command; |
74 | dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " | 74 | dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " |
75 | "wrote 0x%02x.\n", | 75 | "wrote 0x%02x.\n", |
76 | addr, command); | 76 | addr, command); |
77 | } else { | 77 | } else { |
78 | data->byte = chip->bytes[chip->pointer++]; | 78 | data->byte = chip->words[chip->pointer++] & 0xff; |
79 | dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " | 79 | dev_dbg(&adap->dev, "smbus byte - addr 0x%02x, " |
80 | "read 0x%02x.\n", | 80 | "read 0x%02x.\n", |
81 | addr, data->byte); | 81 | addr, data->byte); |
82 | } | 82 | } |
83 | 83 | ||
84 | ret = 0; | 84 | ret = 0; |
85 | break; | 85 | break; |
86 | 86 | ||
87 | case I2C_SMBUS_BYTE_DATA: | 87 | case I2C_SMBUS_BYTE_DATA: |
88 | if (read_write == I2C_SMBUS_WRITE) { | 88 | if (read_write == I2C_SMBUS_WRITE) { |
89 | chip->bytes[command] = data->byte; | 89 | chip->words[command] &= 0xff00; |
90 | chip->words[command] |= data->byte; | ||
90 | dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " | 91 | dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " |
91 | "wrote 0x%02x at 0x%02x.\n", | 92 | "wrote 0x%02x at 0x%02x.\n", |
92 | addr, data->byte, command); | 93 | addr, data->byte, command); |
93 | } else { | 94 | } else { |
94 | data->byte = chip->bytes[command]; | 95 | data->byte = chip->words[command] & 0xff; |
95 | dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " | 96 | dev_dbg(&adap->dev, "smbus byte data - addr 0x%02x, " |
96 | "read 0x%02x at 0x%02x.\n", | 97 | "read 0x%02x at 0x%02x.\n", |
97 | addr, data->byte, command); | 98 | addr, data->byte, command); |
98 | } | 99 | } |
99 | chip->pointer = command + 1; | 100 | chip->pointer = command + 1; |
100 | 101 | ||
101 | ret = 0; | 102 | ret = 0; |
102 | break; | 103 | break; |
103 | 104 | ||
104 | case I2C_SMBUS_WORD_DATA: | 105 | case I2C_SMBUS_WORD_DATA: |
105 | if (read_write == I2C_SMBUS_WRITE) { | 106 | if (read_write == I2C_SMBUS_WRITE) { |
106 | chip->words[command] = data->word; | 107 | chip->words[command] = data->word; |
107 | dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " | 108 | dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " |
108 | "wrote 0x%04x at 0x%02x.\n", | 109 | "wrote 0x%04x at 0x%02x.\n", |
109 | addr, data->word, command); | 110 | addr, data->word, command); |
110 | } else { | 111 | } else { |
111 | data->word = chip->words[command]; | 112 | data->word = chip->words[command]; |
112 | dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " | 113 | dev_dbg(&adap->dev, "smbus word data - addr 0x%02x, " |
113 | "read 0x%04x at 0x%02x.\n", | 114 | "read 0x%04x at 0x%02x.\n", |
114 | addr, data->word, command); | 115 | addr, data->word, command); |
115 | } | 116 | } |
116 | 117 | ||
117 | ret = 0; | 118 | ret = 0; |
118 | break; | 119 | break; |
119 | 120 | ||
120 | default: | 121 | default: |
121 | dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); | 122 | dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n"); |
122 | ret = -1; | 123 | ret = -1; |
123 | break; | 124 | break; |
124 | } /* switch (size) */ | 125 | } /* switch (size) */ |
125 | 126 | ||
126 | return ret; | 127 | return ret; |
127 | } | 128 | } |
128 | 129 | ||
129 | static u32 stub_func(struct i2c_adapter *adapter) | 130 | static u32 stub_func(struct i2c_adapter *adapter) |
130 | { | 131 | { |
131 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | | 132 | return I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | |
132 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; | 133 | I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA; |
133 | } | 134 | } |
134 | 135 | ||
135 | static const struct i2c_algorithm smbus_algorithm = { | 136 | static const struct i2c_algorithm smbus_algorithm = { |
136 | .functionality = stub_func, | 137 | .functionality = stub_func, |
137 | .smbus_xfer = stub_xfer, | 138 | .smbus_xfer = stub_xfer, |
138 | }; | 139 | }; |
139 | 140 | ||
140 | static struct i2c_adapter stub_adapter = { | 141 | static struct i2c_adapter stub_adapter = { |
141 | .owner = THIS_MODULE, | 142 | .owner = THIS_MODULE, |
142 | .class = I2C_CLASS_HWMON, | 143 | .class = I2C_CLASS_HWMON, |
143 | .algo = &smbus_algorithm, | 144 | .algo = &smbus_algorithm, |
144 | .name = "SMBus stub driver", | 145 | .name = "SMBus stub driver", |
145 | }; | 146 | }; |
146 | 147 | ||
147 | static int __init i2c_stub_init(void) | 148 | static int __init i2c_stub_init(void) |
148 | { | 149 | { |
149 | int i, ret; | 150 | int i, ret; |
150 | 151 | ||
151 | if (!chip_addr[0]) { | 152 | if (!chip_addr[0]) { |
152 | printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); | 153 | printk(KERN_ERR "i2c-stub: Please specify a chip address\n"); |
153 | return -ENODEV; | 154 | return -ENODEV; |
154 | } | 155 | } |
155 | 156 | ||
156 | for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { | 157 | for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) { |
157 | if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { | 158 | if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) { |
158 | printk(KERN_ERR "i2c-stub: Invalid chip address " | 159 | printk(KERN_ERR "i2c-stub: Invalid chip address " |
159 | "0x%02x\n", chip_addr[i]); | 160 | "0x%02x\n", chip_addr[i]); |
160 | return -EINVAL; | 161 | return -EINVAL; |
161 | } | 162 | } |
162 | 163 | ||
163 | printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", | 164 | printk(KERN_INFO "i2c-stub: Virtual chip at 0x%02x\n", |
164 | chip_addr[i]); | 165 | chip_addr[i]); |
165 | } | 166 | } |
166 | 167 | ||
167 | /* Allocate memory for all chips at once */ | 168 | /* Allocate memory for all chips at once */ |
168 | stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL); | 169 | stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL); |
169 | if (!stub_chips) { | 170 | if (!stub_chips) { |
170 | printk(KERN_ERR "i2c-stub: Out of memory\n"); | 171 | printk(KERN_ERR "i2c-stub: Out of memory\n"); |
171 | return -ENOMEM; | 172 | return -ENOMEM; |
172 | } | 173 | } |
173 | 174 | ||
174 | ret = i2c_add_adapter(&stub_adapter); | 175 | ret = i2c_add_adapter(&stub_adapter); |
175 | if (ret) | 176 | if (ret) |
176 | kfree(stub_chips); | 177 | kfree(stub_chips); |
177 | return ret; | 178 | return ret; |
178 | } | 179 | } |
179 | 180 | ||
180 | static void __exit i2c_stub_exit(void) | 181 | static void __exit i2c_stub_exit(void) |
181 | { | 182 | { |
182 | i2c_del_adapter(&stub_adapter); | 183 | i2c_del_adapter(&stub_adapter); |
183 | kfree(stub_chips); | 184 | kfree(stub_chips); |
184 | } | 185 | } |
185 | 186 | ||
186 | MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); | 187 | MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>"); |
187 | MODULE_DESCRIPTION("I2C stub driver"); | 188 | MODULE_DESCRIPTION("I2C stub driver"); |
188 | MODULE_LICENSE("GPL"); | 189 | MODULE_LICENSE("GPL"); |
189 | 190 | ||
190 | module_init(i2c_stub_init); | 191 | module_init(i2c_stub_init); |
191 | module_exit(i2c_stub_exit); | 192 | module_exit(i2c_stub_exit); |
192 | 193 |