* [Qemu-devel] i2c-tiny-usb noob question
@ 2015-11-17 13:45 Tim Sander
2015-11-17 14:12 ` Paolo Bonzini
0 siblings, 1 reply; 6+ messages in thread
From: Tim Sander @ 2015-11-17 13:45 UTC (permalink / raw)
To: qemu-devel; +Cc: Paolo Bonzini
[-- Attachment #1: Type: text/plain, Size: 680 bytes --]
Hi
I had been missing a i2c-bus configurable from the command line.
By searching i have seen the discussion:
https://lists.gnu.org/archive/html/qemu-devel/2014-02/msg03146.html
I just gave it a try and now i have a device which is detected by linux and
allows simple reads (without i2c backend though). But unfortunatly i am
currently stuck as i don't know how the address is transmitted to the device?
So any suggestion how to get the i2c address and whats needed to write to
associated i2c devices is very welcome.
The i2c-tiny-usb is a very simple device which has no endpoints but is just
controlled via the usb control. The device fragment is attached.
Thanks
Tim
[-- Attachment #2: dev-i2c-tiny.c --]
[-- Type: text/x-csrc, Size: 10850 bytes --]
/*
* 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 "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;
} 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 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)
{
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 %x\n", data[0]);
break;
case 0x4107:
DPRINTF("write %x\n", data[0]);
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:\n");
DPRINTF("%x %x %x %x %x %x %x %x %x %x %x\n", value, request, length,
data[0], data[1], data[2], data[3], data[4], data[5], data[6],
data[7]);
data[0] = 0x09;
p->actual_length = 1;
break;
case 0xc107:
DPRINTF("read access ");
DPRINTF("%x %x %x %x %x %x %x %x %x %x %x\n", value, request, length,
data[0], data[1], data[2], data[3], data[4], data[5], data[6],
data[7]);
data[0] = 0x08;
p->actual_length = 1;
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)
{
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;
}
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)
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] i2c-tiny-usb noob question
2015-11-17 13:45 [Qemu-devel] i2c-tiny-usb noob question Tim Sander
@ 2015-11-17 14:12 ` Paolo Bonzini
2015-11-17 14:47 ` Tim Sander
` (2 more replies)
0 siblings, 3 replies; 6+ messages in thread
From: Paolo Bonzini @ 2015-11-17 14:12 UTC (permalink / raw)
To: Tim Sander, qemu-devel
On 17/11/2015 14:45, Tim Sander wrote:
> I just gave it a try and now i have a device which is detected by linux and
> allows simple reads (without i2c backend though). But unfortunatly i am
> currently stuck as i don't know how the address is transmitted to the device?
Do you mean how it is transmitted to the i2c-tiny-usb device by the
driver? The only way to find out is to read a datasheet or the driver
code. Or do you mean something else?
Paolo
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] i2c-tiny-usb noob question
2015-11-17 14:12 ` Paolo Bonzini
@ 2015-11-17 14:47 ` Tim Sander
2015-11-17 14:54 ` Tim Sander
2015-11-17 16:25 ` Tim Sander
2 siblings, 0 replies; 6+ messages in thread
From: Tim Sander @ 2015-11-17 14:47 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel
Hi Paolo
Thanks for your reply.
Am Dienstag, 17. November 2015, 15:12:29 schrieb Paolo Bonzini:
> On 17/11/2015 14:45, Tim Sander wrote:
> > I just gave it a try and now i have a device which is detected by linux
> > and
> > allows simple reads (without i2c backend though). But unfortunatly i am
> > currently stuck as i don't know how the address is transmitted to the
> > device?
> Do you mean how it is transmitted to the i2c-tiny-usb device by the
> driver? The only way to find out is to read a datasheet or the driver
> code. Or do you mean something else?
Yes, i meant that. Probably i should elaborate a little of what i found out so
far (without beeing to aquainted with USB low level stuff):
* i2c-tiny-usb is a low speed device
* low speed devices use at maximum 8 bytes ?
* i2c-tiny-usb has no transport endpoints, just a usb control
* I would expect that the statement:
DPRINTF("%x %x %x %x %x %x %x %x %x %x %x\n", value, request, length,
data[0], data[1], data[2], data[3], data[4], data[5], data[6],data[7]);
would print out the address somewhere.
* doing an access with different addresses (i2cget 0 0x50 vs. i2cget 0 0x51)
gives the same debug output. So the address data must be hidden somewhere
else? Probably USBPacket?
So my question could be refined where else in the qemu data structures, the
address might still be hidden? (Or what in my assumptions is wrong so that i
don't see the address.)
I had been looking at the sources of i2c-tiny-usb and the linux driver. I
haven't got though all the layers of the linux infrastructure and i also could
not map the input i see to the code in the usb dongle so far...
Best regards
Tim
PS: The debug output of the code looks like that:
i2cget -y 0 0x57
usb-i2c-tiny: got control c101, value 0
usb-i2c-tiny: USBPacket: p->iov->iov_base: 55da32596b78 pid: 2d stream: 0
parameter: 0 status: 0 actual_length: 0
usb-i2c-tiny: usb_desc_handle_control return value: -1 status: 0
usb-i2c-tiny: got functionality read c101, value 0
usb-i2c-tiny: got control c107, value 1
usb-i2c-tiny: USBPacket: p->iov->iov_base: 55da32596b78 pid: 2d stream: 0
parameter: 0 status: 0 actual_length: 0
usb-i2c-tiny: usb_desc_handle_control return value: -1 status: 0
usb-i2c-tiny: read access usb-i2c-tiny: 1 c107 1 1 0 ff 8e 2d 0 30 0
usb-i2c-tiny: got control c103, value 0
usb-i2c-tiny: USBPacket: p->iov->iov_base: 55da32596b78 pid: 2d stream: 0
parameter: 0 status: 0 actual_length: 0
usb-i2c-tiny: usb_desc_handle_control return value: -1 status: 0
usb-i2c-tiny: unknown call:
usb-i2c-tiny: 0 c103 1 8 0 ff 8e 2d 0 30 0
0x08 <-output of i2cget
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] i2c-tiny-usb noob question
2015-11-17 14:12 ` Paolo Bonzini
2015-11-17 14:47 ` Tim Sander
@ 2015-11-17 14:54 ` Tim Sander
2015-11-17 16:25 ` Tim Sander
2 siblings, 0 replies; 6+ messages in thread
From: Tim Sander @ 2015-11-17 14:54 UTC (permalink / raw)
To: Paolo Bonzini; +Cc: qemu-devel
Hi Paolo
Am Dienstag, 17. November 2015, 15:12:29 schrieb Paolo Bonzini:
> On 17/11/2015 14:45, Tim Sander wrote:
> > I just gave it a try and now i have a device which is detected by linux
> > and
> > allows simple reads (without i2c backend though). But unfortunatly i am
> > currently stuck as i don't know how the address is transmitted to the
> > device?
> Do you mean how it is transmitted to the i2c-tiny-usb device by the
> driver? The only way to find out is to read a datasheet or the driver
> code. Or do you mean something else?
Just after hitting send of my previous mail i found out that i overlooked
"index", which is the argument used in this case to transmit the address.
So the next step would be to use this value and do accesses to any i2c devices
associated: any pointers where s.th. like this has been done?
Best regards
Tim
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] i2c-tiny-usb noob question
2015-11-17 14:12 ` Paolo Bonzini
2015-11-17 14:47 ` Tim Sander
2015-11-17 14:54 ` Tim Sander
@ 2015-11-17 16:25 ` Tim Sander
2015-11-17 17:20 ` Paolo Bonzini
2 siblings, 1 reply; 6+ messages in thread
From: Tim Sander @ 2015-11-17 16:25 UTC (permalink / raw)
To: qemu-devel; +Cc: Paolo Bonzini
Hi
I am no so far that i can successfully read and write from linux usermode to
my virtual i2c-tiny-usb device. Unfortunatly i was not able to plug any i2c-
device into this device to go on with implementing the backend bus accesses.
I can instantiate my i2c-tiny-usb with the following command line option:
-device usb-i2c-tiny,id=i2c-0
Now i would like to add child devices to this device instance. Unfortunatly i
could not find out how to add e.g. the tmp105 sensor to this i2c-bus. It always
apears on its own bus. Is this possible?
Best regards
Tim
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [Qemu-devel] i2c-tiny-usb noob question
2015-11-17 16:25 ` Tim Sander
@ 2015-11-17 17:20 ` Paolo Bonzini
0 siblings, 0 replies; 6+ messages in thread
From: Paolo Bonzini @ 2015-11-17 17:20 UTC (permalink / raw)
To: Tim Sander, qemu-devel
On 17/11/2015 17:25, Tim Sander wrote:
> I can instantiate my i2c-tiny-usb with the following command line option:
> -device usb-i2c-tiny,id=i2c-0
>
> Now i would like to add child devices to this device instance. Unfortunatly i
> could not find out how to add e.g. the tmp105 sensor to this i2c-bus. It always
> apears on its own bus. Is this possible?
Yes, like
-device usb-i2c-tiny,id=usbi2c -device tmp105,bus=usbi2c.0
(possibly even without the ".0", I don't recall).
Paolo
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2015-11-17 17:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2015-11-17 13:45 [Qemu-devel] i2c-tiny-usb noob question Tim Sander
2015-11-17 14:12 ` Paolo Bonzini
2015-11-17 14:47 ` Tim Sander
2015-11-17 14:54 ` Tim Sander
2015-11-17 16:25 ` Tim Sander
2015-11-17 17:20 ` Paolo Bonzini
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).