diff options
Diffstat (limited to 'drivers/i2c/busses/i2c-stub.c')
-rw-r--r-- | drivers/i2c/busses/i2c-stub.c | 79 |
1 files changed, 57 insertions, 22 deletions
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>"); |