diff mbox

[v6] i2c-tiny-usb: a small usb to i2c bridge

Message ID 6712401.QIBatU8kCx@dabox (mailing list archive)
State New, archived
Headers show

Commit Message

Tim Sander Feb. 16, 2016, 3:55 p.m. UTC
Oh my, so many stupid little errors. Due to firewall impairment i am sending these
patches manually. Last time i forgot to disable the newline breaks which made the
patch v5 unusable. Sorry for this inconvenience.

i2c-tiny-usb is a small usb to i2c bridge:
 http://www.harbaum.org/till/i2c_tiny_usb/index.shtml

It is pretty simple and has no usb endpoints just a control.
Reasons for adding this device:
* Linux device driver available
* adding an additional i2c bus via command line e.g.
  -device usb-i2c-tiny,id=i2c-0 -device tmp105,bus=i2c,address=0x50
Signed-off-by: tim@krieglstein.org
---
 default-configs/usb.mak |   1 +
 hw/usb/Makefile.objs    |   1 +
 hw/usb/dev-i2c-tiny.c   | 314 ++++++++++++++++++++++++++++++++++++++++++++++++
 trace-events            |  11 ++
 4 files changed, 327 insertions(+)
 create mode 100644 hw/usb/dev-i2c-tiny.c

Comments

Alex Bennée Feb. 16, 2016, 4:51 p.m. UTC | #1
Tim Sander <tim@krieglstein.org> writes:

> Oh my, so many stupid little errors. Due to firewall impairment i am sending these
> patches manually. Last time i forgot to disable the newline breaks which made the
> patch v5 unusable. Sorry for this inconvenience.

Please run ${QEMU_SRC}/scripts/checkpatch.pl on your patch and correct
the stylistic errors it reports. I'll echo Gerd's comments about the
value of using git send-email to send patches. If you can glean you
email clients SMTP settings then you should be able to get
git-send-email to do this as well. It's much less error prone once you
have it working.

>
> i2c-tiny-usb is a small usb to i2c bridge:
>  http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
>
> It is pretty simple and has no usb endpoints just a control.
> Reasons for adding this device:
> * Linux device driver available
> * adding an additional i2c bus via command line e.g.
>   -device usb-i2c-tiny,id=i2c-0 -device tmp105,bus=i2c,address=0x50
> Signed-off-by: tim@krieglstein.org
> ---
>  default-configs/usb.mak |   1 +
>  hw/usb/Makefile.objs    |   1 +
>  hw/usb/dev-i2c-tiny.c   | 314 ++++++++++++++++++++++++++++++++++++++++++++++++
>  trace-events            |  11 ++
>  4 files changed, 327 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..7abb9df
> --- /dev/null
> +++ b/hw/usb/dev-i2c-tiny.c
> @@ -0,0 +1,314 @@
> +/*
> + * I2C tiny usb device emulation
> + *
> + * i2c-tiny-usb is a small usb to i2c bridge:
> + *
> + * http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
> + *
> + * The simulated device is pretty simple and has no usb endpoints.
> + * There is a Linux device driver available named i2c-tiny-usb.
> + *
> + * Below is an example how to use this device from command line:
> + *  -device usb-i2c-tiny,id=i2c-0 -device tmp105,bus=i2c,address=0x50
> + *
> + * Copyright (c) 2015 Tim Sander <tim@krieglstein.org>
> + *
> + * Loosly based on usb dev-serial.c:
> + * Copyright (c) 2006 CodeSourcery.
> + * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
> + * Written by Paul Brook, reused for FTDI by Samuel Thibault
> + *
> + * This code is licensed under the LGPL.
> + *
> + */
> +
> +#include "trace.h"
> +#include "qemu-common.h"
> +#include "qemu/error-report.h"
> +#include "hw/usb.h"
> +#include "hw/usb/desc.h"
> +#include "hw/i2c/i2c.h"
> +#include "hw/i2c/smbus.h"
> +#include "sysemu/char.h"
> +#include "endian.h"
> +
> +/* 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"
> +#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 = (const 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 void usb_i2c_handle_reset(USBDevice *dev)
> +{
> +    trace_usb_i2c_tiny_reset();
> +}
> +
> +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,i, i2c_value;
> +    uint32_t func;
> +
> +    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
> +    if (ret >= 0) {
> +        return;
> +    }
> +
> +    switch (request) {
> +    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
> +        break;
> +    case 0x4102:
> +        /* set clock, ignore */
> +        trace_usb_i2c_tiny_set_clock();
> +        break;
> +
> +    case 0x4105:
> +        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
> +        break;
> +
> +    case 0x4107:
> +        /* this seems to be a byte type access */
> +        if (i2c_start_transfer(s->i2cbus, /*address*/index, 0)) {
> +            trace_usb_i2c_tiny_i2c_start_transfer_failed();
> +            p->actual_length = 0; /* write failure */
> +            break;
> +        }
> +        for (i = 0; i < length; i++) {
> +            trace_usb_i2c_tiny_write(request, index, i, data[i]);
> +            i2c_send(s->i2cbus, data[i]);
> +        }
> +        p->actual_length = length;
> +        i2c_end_transfer(s->i2cbus);
> +        break;
> +
> +    case 0xc101:
> +        /* thats what the real thing reports, FIXME: can we do better here? */
> +        func = htole32(I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
> +        memcpy(data, &func, sizeof(func));
> +        p->actual_length = sizeof(func);
> +        trace_usb_i2c_tiny_functionality_read(length);
> +        break;
> +
> +    case 0xc103:
> +        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
> +        p->actual_length = 1;
> +        break;
> +
> +    case 0xc106:
> +    case 0xc107:
> +        if (i2c_start_transfer(s->i2cbus, /*address*/ index, 1)) {
> +            trace_usb_i2c_tiny_i2c_start_transfer_failed();
> +            p->actual_length = 0;
> +            break;
> +        }
> +        if(request&1) {
> +            i2c_send(s->i2cbus, data[0]);
> +            i2c_start_transfer(s->i2cbus, /*address*/ index, 1);
> +        }
> +        for (i = 0; i < length; i++) {
> +            i2c_value = i2c_recv(s->i2cbus);
> +            if (i2c_value < 0) {
> +                break;
> +            }
> +            data[i] = i2c_value;
> +            trace_usb_i2c_tiny_read(request, index, i, data[i]);
> +        }
> +        if(request&1) {
> +            i2c_nack(s->i2cbus);
> +        }
> +        p->actual_length = length;
> +        i2c_end_transfer(s->i2cbus);
> +        break;
> +
> +    default:
> +        p->status = USB_RET_STALL;
> +        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
> +        break;
> +    }
> +}
> +
> +static void usb_i2c_handle_data(USBDevice *dev, USBPacket *p)
> +{
> +    trace_usb_i2c_tiny_usb_i2c_handle_data();
> +}
> +
> +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 const VMStateDescription vmstate_usb_i2c = {
> +    .name = "usb-i2c-tiny",
> +};
> +
> +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);
> +}
> +
> +type_init(usb_i2c_register_types)
> diff --git a/trace-events b/trace-events
> index c9ac144..54f977d 100644
> --- a/trace-events
> +++ b/trace-events
> @@ -328,6 +328,17 @@ usb_port_attach(int bus, const char *port, const char *devspeed, const char *por
>  usb_port_detach(int bus, const char *port) "bus %d, port %s"
>  usb_port_release(int bus, const char *port) "bus %d, port %s"
>
> +# hw/usb/dev-tiny-i2c.c
> +usb_i2c_tiny_read(int request, int address,int offset,int data) "request:0x%x address:%i offset:%i data:%i"
> +usb_i2c_tiny_write(int request, int address,int offset,int data) "request:0x%x address:%i offset:%i data:%i"
> +usb_i2c_tiny_functionality_read(int length) "length:%i"
> +usb_i2c_tiny_reset(void) ""
> +usb_i2c_tiny_set_clock(void) ""
> +usb_i2c_tiny_usb_i2c_handle_data(void) ""
> +usb_i2c_tiny_i2c_start_transfer_failed(void) "i2c start transfer failed"
> +usb_i2c_tiny_ignored_request(int index,int request,int value,int length) "index:%i request:0x%x value:%i length:%i"
> +usb_i2c_tiny_unknown_request(int index,int request,int value,int length) "index:%i request:0x%x value:%i length:%i"
> +
>  # hw/usb/hcd-ohci.c
>  usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at %x"
>  usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count  0x%.8x relative %d"


--
Alex Bennée
Gerd Hoffman Feb. 17, 2016, 4:26 p.m. UTC | #2
On Di, 2016-02-16 at 16:51 +0000, Alex Bennée wrote:
> Tim Sander <tim@krieglstein.org> writes:
> 
> > Oh my, so many stupid little errors. Due to firewall impairment i am sending these
> > patches manually. Last time i forgot to disable the newline breaks which made the
> > patch v5 unusable. Sorry for this inconvenience.

"git am" accepts this one, but ...

> Please run ${QEMU_SRC}/scripts/checkpatch.pl on your patch and correct
> the stylistic errors it reports.

... checkpatch.pl indeed throws a bunch of codestyle warnings.

> I'll echo Gerd's comments about the
> value of using git send-email to send patches. If you can glean you
> email clients SMTP settings then you should be able to get
> git-send-email to do this as well.

Yes, you can configure git send-email to use your internal mail server
to send out patches.  Best place it in the global config
($HOME/.gitconfig instead of $repo/.git/config) so you don't have to
repeat the procedure for every git tree you have.

Alternatively you can configure a mail daemon such as postfix on your
workstation to deliver mails using your internal mail server as relay.

cheers,
  Gerd
Gerd Hoffman Feb. 22, 2016, 8:59 a.m. UTC | #3
On Di, 2016-02-16 at 16:55 +0100, Tim Sander wrote:
> Oh my, so many stupid little errors. Due to firewall impairment i am sending these
> patches manually. Last time i forgot to disable the newline breaks which made the
> patch v5 unusable. Sorry for this inconvenience.

Didn't work again:

nilsson kraxel ~/projects/qemu (queue/usb)# patches apply -s
id:2351494.kn6DdoE8y3@dabox
Applying: i2c-tiny-usb is a small usb to i2c bridge
fatal: corrupt patch at line 101

cheers,
  Gerd
diff mbox

Patch

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..7abb9df
--- /dev/null
+++ b/hw/usb/dev-i2c-tiny.c
@@ -0,0 +1,314 @@ 
+/*
+ * I2C tiny usb device emulation
+ *
+ * i2c-tiny-usb is a small usb to i2c bridge:
+ *
+ * http://www.harbaum.org/till/i2c_tiny_usb/index.shtml
+ *
+ * The simulated device is pretty simple and has no usb endpoints.
+ * There is a Linux device driver available named i2c-tiny-usb.
+ *
+ * Below is an example how to use this device from command line:
+ *  -device usb-i2c-tiny,id=i2c-0 -device tmp105,bus=i2c,address=0x50
+ *
+ * Copyright (c) 2015 Tim Sander <tim@krieglstein.org>
+ *
+ * Loosly based on usb dev-serial.c:
+ * Copyright (c) 2006 CodeSourcery.
+ * Copyright (c) 2008 Samuel Thibault <samuel.thibault@ens-lyon.org>
+ * Written by Paul Brook, reused for FTDI by Samuel Thibault
+ *
+ * This code is licensed under the LGPL.
+ *
+ */
+
+#include "trace.h"
+#include "qemu-common.h"
+#include "qemu/error-report.h"
+#include "hw/usb.h"
+#include "hw/usb/desc.h"
+#include "hw/i2c/i2c.h"
+#include "hw/i2c/smbus.h"
+#include "sysemu/char.h"
+#include "endian.h"
+
+/* 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"
+#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 = (const 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 void usb_i2c_handle_reset(USBDevice *dev)
+{
+    trace_usb_i2c_tiny_reset();
+}
+
+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,i, i2c_value;
+    uint32_t func;
+
+    ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
+    if (ret >= 0) {
+        return;
+    }
+
+    switch (request) {
+    case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
+        break;
+    case 0x4102:
+        /* set clock, ignore */
+        trace_usb_i2c_tiny_set_clock();
+        break;
+
+    case 0x4105:
+        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
+        break;
+
+    case 0x4107:
+        /* this seems to be a byte type access */
+        if (i2c_start_transfer(s->i2cbus, /*address*/index, 0)) {
+            trace_usb_i2c_tiny_i2c_start_transfer_failed();
+            p->actual_length = 0; /* write failure */
+            break;
+        }
+        for (i = 0; i < length; i++) {
+            trace_usb_i2c_tiny_write(request, index, i, data[i]);
+            i2c_send(s->i2cbus, data[i]);
+        }
+        p->actual_length = length;
+        i2c_end_transfer(s->i2cbus);
+        break;
+
+    case 0xc101:
+        /* thats what the real thing reports, FIXME: can we do better here? */
+        func = htole32(I2C_FUNC_I2C | I2C_FUNC_SMBUS_EMUL);
+        memcpy(data, &func, sizeof(func));
+        p->actual_length = sizeof(func);
+        trace_usb_i2c_tiny_functionality_read(length);
+        break;
+
+    case 0xc103:
+        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
+        p->actual_length = 1;
+        break;
+
+    case 0xc106:
+    case 0xc107:
+        if (i2c_start_transfer(s->i2cbus, /*address*/ index, 1)) {
+            trace_usb_i2c_tiny_i2c_start_transfer_failed();
+            p->actual_length = 0;
+            break;
+        }
+        if(request&1) {
+            i2c_send(s->i2cbus, data[0]);
+            i2c_start_transfer(s->i2cbus, /*address*/ index, 1);
+        }
+        for (i = 0; i < length; i++) {
+            i2c_value = i2c_recv(s->i2cbus);
+            if (i2c_value < 0) {
+                break;
+            }
+            data[i] = i2c_value;
+            trace_usb_i2c_tiny_read(request, index, i, data[i]);
+        }
+        if(request&1) {
+            i2c_nack(s->i2cbus);
+        }
+        p->actual_length = length;
+        i2c_end_transfer(s->i2cbus);
+        break;
+
+    default:
+        p->status = USB_RET_STALL;
+        trace_usb_i2c_tiny_unknown_request(index, request, value, length);
+        break;
+    }
+}
+
+static void usb_i2c_handle_data(USBDevice *dev, USBPacket *p)
+{
+    trace_usb_i2c_tiny_usb_i2c_handle_data();
+}
+
+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 const VMStateDescription vmstate_usb_i2c = {
+    .name = "usb-i2c-tiny",
+};
+
+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);
+}
+
+type_init(usb_i2c_register_types)
diff --git a/trace-events b/trace-events
index c9ac144..54f977d 100644
--- a/trace-events
+++ b/trace-events
@@ -328,6 +328,17 @@  usb_port_attach(int bus, const char *port, const char *devspeed, const char *por
 usb_port_detach(int bus, const char *port) "bus %d, port %s"
 usb_port_release(int bus, const char *port) "bus %d, port %s"
 
+# hw/usb/dev-tiny-i2c.c
+usb_i2c_tiny_read(int request, int address,int offset,int data) "request:0x%x address:%i offset:%i data:%i"
+usb_i2c_tiny_write(int request, int address,int offset,int data) "request:0x%x address:%i offset:%i data:%i"
+usb_i2c_tiny_functionality_read(int length) "length:%i"
+usb_i2c_tiny_reset(void) ""
+usb_i2c_tiny_set_clock(void) ""
+usb_i2c_tiny_usb_i2c_handle_data(void) ""
+usb_i2c_tiny_i2c_start_transfer_failed(void) "i2c start transfer failed"
+usb_i2c_tiny_ignored_request(int index,int request,int value,int length) "index:%i request:0x%x value:%i length:%i"
+usb_i2c_tiny_unknown_request(int index,int request,int value,int length) "index:%i request:0x%x value:%i length:%i"
+
 # hw/usb/hcd-ohci.c
 usb_ohci_iso_td_read_failed(uint32_t addr) "ISO_TD read error at %x"
 usb_ohci_iso_td_head(uint32_t head, uint32_t tail, uint32_t flags, uint32_t bp, uint32_t next, uint32_t be, uint32_t framenum, uint32_t startframe, uint32_t framecount, int rel_frame_num) "ISO_TD ED head 0x%.8x tailp 0x%.8x\n0x%.8x 0x%.8x 0x%.8x 0x%.8x\nframe_number 0x%.8x starting_frame 0x%.8x\nframe_count  0x%.8x relative %d"