qemu-devel
[Top][All Lists]
Advanced

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

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


From: Alex Bennée
Subject: Re: [Qemu-devel] [PATCH RFC] i2c-tiny-usb
Date: Thu, 26 Nov 2015 18:07:35 +0000
User-agent: mu4e 0.9.15; emacs 24.5.50.4

Tim Sander <address@hidden> writes:

> 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.

You are missing a s-o-b line and scripts/checkpatch.pl complains about a
few things you should fix before your next submission.

>
> 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,

Where did these magic numbers come from?

> +        .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);

I get that debug output is useful for debugging but if you want to
maintain ability to look at the i2c transactions you might want to
consider the tracing infrastructure.

> +    if (ret >= 0) {
> +        return;
> +    }
> +
> +    switch (request) {
> +    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
> +        break;
> +

Again where are these magic numbers coming from?

> +    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)

Cheers,

--
Alex Bennée



reply via email to

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