Commit 569be443e3c1329fc6725988004f5d8a32fe3be5

Authored by Jean Delvare
Committed by Jean Delvare
1 parent b3af547e19

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