Message ID | 1664220.kcr3K9QWbf@dabox |
---|---|
State | New |
Headers | show |
Tim Sander <tim@krieglstein.org> 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 <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 "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
On Do, 2015-11-26 at 17:35 +0100, Tim Sander wrote: > Hi > > Below is a patch implementing the i2c-tiny-usb device. Is there a specification for this kind of device? Or does it mimic existing hardware? Please add that into to the comment at the head of the file. > +#ifdef DEBUG_USBI2C > +#define DPRINTF(fmt, ...) \ > +do { printf("usb-i2c-tiny: " fmt , ## __VA_ARGS__); } while (0) > +#else > +#define DPRINTF(fmt, ...) do {} while (0) > +#endif Please consider turning them into trace points. > +static const USBDescIface desc_iface0 = { > + .bInterfaceNumber = 1, > + .bNumEndpoints = 0, > + .bInterfaceClass = 0xff, > + .bInterfaceSubClass = 0xff, > + .bInterfaceProtocol = 0xff, > + .eps = (USBDescEndpoint[]) { > + } > +}; Hmm? No endpoints? > +static void usb_i2c_handle_control(USBDevice *dev, USBPacket *p, > + int request, int value, int index, int length, uint8_t *data) > +{ > + 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; Ah, it all goes over the control pipe. > +static USBDevice *usb_i2c_init(USBBus *bus, const char *filename) Please drop this ... > + usb_legacy_register("usb-i2c-tiny", "i2c-bus-tiny", usb_i2c_init); ... and this. It's for backward compatibility with old qemu versions (-usbdevice ...), and we don't need that for new devices. cheers, Gerd
Hi Alex Thanks for your feedback, answers as usual inline. Am Donnerstag, 26. November 2015, 18:07:35 schrieb Alex Bennée: > Tim Sander <tim@krieglstein.org> 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. I was unsure about the access length so i was just asking for feedback this time, thats why omitted the signed of by line. It seems as if i grabbed an older version of checkpatch as these errors didn't come up with the slightly older script. These new occurences will be fixed. > > 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 <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 "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? I guess i have forgotton to add the link of the hardware which i am aiming to emulate: http://www.harbaum.org/till/i2c_tiny_usb/index.shtml Looking at the Linux driver it seems there are two supported manufacturer numbers, i just took one of them. > > + .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. Any pointers a quick search turned up the http://wiki.qemu.org/Features/Tracing/Roadmap page but this seems pretty outdated? > > + if (ret >= 0) { > > + return; > > + } > > + > > + switch (request) { > > + case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: > > + break; > > + > > Again where are these magic numbers coming from? I choose this usb device as it has a mainline driver and is reasonable simple. These numbers are just what i have seen during reverse engineering: starting up the linux i2c-tiny-usb module and look for the results. So i don't know more than what i have written into the comments. <snip> Best regards Tim
Tim Sander <tim@krieglstein.org> writes: [...] >> 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. > Any pointers a quick search turned up the > http://wiki.qemu.org/Features/Tracing/Roadmap page but this seems pretty > outdated? Have a look at docs/tracing.txt. [...]
Am Freitag, 27. November 2015, 07:48:21 schrieb Gerd Hoffmann: > On Do, 2015-11-26 at 17:35 +0100, Tim Sander wrote: > > Hi > > > > Below is a patch implementing the i2c-tiny-usb device. > > Is there a specification for this kind of device? http://www.harbaum.org/till/i2c_tiny_usb/index.shtml > Or does it mimic existing hardware? Yes, the reason i choose this device where: *simple *linux driver available which makes a cmdline configurable i2c bus easy. > Please add that into to the comment at the head of the file. Will do. > > +#ifdef DEBUG_USBI2C > > +#define DPRINTF(fmt, ...) \ > > +do { printf("usb-i2c-tiny: " fmt , ## __VA_ARGS__); } while (0) > > +#else > > +#define DPRINTF(fmt, ...) do {} while (0) > > +#endif > > Please consider turning them into trace points. Ok. > > +static const USBDescIface desc_iface0 = { > > + .bInterfaceNumber = 1, > > + .bNumEndpoints = 0, > > + .bInterfaceClass = 0xff, > > + .bInterfaceSubClass = 0xff, > > + .bInterfaceProtocol = 0xff, > > + .eps = (USBDescEndpoint[]) { > > + } > > +}; > > Hmm? No endpoints? Yes this device has indeed no endpoints just control as you found out below. > > +static void usb_i2c_handle_control(USBDevice *dev, USBPacket *p, > > + int request, int value, int index, int length, uint8_t > > *data) +{ > > > > + 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; > > Ah, it all goes over the control pipe. > > > +static USBDevice *usb_i2c_init(USBBus *bus, const char *filename) > > Please drop this ... > > > + usb_legacy_register("usb-i2c-tiny", "i2c-bus-tiny", usb_i2c_init); > > ... and this. > > It's for backward compatibility with old qemu versions (-usbdevice ...), > and we don't need that for new devices. Nice this makes the code even smaller :-). Best regards Tim
Tim Sander <tim@krieglstein.org> writes: > Hi Alex > > Thanks for your feedback, answers as usual inline. > > Am Donnerstag, 26. November 2015, 18:07:35 schrieb Alex Bennée: >> Tim Sander <tim@krieglstein.org> writes: >> > Hi >> > <snip> >> You are missing a s-o-b line and scripts/checkpatch.pl complains about a >> few things you should fix before your next submission. > I was unsure about the access length so i was just asking for feedback this > time, thats why omitted the signed of by line. > It seems as if i grabbed an older version of checkpatch as these errors didn't > come up with the slightly older script. These new occurences will be fixed. Cool. FWIW I always run against the current $(QEMU_SRC)/scripts/checkpatch.pl as it is updated reasonably frequently. -- Alex Bennée
Hi Paolo Am Freitag, 27. November 2015, 10:32:25 schrieb Paolo Bonzini: > On 26/11/2015 17:35, Tim Sander wrote: > > 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? > > Yes, it is. This is a bus address, not an address inside the device. > > Also you should only call i2c_start_transfer/i2c_end_transfer once. Good to know. I have one more thing, i2cdetect looks like this: 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f 40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f 50: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f 60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f 70: 70 71 72 73 74 75 76 77 But i only have a device at 0x50 so most numbers should be "--". I know that i just have to set the read length to zero, to archive that. Currently i don't know how to determine if there is something registered on the requested i2c address? Best regards Tim
On 27/11/2015 13:39, Tim Sander wrote: > > I have one more thing, i2cdetect looks like this: > 0 1 2 3 4 5 6 7 8 9 a b c d e f > 00: 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f > 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f > 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f > 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f > 40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f > 50: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f > 60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f > 70: 70 71 72 73 74 75 76 77 > > But i only have a device at 0x50 so most numbers should be "--". > I know that i just have to set the read length to zero, to archive that. > Currently i don't know how to determine if there is something registered on > the requested i2c address? If there is no slave at the requested address, i2c_start_transfer will return 1. Paolo
Hi Paolo and List Am Freitag, 27. November 2015, 13:53:22 schrieb Paolo Bonzini: > On 27/11/2015 13:39, Tim Sander wrote: > > I have one more thing, i2cdetect looks like this: > > 0 1 2 3 4 5 6 7 8 9 a b c d e f > > > > 00: 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f > > 10: 10 11 12 13 14 15 16 17 18 19 1a 1b 1c 1d 1e 1f > > 20: 20 21 22 23 24 25 26 27 28 29 2a 2b 2c 2d 2e 2f > > 30: 30 31 32 33 34 35 36 37 38 39 3a 3b 3c 3d 3e 3f > > 40: 40 41 42 43 44 45 46 47 48 49 4a 4b 4c 4d 4e 4f > > 50: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f > > 60: 60 61 62 63 64 65 66 67 68 69 6a 6b 6c 6d 6e 6f > > 70: 70 71 72 73 74 75 76 77 > > > > But i only have a device at 0x50 so most numbers should be "--". > > I know that i just have to set the read length to zero, to archive that. > > Currently i don't know how to determine if there is something registered > > on > > the requested i2c address? > > If there is no slave at the requested address, i2c_start_transfer will > return 1. Ok, that works. Now probably the last problem i see is that i fail to set the data-address of the i2c-device? I know the correct offset address for accesses on the bus e.g. i2cget -y 0 0x50 2 where 2 is an example offset for the access to this device. So any hint how setting the data-address on the i2c bus in qemu works? I have seen the function i2c_set_slave_address but i guess thats not the right function setting the data address. Best regards Tim
On 09/12/2015 17:40, Tim Sander wrote: >> > If there is no slave at the requested address, i2c_start_transfer will >> > return 1. > Ok, that works. Now probably the last problem i see is that i fail to set the > data-address of the i2c-device? > I know the correct offset address for accesses on the bus e.g. > i2cget -y 0 0x50 2 > where 2 is an example offset for the access to this device. > > So any hint how setting the data-address on the i2c bus in qemu works? If you have a data address, you probably want to use functions like smbus_read_byte that do the right write-read sequence for you: if (i2c_start_transfer(bus, addr, 0)) { return -1; } i2c_send(bus, command); i2c_start_transfer(bus, addr, 1); data = i2c_recv(bus); i2c_nack(bus); i2c_end_transfer(bus); Paolo
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 <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 "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)