[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[Qemu-devel] [PATCH RFC] i2c-tiny-usb
From: |
Tim Sander |
Subject: |
[Qemu-devel] [PATCH RFC] i2c-tiny-usb |
Date: |
Thu, 26 Nov 2015 17:35:57 +0100 |
User-agent: |
KMail/4.14.3 (Linux/4.1.0quirk-00001-gf1e27b0-dirty; KDE/4.14.13; x86_64; ; ) |
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.
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,
+ .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)
--
1.9.1
- [Qemu-devel] [PATCH RFC] i2c-tiny-usb,
Tim Sander <=