qemu/hw/i2c/smbus_slave.c
<<
>>
Prefs
   1/*
   2 * QEMU SMBus device emulation.
   3 *
   4 * This code is a helper for SMBus device emulation.  It implements an
   5 * I2C device inteface and runs the SMBus protocol from the device
   6 * point of view and maps those to simple calls to emulate.
   7 *
   8 * Copyright (c) 2007 CodeSourcery.
   9 * Written by Paul Brook
  10 *
  11 * This code is licensed under the LGPL.
  12 */
  13
  14/* TODO: Implement PEC.  */
  15
  16#include "qemu/osdep.h"
  17#include "hw/i2c/i2c.h"
  18#include "hw/i2c/smbus_slave.h"
  19#include "migration/vmstate.h"
  20#include "qemu/module.h"
  21
  22//#define DEBUG_SMBUS 1
  23
  24#ifdef DEBUG_SMBUS
  25#define DPRINTF(fmt, ...) \
  26do { printf("smbus(%02x): " fmt , dev->i2c.address, ## __VA_ARGS__); } while (0)
  27#define BADF(fmt, ...) \
  28do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__); exit(1);} while (0)
  29#else
  30#define DPRINTF(fmt, ...) do {} while(0)
  31#define BADF(fmt, ...) \
  32do { fprintf(stderr, "smbus: error: " fmt , ## __VA_ARGS__);} while (0)
  33#endif
  34
  35enum {
  36    SMBUS_IDLE,
  37    SMBUS_WRITE_DATA,
  38    SMBUS_READ_DATA,
  39    SMBUS_DONE,
  40    SMBUS_CONFUSED = -1
  41};
  42
  43static void smbus_do_quick_cmd(SMBusDevice *dev, int recv)
  44{
  45    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
  46
  47    DPRINTF("Quick Command %d\n", recv);
  48    if (sc->quick_cmd) {
  49        sc->quick_cmd(dev, recv);
  50    }
  51}
  52
  53static void smbus_do_write(SMBusDevice *dev)
  54{
  55    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
  56
  57    DPRINTF("Command %d len %d\n", dev->data_buf[0], dev->data_len);
  58    if (sc->write_data) {
  59        sc->write_data(dev, dev->data_buf, dev->data_len);
  60    }
  61}
  62
  63static int smbus_i2c_event(I2CSlave *s, enum i2c_event event)
  64{
  65    SMBusDevice *dev = SMBUS_DEVICE(s);
  66
  67    switch (event) {
  68    case I2C_START_SEND:
  69        switch (dev->mode) {
  70        case SMBUS_IDLE:
  71            DPRINTF("Incoming data\n");
  72            dev->mode = SMBUS_WRITE_DATA;
  73            break;
  74
  75        default:
  76            BADF("Unexpected send start condition in state %d\n", dev->mode);
  77            dev->mode = SMBUS_CONFUSED;
  78            break;
  79        }
  80        break;
  81
  82    case I2C_START_RECV:
  83        switch (dev->mode) {
  84        case SMBUS_IDLE:
  85            DPRINTF("Read mode\n");
  86            dev->mode = SMBUS_READ_DATA;
  87            break;
  88
  89        case SMBUS_WRITE_DATA:
  90            if (dev->data_len == 0) {
  91                BADF("Read after write with no data\n");
  92                dev->mode = SMBUS_CONFUSED;
  93            } else {
  94                smbus_do_write(dev);
  95                DPRINTF("Read mode\n");
  96                dev->mode = SMBUS_READ_DATA;
  97            }
  98            break;
  99
 100        default:
 101            BADF("Unexpected recv start condition in state %d\n", dev->mode);
 102            dev->mode = SMBUS_CONFUSED;
 103            break;
 104        }
 105        break;
 106
 107    case I2C_FINISH:
 108        if (dev->data_len == 0) {
 109            if (dev->mode == SMBUS_WRITE_DATA || dev->mode == SMBUS_READ_DATA) {
 110                smbus_do_quick_cmd(dev, dev->mode == SMBUS_READ_DATA);
 111            }
 112        } else {
 113            switch (dev->mode) {
 114            case SMBUS_WRITE_DATA:
 115                smbus_do_write(dev);
 116                break;
 117
 118            case SMBUS_READ_DATA:
 119                BADF("Unexpected stop during receive\n");
 120                break;
 121
 122            default:
 123                /* Nothing to do.  */
 124                break;
 125            }
 126        }
 127        dev->mode = SMBUS_IDLE;
 128        dev->data_len = 0;
 129        break;
 130
 131    case I2C_NACK:
 132        switch (dev->mode) {
 133        case SMBUS_DONE:
 134            /* Nothing to do.  */
 135            break;
 136
 137        case SMBUS_READ_DATA:
 138            dev->mode = SMBUS_DONE;
 139            break;
 140
 141        default:
 142            BADF("Unexpected NACK in state %d\n", dev->mode);
 143            dev->mode = SMBUS_CONFUSED;
 144            break;
 145        }
 146    }
 147
 148    return 0;
 149}
 150
 151static uint8_t smbus_i2c_recv(I2CSlave *s)
 152{
 153    SMBusDevice *dev = SMBUS_DEVICE(s);
 154    SMBusDeviceClass *sc = SMBUS_DEVICE_GET_CLASS(dev);
 155    uint8_t ret = 0xff;
 156
 157    switch (dev->mode) {
 158    case SMBUS_READ_DATA:
 159        if (sc->receive_byte) {
 160            ret = sc->receive_byte(dev);
 161        }
 162        DPRINTF("Read data %02x\n", ret);
 163        break;
 164
 165    default:
 166        BADF("Unexpected read in state %d\n", dev->mode);
 167        dev->mode = SMBUS_CONFUSED;
 168        break;
 169    }
 170
 171    return ret;
 172}
 173
 174static int smbus_i2c_send(I2CSlave *s, uint8_t data)
 175{
 176    SMBusDevice *dev = SMBUS_DEVICE(s);
 177
 178    switch (dev->mode) {
 179    case SMBUS_WRITE_DATA:
 180        DPRINTF("Write data %02x\n", data);
 181        if (dev->data_len >= sizeof(dev->data_buf)) {
 182            BADF("Too many bytes sent\n");
 183        } else {
 184            dev->data_buf[dev->data_len++] = data;
 185        }
 186        break;
 187
 188    default:
 189        BADF("Unexpected write in state %d\n", dev->mode);
 190        break;
 191    }
 192
 193    return 0;
 194}
 195
 196static void smbus_device_class_init(ObjectClass *klass, void *data)
 197{
 198    I2CSlaveClass *sc = I2C_SLAVE_CLASS(klass);
 199
 200    sc->event = smbus_i2c_event;
 201    sc->recv = smbus_i2c_recv;
 202    sc->send = smbus_i2c_send;
 203}
 204
 205bool smbus_vmstate_needed(SMBusDevice *dev)
 206{
 207    return dev->mode != SMBUS_IDLE;
 208}
 209
 210const VMStateDescription vmstate_smbus_device = {
 211    .name = TYPE_SMBUS_DEVICE,
 212    .version_id = 1,
 213    .minimum_version_id = 1,
 214    .fields      = (VMStateField[]) {
 215        VMSTATE_I2C_SLAVE(i2c, SMBusDevice),
 216        VMSTATE_INT32(mode, SMBusDevice),
 217        VMSTATE_INT32(data_len, SMBusDevice),
 218        VMSTATE_UINT8_ARRAY(data_buf, SMBusDevice, SMBUS_DATA_MAX_LEN),
 219        VMSTATE_END_OF_LIST()
 220    }
 221};
 222
 223static const TypeInfo smbus_device_type_info = {
 224    .name = TYPE_SMBUS_DEVICE,
 225    .parent = TYPE_I2C_SLAVE,
 226    .instance_size = sizeof(SMBusDevice),
 227    .abstract = true,
 228    .class_size = sizeof(SMBusDeviceClass),
 229    .class_init = smbus_device_class_init,
 230};
 231
 232static void smbus_device_register_types(void)
 233{
 234    type_register_static(&smbus_device_type_info);
 235}
 236
 237type_init(smbus_device_register_types)
 238