linux/drivers/i2c/i2c-stub.c
<<
>>
Prefs
   1/*
   2    i2c-stub.c - I2C/SMBus chip emulator
   3
   4    Copyright (c) 2004 Mark M. Hoffman <mhoffman@lightlink.com>
   5    Copyright (C) 2007, 2012 Jean Delvare <jdelvare@suse.de>
   6
   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
   9    the Free Software Foundation; either version 2 of the License, or
  10    (at your option) any later version.
  11
  12    This program is distributed in the hope that it will be useful,
  13    but WITHOUT ANY WARRANTY; without even the implied warranty of
  14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  15    GNU General Public License for more details.
  16
  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
  19    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  20*/
  21
  22#define DEBUG 1
  23
  24#include <linux/init.h>
  25#include <linux/module.h>
  26#include <linux/kernel.h>
  27#include <linux/slab.h>
  28#include <linux/errno.h>
  29#include <linux/i2c.h>
  30
  31#define MAX_CHIPS 10
  32#define STUB_FUNC (I2C_FUNC_SMBUS_QUICK | I2C_FUNC_SMBUS_BYTE | \
  33                   I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_WORD_DATA | \
  34                   I2C_FUNC_SMBUS_I2C_BLOCK)
  35
  36static unsigned short chip_addr[MAX_CHIPS];
  37module_param_array(chip_addr, ushort, NULL, S_IRUGO);
  38MODULE_PARM_DESC(chip_addr,
  39                 "Chip addresses (up to 10, between 0x03 and 0x77)");
  40
  41static unsigned long functionality = STUB_FUNC;
  42module_param(functionality, ulong, S_IRUGO | S_IWUSR);
  43MODULE_PARM_DESC(functionality, "Override functionality bitfield");
  44
  45struct stub_chip {
  46        u8 pointer;
  47        u16 words[256];         /* Byte operations use the LSB as per SMBus
  48                                   specification */
  49};
  50
  51static struct stub_chip *stub_chips;
  52
  53/* Return negative errno on error. */
  54static s32 stub_xfer(struct i2c_adapter *adap, u16 addr, unsigned short flags,
  55        char read_write, u8 command, int size, union i2c_smbus_data *data)
  56{
  57        s32 ret;
  58        int i, len;
  59        struct stub_chip *chip = NULL;
  60
  61        /* Search for the right chip */
  62        for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
  63                if (addr == chip_addr[i]) {
  64                        chip = stub_chips + i;
  65                        break;
  66                }
  67        }
  68        if (!chip)
  69                return -ENODEV;
  70
  71        switch (size) {
  72
  73        case I2C_SMBUS_QUICK:
  74                dev_dbg(&adap->dev, "smbus quick - addr 0x%02x\n", addr);
  75                ret = 0;
  76                break;
  77
  78        case I2C_SMBUS_BYTE:
  79                if (read_write == I2C_SMBUS_WRITE) {
  80                        chip->pointer = command;
  81                        dev_dbg(&adap->dev,
  82                                "smbus byte - addr 0x%02x, wrote 0x%02x.\n",
  83                                addr, command);
  84                } else {
  85                        data->byte = chip->words[chip->pointer++] & 0xff;
  86                        dev_dbg(&adap->dev,
  87                                "smbus byte - addr 0x%02x, read  0x%02x.\n",
  88                                addr, data->byte);
  89                }
  90
  91                ret = 0;
  92                break;
  93
  94        case I2C_SMBUS_BYTE_DATA:
  95                if (read_write == I2C_SMBUS_WRITE) {
  96                        chip->words[command] &= 0xff00;
  97                        chip->words[command] |= data->byte;
  98                        dev_dbg(&adap->dev,
  99                                "smbus byte data - addr 0x%02x, wrote 0x%02x at 0x%02x.\n",
 100                                addr, data->byte, command);
 101                } else {
 102                        data->byte = chip->words[command] & 0xff;
 103                        dev_dbg(&adap->dev,
 104                                "smbus byte data - addr 0x%02x, read  0x%02x at 0x%02x.\n",
 105                                addr, data->byte, command);
 106                }
 107                chip->pointer = command + 1;
 108
 109                ret = 0;
 110                break;
 111
 112        case I2C_SMBUS_WORD_DATA:
 113                if (read_write == I2C_SMBUS_WRITE) {
 114                        chip->words[command] = data->word;
 115                        dev_dbg(&adap->dev,
 116                                "smbus word data - addr 0x%02x, wrote 0x%04x at 0x%02x.\n",
 117                                addr, data->word, command);
 118                } else {
 119                        data->word = chip->words[command];
 120                        dev_dbg(&adap->dev,
 121                                "smbus word data - addr 0x%02x, read  0x%04x at 0x%02x.\n",
 122                                addr, data->word, command);
 123                }
 124
 125                ret = 0;
 126                break;
 127
 128        case I2C_SMBUS_I2C_BLOCK_DATA:
 129                len = data->block[0];
 130                if (read_write == I2C_SMBUS_WRITE) {
 131                        for (i = 0; i < len; i++) {
 132                                chip->words[command + i] &= 0xff00;
 133                                chip->words[command + i] |= data->block[1 + i];
 134                        }
 135                        dev_dbg(&adap->dev,
 136                                "i2c block data - addr 0x%02x, wrote %d bytes at 0x%02x.\n",
 137                                addr, len, command);
 138                } else {
 139                        for (i = 0; i < len; i++) {
 140                                data->block[1 + i] =
 141                                        chip->words[command + i] & 0xff;
 142                        }
 143                        dev_dbg(&adap->dev,
 144                                "i2c block data - addr 0x%02x, read  %d bytes at 0x%02x.\n",
 145                                addr, len, command);
 146                }
 147
 148                ret = 0;
 149                break;
 150
 151        default:
 152                dev_dbg(&adap->dev, "Unsupported I2C/SMBus command\n");
 153                ret = -EOPNOTSUPP;
 154                break;
 155        } /* switch (size) */
 156
 157        return ret;
 158}
 159
 160static u32 stub_func(struct i2c_adapter *adapter)
 161{
 162        return STUB_FUNC & functionality;
 163}
 164
 165static const struct i2c_algorithm smbus_algorithm = {
 166        .functionality  = stub_func,
 167        .smbus_xfer     = stub_xfer,
 168};
 169
 170static struct i2c_adapter stub_adapter = {
 171        .owner          = THIS_MODULE,
 172        .class          = I2C_CLASS_HWMON | I2C_CLASS_SPD,
 173        .algo           = &smbus_algorithm,
 174        .name           = "SMBus stub driver",
 175};
 176
 177static int __init i2c_stub_init(void)
 178{
 179        int i, ret;
 180
 181        if (!chip_addr[0]) {
 182                pr_err("i2c-stub: Please specify a chip address\n");
 183                return -ENODEV;
 184        }
 185
 186        for (i = 0; i < MAX_CHIPS && chip_addr[i]; i++) {
 187                if (chip_addr[i] < 0x03 || chip_addr[i] > 0x77) {
 188                        pr_err("i2c-stub: Invalid chip address 0x%02x\n",
 189                               chip_addr[i]);
 190                        return -EINVAL;
 191                }
 192
 193                pr_info("i2c-stub: Virtual chip at 0x%02x\n", chip_addr[i]);
 194        }
 195
 196        /* Allocate memory for all chips at once */
 197        stub_chips = kzalloc(i * sizeof(struct stub_chip), GFP_KERNEL);
 198        if (!stub_chips) {
 199                pr_err("i2c-stub: Out of memory\n");
 200                return -ENOMEM;
 201        }
 202
 203        ret = i2c_add_adapter(&stub_adapter);
 204        if (ret)
 205                kfree(stub_chips);
 206        return ret;
 207}
 208
 209static void __exit i2c_stub_exit(void)
 210{
 211        i2c_del_adapter(&stub_adapter);
 212        kfree(stub_chips);
 213}
 214
 215MODULE_AUTHOR("Mark M. Hoffman <mhoffman@lightlink.com>");
 216MODULE_DESCRIPTION("I2C stub driver");
 217MODULE_LICENSE("GPL");
 218
 219module_init(i2c_stub_init);
 220module_exit(i2c_stub_exit);
 221