[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: [Qemu-devel] [PATCH v6] i2c-tiny-usb: a small usb to i2c bridge
From: |
Alex Bennée |
Subject: |
Re: [Qemu-devel] [PATCH v6] i2c-tiny-usb: a small usb to i2c bridge |
Date: |
Tue, 16 Feb 2016 16:51:13 +0000 |
User-agent: |
mu4e 0.9.17; emacs 25.0.91.3 |
Tim Sander <address@hidden> 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: address@hidden
> ---
> 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 <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 "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