qemu-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Qemu-devel] [PATCH RFC] i2c-tiny-usb


From: Tim Sander
Subject: [Qemu-devel] [PATCH RFC] i2c-tiny-usb
Date: Thu, 26 Nov 2015 17:35:57 +0100
User-agent: KMail/4.14.3 (Linux/4.1.0quirk-00001-gf1e27b0-dirty; KDE/4.14.13; x86_64; ; )

Hi 

Below is a patch implementing the i2c-tiny-usb device.
I am currently not sure about the i2c semantics. I think
incrementing the address on longer reads is wrong?
But at least i can read the high byte(?) of the temperature
via "i2cget -y 0 0x50".

With this device it should be possible to define i2c busses 
via command line e.g:
-device usb-i2c-tiny,id=i2c-0 -device tmp105,bus=i2c,address=0x50
have been used for the first test.

Best regards
Tim
---
 default-configs/usb.mak |   1 +
 hw/usb/Makefile.objs    |   1 +
 hw/usb/dev-i2c-tiny.c   | 383 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 385 insertions(+)
 create mode 100644 hw/usb/dev-i2c-tiny.c

diff --git a/default-configs/usb.mak b/default-configs/usb.mak
index f4b8568..01d2c9f 100644
--- a/default-configs/usb.mak
+++ b/default-configs/usb.mak
@@ -8,3 +8,4 @@ CONFIG_USB_AUDIO=y
 CONFIG_USB_SERIAL=y
 CONFIG_USB_NETWORK=y
 CONFIG_USB_BLUETOOTH=y
+CONFIG_USB_I2C_TINY=y
diff --git a/hw/usb/Makefile.objs b/hw/usb/Makefile.objs
index 8f00fbd..3a4c337 100644
--- a/hw/usb/Makefile.objs
+++ b/hw/usb/Makefile.objs
@@ -20,6 +20,7 @@ common-obj-$(CONFIG_USB_AUDIO)        += dev-audio.o
 common-obj-$(CONFIG_USB_SERIAL)       += dev-serial.o
 common-obj-$(CONFIG_USB_NETWORK)      += dev-network.o
 common-obj-$(CONFIG_USB_BLUETOOTH)    += dev-bluetooth.o
+common-obj-$(CONFIG_USB_I2C_TINY)     += dev-i2c-tiny.o
 
 ifeq ($(CONFIG_USB_SMARTCARD),y)
 common-obj-y                          += dev-smartcard-reader.o
diff --git a/hw/usb/dev-i2c-tiny.c b/hw/usb/dev-i2c-tiny.c
new file mode 100644
index 0000000..1dabb36
--- /dev/null
+++ b/hw/usb/dev-i2c-tiny.c
@@ -0,0 +1,383 @@
+/*
+ * I2C tiny usb device emulation
+ *
+ * Copyright (c) 2015 Tim Sander <address@hidden>
+ *
+ * Loosly based on usb dev-serial.c:
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <address@hidden>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licensed under the LGPL.
+ *
+ */
+
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/i2c/i2c.h"
+#include "sysemu/char.h"
+#include "endian.h"
+
+/* #define DEBUG_USBI2C */
+
+#ifdef DEBUG_USBI2C
+#define DPRINTF(fmt, ...) \
+do { printf("usb-i2c-tiny: " fmt , ## __VA_ARGS__); } while (0)
+#else
+#define DPRINTF(fmt, ...) do {} while (0)
+#endif
+
+/* commands from USB, must e.g. match command ids in kernel driver */
+#define CMD_ECHO       0
+#define CMD_GET_FUNC   1
+#define CMD_SET_DELAY  2
+#define CMD_GET_STATUS 3
+
+/* To determine what functionality is present */
+#define I2C_FUNC_I2C                            0x00000001
+#define I2C_FUNC_10BIT_ADDR                     0x00000002
+#define I2C_FUNC_PROTOCOL_MANGLING              0x00000004
+#define I2C_FUNC_SMBUS_HWPEC_CALC               0x00000008 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_READ_WORD_DATA_PEC       0x00000800 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA_PEC      0x00001000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_PROC_CALL_PEC            0x00002000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL_PEC      0x00004000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_BLOCK_PROC_CALL          0x00008000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_QUICK                    0x00010000
+#define I2C_FUNC_SMBUS_READ_BYTE                0x00020000
+#define I2C_FUNC_SMBUS_WRITE_BYTE               0x00040000
+#define I2C_FUNC_SMBUS_READ_BYTE_DATA           0x00080000
+#define I2C_FUNC_SMBUS_WRITE_BYTE_DATA          0x00100000
+#define I2C_FUNC_SMBUS_READ_WORD_DATA           0x00200000
+#define I2C_FUNC_SMBUS_WRITE_WORD_DATA          0x00400000
+#define I2C_FUNC_SMBUS_PROC_CALL                0x00800000
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA          0x01000000
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA         0x02000000
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK           0x04000000 /*I2C-like blk-xfr 
*/
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK          0x08000000 /*1-byte reg. 
addr.*/
+#define I2C_FUNC_SMBUS_READ_I2C_BLOCK_2         0x10000000 /*I2C-like 
blk-xfer*/
+#define I2C_FUNC_SMBUS_WRITE_I2C_BLOCK_2        0x20000000 /* w/ 2-byte 
regadr*/
+#define I2C_FUNC_SMBUS_READ_BLOCK_DATA_PEC      0x40000000 /* SMBus 2.0 */
+#define I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC     0x80000000 /* SMBus 2.0 */
+
+#define I2C_FUNC_SMBUS_BYTE (I2C_FUNC_SMBUS_READ_BYTE | \
+    I2C_FUNC_SMBUS_WRITE_BYTE)
+#define I2C_FUNC_SMBUS_BYTE_DATA (I2C_FUNC_SMBUS_READ_BYTE_DATA | \
+    I2C_FUNC_SMBUS_WRITE_BYTE_DATA)
+#define I2C_FUNC_SMBUS_WORD_DATA (I2C_FUNC_SMBUS_READ_WORD_DATA | \
+    I2C_FUNC_SMBUS_WRITE_WORD_DATA)
+#define I2C_FUNC_SMBUS_BLOCK_DATA (I2C_FUNC_SMBUS_READ_BLOCK_DATA | \
+    I2C_FUNC_SMBUS_WRITE_BLOCK_DATA)
+#define I2C_FUNC_SMBUS_I2C_BLOCK (I2C_FUNC_SMBUS_READ_I2C_BLOCK | \
+    I2C_FUNC_SMBUS_WRITE_I2C_BLOCK)
+
+#define I2C_FUNC_SMBUS_EMUL (I2C_FUNC_SMBUS_QUICK | \
+    I2C_FUNC_SMBUS_BYTE | \
+    I2C_FUNC_SMBUS_BYTE_DATA | \
+    I2C_FUNC_SMBUS_WORD_DATA | \
+    I2C_FUNC_SMBUS_PROC_CALL | \
+    I2C_FUNC_SMBUS_WRITE_BLOCK_DATA | \
+    I2C_FUNC_SMBUS_WRITE_BLOCK_DATA_PEC | \
+    I2C_FUNC_SMBUS_I2C_BLOCK)
+
+#define DeviceOutVendor ((USB_DIR_OUT|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+#define DeviceInVendor  ((USB_DIR_IN|USB_TYPE_VENDOR|USB_RECIP_DEVICE)<<8)
+
+typedef struct {
+    USBDevice dev;
+    I2CBus *i2cbus;
+} UsbI2cTinyState;
+
+#define TYPE_USB_I2C_TINY "usb-i2c-dev"
+#define USB_I2C_TINY_DEV(obj) OBJECT_CHECK(UsbI2cTinyState, \
+    (obj), TYPE_USB_I2C_TINY)
+
+enum {
+    STR_MANUFACTURER = 1,
+    STR_PRODUCT_SERIAL,
+    STR_SERIALNUMBER,
+};
+
+static const USBDescStrings desc_strings = {
+    [STR_MANUFACTURER]    = "QEMU",
+    [STR_PRODUCT_SERIAL]  = "QEMU USB I2C Tiny",
+    [STR_SERIALNUMBER]    = "1",
+};
+
+static const USBDescIface desc_iface0 = {
+    .bInterfaceNumber              = 1,
+    .bNumEndpoints                 = 0,
+    .bInterfaceClass               = 0xff,
+    .bInterfaceSubClass            = 0xff,
+    .bInterfaceProtocol            = 0xff,
+    .eps = (USBDescEndpoint[]) {
+    }
+};
+
+static const USBDescDevice desc_device = {
+    .bcdUSB                        = 0x0110,
+    .bMaxPacketSize0               = 8,
+    .bNumConfigurations            = 1,
+    .confs = (USBDescConfig[]) {
+        {
+            .bNumInterfaces        = 1,
+            .bConfigurationValue   = 1,
+            .bmAttributes          = USB_CFG_ATT_ONE,
+            .bMaxPower             = 50,
+            .nif = 1,
+            .ifs = &desc_iface0,
+        },
+    },
+};
+
+static const USBDesc desc_usb_i2c = {
+    .id = {
+        .idVendor          = 0x0403,
+        .idProduct         = 0xc631,
+        .bcdDevice         = 0x0205,
+        .iManufacturer     = STR_MANUFACTURER,
+        .iProduct          = STR_PRODUCT_SERIAL,
+        .iSerialNumber     = STR_SERIALNUMBER,
+    },
+    .full = &desc_device,
+    .str  = desc_strings,
+};
+
+static int usb_i2c_read_byte(I2CBus *bus, uint8_t addr)
+{
+    int retval;
+    i2c_start_transfer(bus, addr, 1);
+    retval = i2c_recv(bus);
+    i2c_end_transfer(bus);
+    return retval;
+}
+
+static int usb_i2c_write_byte(I2CBus *bus, uint8_t addr, uint8_t data)
+{
+    int retval;
+    i2c_start_transfer(bus, addr, 0);
+    retval = i2c_send(bus, data);
+    i2c_end_transfer(bus);
+    return retval;
+}
+
+static void usb_i2c_handle_reset(USBDevice *dev)
+{
+    DPRINTF("reset\n");
+}
+
+static void usb_i2c_handle_control(USBDevice *dev, USBPacket *p,
+               int request, int value, int index, int length, uint8_t *data)
+{
+    UsbI2cTinyState *s = (UsbI2cTinyState *)dev;
+    int ret;
+
+    DPRINTF("got control %x, value %x\n", request, value);
+    DPRINTF("iov_base:%lx pid:%x stream:%x param:%lx status:%x len:%x\n",
+            (uint64_t)(p->iov).iov->iov_base, p->pid, p->stream, p->parameter,
+            p->status, p->actual_length);
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    DPRINTF("usb_desc_handle_control return value: %i status: %x\n", ret,
+            p->status);
+    if (ret >= 0) {
+        return;
+    }
+
+    switch (request) {
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        break;
+
+    case 0x4102:
+        /* set clock, ignore */
+        break;
+
+    case 0x4105:
+        DPRINTF("unknown access 0x4105 %x\n", index);
+        p->actual_length = 1;
+        break;
+
+    case 0x4107:
+        {
+        int i;
+        DPRINTF("write addr:0x%x len:%i\n", index, length);
+        for (i = 0; i < length; i++) {
+            usb_i2c_write_byte(s->i2cbus, index+i, data[i]);
+        }
+        }
+        break;
+
+    case 0xc101:
+        {
+        /* thats what the real thing reports, FIXME: can we do better here? */
+        uint32_t func = htole32(I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
+        DPRINTF("got functionality read %x, value %x\n", request, value);
+        memcpy(data, &func, sizeof(func));
+        p->actual_length = sizeof(func);
+        }
+        break;
+
+    case 0xc103:
+        DPRINTF("unknown call 0xc103 index: 0x%x\n", index);
+        DPRINTF("v:%x r:%x l:%x i:%x\n", value, request, length, index);
+        p->actual_length = 1;
+        break;
+
+    case 0xc106:
+        DPRINTF("unknown call 0xc106 index:0x%x\n", index);
+        DPRINTF("v:%x r:%x l:%x i:%x\n", value, request, length, index);
+        p->actual_length = 1;
+        break;
+
+    case 0xc107:
+        {
+        int i;
+        DPRINTF("read access addr:0x%x len:0x%x\n", index, length);
+        DPRINTF("v:%x r:%x l:%x i:%x\n", value, request, length, index);
+        for (i = 0; i < length; i++) {
+            data[i] = usb_i2c_read_byte(s->i2cbus, index+i);
+        }
+        p->actual_length = length;
+        }
+        break;
+
+    default:
+        DPRINTF("got unsupported/bogus control %x, value %x\n", request, 
value);
+        p->status = USB_RET_STALL;
+        break;
+    }
+}
+
+static void usb_i2c_handle_data(USBDevice *dev, USBPacket *p)
+{
+    DPRINTF("unexpected call to usb_i2c_handle_data\n");
+}
+
+static void usb_i2c_realize(USBDevice *dev, Error **errp)
+{
+    UsbI2cTinyState *s = (UsbI2cTinyState *)dev;
+    Error *local_err = NULL;
+
+    usb_desc_create_serial(dev);
+    usb_desc_init(dev);
+
+    usb_check_attach(dev, &local_err);
+    if (local_err) {
+        error_propagate(errp, local_err);
+        return;
+    }
+    s->i2cbus = i2c_init_bus(&dev->qdev, "i2c");
+    usb_i2c_handle_reset(dev);
+}
+
+static USBDevice *usb_i2c_init(USBBus *bus, const char *filename)
+{
+    USBDevice *dev;
+    CharDriverState *cdrv;
+    uint32_t vendorid = 0, productid = 0;
+    char label[32];
+    static int index;
+
+    while (*filename && *filename != ':') {
+        const char *p;
+        char *e;
+        if (strstart(filename, "vendorid=", &p)) {
+            vendorid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                error_report("bogus vendor ID %s", p);
+                return NULL;
+            }
+            filename = e;
+        } else if (strstart(filename, "productid=", &p)) {
+            productid = strtol(p, &e, 16);
+            if (e == p || (*e && *e != ',' && *e != ':')) {
+                error_report("bogus product ID %s", p);
+                return NULL;
+            }
+            filename = e;
+        } else {
+            error_report("unrecognized usbi2c USB option %s", filename);
+            return NULL;
+        }
+        while (*filename == ',') {
+            filename++;
+        }
+    }
+    if (!*filename) {
+        error_report("character device specification needed");
+        return NULL;
+    }
+    filename++;
+
+    snprintf(label, sizeof(label), "usbusbi2c%d", index++);
+    cdrv = qemu_chr_new(label, filename, NULL);
+    if (!cdrv) {
+        return NULL;
+    }
+
+    dev = usb_create(bus, "usb-usbi2c");
+    qdev_prop_set_chr(&dev->qdev, "chardev", cdrv);
+    if (vendorid) {
+        qdev_prop_set_uint16(&dev->qdev, "vendorid", vendorid);
+    }
+    if (productid) {
+        qdev_prop_set_uint16(&dev->qdev, "productid", productid);
+    }
+    return dev;
+}
+
+static const VMStateDescription vmstate_usb_i2c = {
+    .name = "usb-i2c-tiny",
+    .unmigratable = 1,
+};
+
+static Property usbi2c_properties[] = {
+    DEFINE_PROP_END_OF_LIST(),
+};
+
+static void usb_i2c_dev_class_init(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->realize        = usb_i2c_realize;
+    uc->handle_reset   = usb_i2c_handle_reset;
+    uc->handle_control = usb_i2c_handle_control;
+    uc->handle_data    = usb_i2c_handle_data;
+    dc->vmsd = &vmstate_usb_i2c;
+    set_bit(DEVICE_CATEGORY_INPUT, dc->categories);
+}
+
+static const TypeInfo usb_i2c_dev_type_info = {
+    .name = TYPE_USB_I2C_TINY,
+    .parent = TYPE_USB_DEVICE,
+    .instance_size = sizeof(UsbI2cTinyState),
+    .abstract = true,
+    .class_init = usb_i2c_dev_class_init,
+};
+
+static void usb_i2c_class_initfn(ObjectClass *klass, void *data)
+{
+    DeviceClass *dc = DEVICE_CLASS(klass);
+    USBDeviceClass *uc = USB_DEVICE_CLASS(klass);
+
+    uc->product_desc   = "QEMU USB I2C Tiny";
+    uc->usb_desc       = &desc_usb_i2c;
+    dc->props = usbi2c_properties;
+}
+
+static const TypeInfo usbi2c_info = {
+    .name          = "usb-i2c-tiny",
+    .parent        = TYPE_USB_I2C_TINY,
+    .class_init    = usb_i2c_class_initfn,
+};
+
+static void usb_i2c_register_types(void)
+{
+    type_register_static(&usb_i2c_dev_type_info);
+    type_register_static(&usbi2c_info);
+    usb_legacy_register("usb-i2c-tiny", "i2c-bus-tiny", usb_i2c_init);
+}
+
+type_init(usb_i2c_register_types)
-- 
1.9.1




reply via email to

[Prev in Thread] Current Thread [Next in Thread]