[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