mirror of https://github.com/xemu-project/xemu.git
Merge branch 'usb.4' of git://anongit.freedesktop.org/spice/qemu
* 'usb.4' of git://anongit.freedesktop.org/spice/qemu: (32 commits) usb: zap pdev from usbport usb: rewrite fw path, fix numbering usb: add port property. usb: keep track of physical port address. usb storage: handle long responses usb storage: fix status reporting usb storage: high speed support usb: add device qualifier support usb: add usb_desc_attach usb: add attach callback usb: add speed mask to ports usb: hid: change serial number to "42". usb: hid: remote wakeup support. usb: hub: remote wakeup support. usb: uhci: remote wakeup support. usb: add usb_wakeup() + wakeup callback to port ops usb: rework attach/detach workflow usb: create USBPortOps, move attach there. usb: move remote wakeup handling to common code usb: move USB_REQ_{GET,SET}_CONFIGURATION handling to common code ...
This commit is contained in:
commit
b947c12c0b
|
@ -329,8 +329,8 @@ F: hw/lsi53c895a.c
|
||||||
F: hw/scsi*
|
F: hw/scsi*
|
||||||
|
|
||||||
USB
|
USB
|
||||||
M: qemu-devel@nongnu.org
|
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||||
S: Odd Fixes
|
S: Maintained
|
||||||
F: hw/usb*
|
F: hw/usb*
|
||||||
|
|
||||||
vhost
|
vhost
|
||||||
|
|
|
@ -88,7 +88,7 @@ common-obj-y += eeprom93xx.o
|
||||||
common-obj-y += scsi-disk.o cdrom.o
|
common-obj-y += scsi-disk.o cdrom.o
|
||||||
common-obj-y += scsi-generic.o scsi-bus.o
|
common-obj-y += scsi-generic.o scsi-bus.o
|
||||||
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
common-obj-y += usb.o usb-hub.o usb-$(HOST_USB).o usb-hid.o usb-msd.o usb-wacom.o
|
||||||
common-obj-y += usb-serial.o usb-net.o usb-bus.o
|
common-obj-y += usb-serial.o usb-net.o usb-bus.o usb-desc.o
|
||||||
common-obj-$(CONFIG_SSI) += ssi.o
|
common-obj-$(CONFIG_SSI) += ssi.o
|
||||||
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
common-obj-$(CONFIG_SSI_SD) += ssi-sd.o
|
||||||
common-obj-$(CONFIG_SD) += sd.o
|
common-obj-$(CONFIG_SD) += sd.o
|
||||||
|
|
525
hw/usb-bt.c
525
hw/usb-bt.c
|
@ -20,6 +20,7 @@
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "bt.h"
|
#include "bt.h"
|
||||||
|
|
||||||
|
@ -51,251 +52,202 @@ struct USBBtState {
|
||||||
#define USB_ACL_EP 2
|
#define USB_ACL_EP 2
|
||||||
#define USB_SCO_EP 3
|
#define USB_SCO_EP 3
|
||||||
|
|
||||||
static const uint8_t qemu_bt_dev_descriptor[] = {
|
enum {
|
||||||
0x12, /* u8 bLength; */
|
STR_MANUFACTURER = 1,
|
||||||
USB_DT_DEVICE, /* u8 bDescriptorType; Device */
|
STR_SERIALNUMBER,
|
||||||
0x10, 0x01, /* u16 bcdUSB; v1.10 */
|
|
||||||
|
|
||||||
0xe0, /* u8 bDeviceClass; Wireless */
|
|
||||||
0x01, /* u8 bDeviceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 bDeviceProtocol; Bluetooth */
|
|
||||||
0x40, /* u8 bMaxPacketSize0; 64 Bytes */
|
|
||||||
|
|
||||||
0x12, 0x0a, /* u16 idVendor; */
|
|
||||||
0x01, 0x00, /* u16 idProduct; Bluetooth Dongle (HCI mode) */
|
|
||||||
0x58, 0x19, /* u16 bcdDevice; (some devices have 0x48, 0x02) */
|
|
||||||
|
|
||||||
0x00, /* u8 iManufacturer; */
|
|
||||||
0x00, /* u8 iProduct; */
|
|
||||||
0x00, /* u8 iSerialNumber; */
|
|
||||||
0x01, /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_bt_config_descriptor[] = {
|
static const USBDescStrings desc_strings = {
|
||||||
/* one configuration */
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
0x09, /* u8 bLength; */
|
[STR_SERIALNUMBER] = "1",
|
||||||
USB_DT_CONFIG, /* u8 bDescriptorType; */
|
};
|
||||||
0xb1, 0x00, /* u16 wTotalLength; */
|
|
||||||
0x02, /* u8 bNumInterfaces; (2) */
|
|
||||||
0x01, /* u8 bConfigurationValue; */
|
|
||||||
0x00, /* u8 iConfiguration; */
|
|
||||||
0xc0, /* u8 bmAttributes;
|
|
||||||
Bit 7: must be set,
|
|
||||||
6: Self-powered,
|
|
||||||
5: Remote wakeup,
|
|
||||||
4..0: resvd */
|
|
||||||
0x00, /* u8 MaxPower; */
|
|
||||||
|
|
||||||
/* USB 1.1:
|
static const USBDescIface desc_iface_bluetooth[] = {
|
||||||
* USB 2.0, single TT organization (mandatory):
|
{
|
||||||
* one interface, protocol 0
|
.bInterfaceNumber = 0,
|
||||||
*
|
.bNumEndpoints = 3,
|
||||||
* USB 2.0, multiple TT organization (optional):
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
* two interfaces, protocols 1 (like single TT)
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
* and 2 (multiple TT mode) ... config is
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
* sometimes settable
|
.eps = (USBDescEndpoint[]) {
|
||||||
* NOT IMPLEMENTED
|
{
|
||||||
*/
|
.bEndpointAddress = USB_DIR_IN | USB_EVT_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x10,
|
||||||
|
.bInterval = 0x02,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_ACL_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 0x40,
|
||||||
|
.bInterval = 0x0a,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_ACL_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 0x40,
|
||||||
|
.bInterval = 0x0a,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 0,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 1,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x09,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x09,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 2,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x11,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x11,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 3,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x19,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x19,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 4,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x21,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x21,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},{
|
||||||
|
.bInterfaceNumber = 1,
|
||||||
|
.bAlternateSetting = 5,
|
||||||
|
.bNumEndpoints = 2,
|
||||||
|
.bInterfaceClass = 0xe0, /* Wireless */
|
||||||
|
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||||
|
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x31,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | USB_SCO_EP,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 0x31,
|
||||||
|
.bInterval = 0x01,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* interface one */
|
static const USBDescDevice desc_device_bluetooth = {
|
||||||
0x09, /* u8 if_bLength; */
|
.bcdUSB = 0x0110,
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
.bDeviceClass = 0xe0, /* Wireless */
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
.bDeviceSubClass = 0x01, /* Radio Frequency */
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
.bDeviceProtocol = 0x01, /* Bluetooth */
|
||||||
0x03, /* u8 if_bNumEndpoints; */
|
.bMaxPacketSize0 = 64,
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
.bNumConfigurations = 1,
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
.confs = (USBDescConfig[]) {
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
{
|
||||||
0x00, /* u8 if_iInterface; */
|
.bNumInterfaces = 2,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = 0xc0,
|
||||||
|
.bMaxPower = 0,
|
||||||
|
.nif = ARRAY_SIZE(desc_iface_bluetooth),
|
||||||
|
.ifs = desc_iface_bluetooth,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* endpoint one */
|
static const USBDesc desc_bluetooth = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.id = {
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
.idVendor = 0x0a12,
|
||||||
USB_DIR_IN | USB_EVT_EP, /* u8 ep_bEndpointAddress; */
|
.idProduct = 0x0001,
|
||||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
.bcdDevice = 0x1958,
|
||||||
0x10, 0x00, /* u16 ep_wMaxPacketSize; */
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
0x02, /* u8 ep_bInterval; */
|
.iProduct = 0,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
/* endpoint two */
|
},
|
||||||
0x07, /* u8 ep_bLength; */
|
.full = &desc_device_bluetooth,
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
.str = desc_strings,
|
||||||
USB_DIR_OUT | USB_ACL_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint three */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_ACL_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting one */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x00, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x00, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting two */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x01, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x09, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x09, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting three */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x02, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x11, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x11, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting four */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x03, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x19, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x19, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting five */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x04, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x21, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x21, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* interface two setting six */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
|
||||||
0x01, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x05, /* u8 if_bAlternateSetting; */
|
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
|
||||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
|
||||||
0x00, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* endpoint one */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_OUT | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x31, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* endpoint two */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
|
||||||
USB_DIR_IN | USB_SCO_EP, /* u8 ep_bEndpointAddress; */
|
|
||||||
0x01, /* u8 ep_bmAttributes; Isochronous */
|
|
||||||
0x31, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x01, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
|
|
||||||
/* If implemented, the DFU interface descriptor goes here with no
|
|
||||||
* endpoints or alternative settings. */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
|
static void usb_bt_fifo_reset(struct usb_hci_in_fifo_s *fifo)
|
||||||
|
@ -424,85 +376,38 @@ static int usb_bt_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
struct USBBtState *s = (struct USBBtState *) dev->opaque;
|
struct USBBtState *s = (struct USBBtState *) dev->opaque;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
switch (request) {
|
||||||
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||||
|
s->config = 0;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||||
|
s->config = 1;
|
||||||
|
usb_bt_fifo_reset(&s->evt);
|
||||||
|
usb_bt_fifo_reset(&s->acl);
|
||||||
|
usb_bt_fifo_reset(&s->sco);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
case InterfaceRequest | USB_REQ_GET_STATUS:
|
case InterfaceRequest | USB_REQ_GET_STATUS:
|
||||||
case EndpointRequest | USB_REQ_GET_STATUS:
|
case EndpointRequest | USB_REQ_GET_STATUS:
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
data[0] = 0x00;
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
data[1] = 0x00;
|
||||||
ret = 2;
|
ret = 2;
|
||||||
break;
|
break;
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
|
case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
goto fail;
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
case InterfaceOutRequest | USB_REQ_SET_FEATURE:
|
case InterfaceOutRequest | USB_REQ_SET_FEATURE:
|
||||||
case EndpointOutRequest | USB_REQ_SET_FEATURE:
|
case EndpointOutRequest | USB_REQ_SET_FEATURE:
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
goto fail;
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch (value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
ret = sizeof(qemu_bt_dev_descriptor);
|
|
||||||
memcpy(data, qemu_bt_dev_descriptor, ret);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
ret = sizeof(qemu_bt_config_descriptor);
|
|
||||||
memcpy(data, qemu_bt_config_descriptor, ret);
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch(value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = qemu_bt_config_descriptor[0x5];
|
|
||||||
ret = 1;
|
|
||||||
s->config = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
if (value != qemu_bt_config_descriptor[0x5] && value != 0) {
|
|
||||||
printf("%s: Wrong SET_CONFIGURATION request (%i)\n",
|
|
||||||
__FUNCTION__, value);
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
s->config = 1;
|
|
||||||
usb_bt_fifo_reset(&s->evt);
|
|
||||||
usb_bt_fifo_reset(&s->acl);
|
|
||||||
usb_bt_fifo_reset(&s->sco);
|
|
||||||
break;
|
break;
|
||||||
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
||||||
if (value != 0 || (index & ~1) || length != 1)
|
if (value != 0 || (index & ~1) || length != 1)
|
||||||
|
@ -618,8 +523,7 @@ static void usb_bt_handle_destroy(USBDevice *dev)
|
||||||
|
|
||||||
static int usb_bt_initfn(USBDevice *dev)
|
static int usb_bt_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
struct USBBtState *s = DO_UPCAST(struct USBBtState, dev, dev);
|
usb_desc_init(dev);
|
||||||
s->dev.speed = USB_SPEED_HIGH;
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -648,6 +552,7 @@ static struct USBDeviceInfo bt_info = {
|
||||||
.product_desc = "QEMU BT dongle",
|
.product_desc = "QEMU BT dongle",
|
||||||
.qdev.name = "usb-bt-dongle",
|
.qdev.name = "usb-bt-dongle",
|
||||||
.qdev.size = sizeof(struct USBBtState),
|
.qdev.size = sizeof(struct USBBtState),
|
||||||
|
.usb_desc = &desc_bluetooth,
|
||||||
.init = usb_bt_initfn,
|
.init = usb_bt_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_bt_handle_reset,
|
.handle_reset = usb_bt_handle_reset,
|
||||||
|
|
129
hw/usb-bus.c
129
hw/usb-bus.c
|
@ -5,13 +5,20 @@
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
|
|
||||||
static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
|
static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent);
|
||||||
static char *usbbus_get_fw_dev_path(DeviceState *dev);
|
|
||||||
|
static char *usb_get_dev_path(DeviceState *dev);
|
||||||
|
static char *usb_get_fw_dev_path(DeviceState *qdev);
|
||||||
|
|
||||||
static struct BusInfo usb_bus_info = {
|
static struct BusInfo usb_bus_info = {
|
||||||
.name = "USB",
|
.name = "USB",
|
||||||
.size = sizeof(USBBus),
|
.size = sizeof(USBBus),
|
||||||
.print_dev = usb_bus_dev_print,
|
.print_dev = usb_bus_dev_print,
|
||||||
.get_fw_dev_path = usbbus_get_fw_dev_path,
|
.get_dev_path = usb_get_dev_path,
|
||||||
|
.get_fw_dev_path = usb_get_fw_dev_path,
|
||||||
|
.props = (Property[]) {
|
||||||
|
DEFINE_PROP_STRING("port", USBDevice, port_path),
|
||||||
|
DEFINE_PROP_END_OF_LIST()
|
||||||
|
},
|
||||||
};
|
};
|
||||||
static int next_usb_bus = 0;
|
static int next_usb_bus = 0;
|
||||||
static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
|
static QTAILQ_HEAD(, USBBus) busses = QTAILQ_HEAD_INITIALIZER(busses);
|
||||||
|
@ -48,6 +55,7 @@ static int usb_qdev_init(DeviceState *qdev, DeviceInfo *base)
|
||||||
pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
|
pstrcpy(dev->product_desc, sizeof(dev->product_desc), info->product_desc);
|
||||||
dev->info = info;
|
dev->info = info;
|
||||||
dev->auto_attach = 1;
|
dev->auto_attach = 1;
|
||||||
|
QLIST_INIT(&dev->strings);
|
||||||
rc = dev->info->init(dev);
|
rc = dev->info->init(dev);
|
||||||
if (rc == 0 && dev->auto_attach)
|
if (rc == 0 && dev->auto_attach)
|
||||||
usb_device_attach(dev);
|
usb_device_attach(dev);
|
||||||
|
@ -112,16 +120,28 @@ USBDevice *usb_create_simple(USBBus *bus, const char *name)
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
||||||
USBDevice *pdev, usb_attachfn attach)
|
USBPortOps *ops, int speedmask)
|
||||||
{
|
{
|
||||||
port->opaque = opaque;
|
port->opaque = opaque;
|
||||||
port->index = index;
|
port->index = index;
|
||||||
port->attach = attach;
|
port->opaque = opaque;
|
||||||
port->pdev = pdev;
|
port->index = index;
|
||||||
|
port->ops = ops;
|
||||||
|
port->speedmask = speedmask;
|
||||||
QTAILQ_INSERT_TAIL(&bus->free, port, next);
|
QTAILQ_INSERT_TAIL(&bus->free, port, next);
|
||||||
bus->nfree++;
|
bus->nfree++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr)
|
||||||
|
{
|
||||||
|
if (upstream) {
|
||||||
|
snprintf(downstream->path, sizeof(downstream->path), "%s.%d",
|
||||||
|
upstream->path, portnr);
|
||||||
|
} else {
|
||||||
|
snprintf(downstream->path, sizeof(downstream->path), "%d", portnr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void usb_unregister_port(USBBus *bus, USBPort *port)
|
void usb_unregister_port(USBBus *bus, USBPort *port)
|
||||||
{
|
{
|
||||||
if (port->dev)
|
if (port->dev)
|
||||||
|
@ -140,9 +160,22 @@ static void do_attach(USBDevice *dev)
|
||||||
dev->product_desc);
|
dev->product_desc);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
dev->attached++;
|
if (dev->port_path) {
|
||||||
|
QTAILQ_FOREACH(port, &bus->free, next) {
|
||||||
|
if (strcmp(port->path, dev->port_path) == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (port == NULL) {
|
||||||
|
fprintf(stderr, "Warning: usb port %s (bus %s) not found\n",
|
||||||
|
dev->port_path, bus->qbus.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
port = QTAILQ_FIRST(&bus->free);
|
||||||
|
}
|
||||||
|
|
||||||
port = QTAILQ_FIRST(&bus->free);
|
dev->attached++;
|
||||||
QTAILQ_REMOVE(&bus->free, port, next);
|
QTAILQ_REMOVE(&bus->free, port, next);
|
||||||
bus->nfree--;
|
bus->nfree--;
|
||||||
|
|
||||||
|
@ -156,8 +189,9 @@ int usb_device_attach(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBBus *bus = usb_bus_from_device(dev);
|
USBBus *bus = usb_bus_from_device(dev);
|
||||||
|
|
||||||
if (bus->nfree == 1) {
|
if (bus->nfree == 1 && dev->port_path == NULL) {
|
||||||
/* Create a new hub and chain it on. */
|
/* Create a new hub and chain it on
|
||||||
|
(unless a physical port location is specified). */
|
||||||
usb_create_simple(bus, "usb-hub");
|
usb_create_simple(bus, "usb-hub");
|
||||||
}
|
}
|
||||||
do_attach(dev);
|
do_attach(dev);
|
||||||
|
@ -231,12 +265,43 @@ static void usb_bus_dev_print(Monitor *mon, DeviceState *qdev, int indent)
|
||||||
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
||||||
USBBus *bus = usb_bus_from_device(dev);
|
USBBus *bus = usb_bus_from_device(dev);
|
||||||
|
|
||||||
monitor_printf(mon, "%*saddr %d.%d, speed %s, name %s%s\n",
|
monitor_printf(mon, "%*saddr %d.%d, port %s, speed %s, name %s%s\n",
|
||||||
indent, "", bus->busnr, dev->addr,
|
indent, "", bus->busnr, dev->addr,
|
||||||
|
dev->port ? dev->port->path : "-",
|
||||||
usb_speed(dev->speed), dev->product_desc,
|
usb_speed(dev->speed), dev->product_desc,
|
||||||
dev->attached ? ", attached" : "");
|
dev->attached ? ", attached" : "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static char *usb_get_dev_path(DeviceState *qdev)
|
||||||
|
{
|
||||||
|
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
||||||
|
return qemu_strdup(dev->port->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *usb_get_fw_dev_path(DeviceState *qdev)
|
||||||
|
{
|
||||||
|
USBDevice *dev = DO_UPCAST(USBDevice, qdev, qdev);
|
||||||
|
char *fw_path, *in;
|
||||||
|
int pos = 0;
|
||||||
|
long nr;
|
||||||
|
|
||||||
|
fw_path = qemu_malloc(32 + strlen(dev->port->path) * 6);
|
||||||
|
in = dev->port->path;
|
||||||
|
while (true) {
|
||||||
|
nr = strtol(in, &in, 10);
|
||||||
|
if (in[0] == '.') {
|
||||||
|
/* some hub between root port and device */
|
||||||
|
pos += sprintf(fw_path + pos, "hub@%ld/", nr);
|
||||||
|
in++;
|
||||||
|
} else {
|
||||||
|
/* the device itself */
|
||||||
|
pos += sprintf(fw_path + pos, "%s@%ld", qdev_fw_name(qdev), nr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return fw_path;
|
||||||
|
}
|
||||||
|
|
||||||
void usb_info(Monitor *mon)
|
void usb_info(Monitor *mon)
|
||||||
{
|
{
|
||||||
USBBus *bus;
|
USBBus *bus;
|
||||||
|
@ -253,8 +318,8 @@ void usb_info(Monitor *mon)
|
||||||
dev = port->dev;
|
dev = port->dev;
|
||||||
if (!dev)
|
if (!dev)
|
||||||
continue;
|
continue;
|
||||||
monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n",
|
monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
|
||||||
bus->busnr, dev->addr, usb_speed(dev->speed),
|
bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
|
||||||
dev->product_desc);
|
dev->product_desc);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -309,43 +374,3 @@ USBDevice *usbdevice_create(const char *cmdline)
|
||||||
}
|
}
|
||||||
return usb->usbdevice_init(params);
|
return usb->usbdevice_init(params);
|
||||||
}
|
}
|
||||||
|
|
||||||
static int usbbus_get_fw_dev_path_helper(USBDevice *d, USBBus *bus, char *p,
|
|
||||||
int len)
|
|
||||||
{
|
|
||||||
int l = 0;
|
|
||||||
USBPort *port;
|
|
||||||
|
|
||||||
QTAILQ_FOREACH(port, &bus->used, next) {
|
|
||||||
if (port->dev == d) {
|
|
||||||
if (port->pdev) {
|
|
||||||
l = usbbus_get_fw_dev_path_helper(port->pdev, bus, p, len);
|
|
||||||
}
|
|
||||||
l += snprintf(p + l, len - l, "%s@%x/", qdev_fw_name(&d->qdev),
|
|
||||||
port->index);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return l;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char *usbbus_get_fw_dev_path(DeviceState *dev)
|
|
||||||
{
|
|
||||||
USBDevice *d = (USBDevice*)dev;
|
|
||||||
USBBus *bus = usb_bus_from_device(d);
|
|
||||||
char path[100];
|
|
||||||
int l;
|
|
||||||
|
|
||||||
assert(d->attached != 0);
|
|
||||||
|
|
||||||
l = usbbus_get_fw_dev_path_helper(d, bus, path, sizeof(path));
|
|
||||||
|
|
||||||
if (l == 0) {
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
|
|
||||||
path[l-1] = '\0';
|
|
||||||
|
|
||||||
return strdup(path);
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,406 @@
|
||||||
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
|
#include "trace.h"
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static uint8_t usb_lo(uint16_t val)
|
||||||
|
{
|
||||||
|
return val & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
static uint8_t usb_hi(uint16_t val)
|
||||||
|
{
|
||||||
|
return (val >> 8) & 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
||||||
|
uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength = 0x12;
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x00] = bLength;
|
||||||
|
dest[0x01] = USB_DT_DEVICE;
|
||||||
|
|
||||||
|
dest[0x02] = usb_lo(dev->bcdUSB);
|
||||||
|
dest[0x03] = usb_hi(dev->bcdUSB);
|
||||||
|
dest[0x04] = dev->bDeviceClass;
|
||||||
|
dest[0x05] = dev->bDeviceSubClass;
|
||||||
|
dest[0x06] = dev->bDeviceProtocol;
|
||||||
|
dest[0x07] = dev->bMaxPacketSize0;
|
||||||
|
|
||||||
|
dest[0x08] = usb_lo(id->idVendor);
|
||||||
|
dest[0x09] = usb_hi(id->idVendor);
|
||||||
|
dest[0x0a] = usb_lo(id->idProduct);
|
||||||
|
dest[0x0b] = usb_hi(id->idProduct);
|
||||||
|
dest[0x0c] = usb_lo(id->bcdDevice);
|
||||||
|
dest[0x0d] = usb_hi(id->bcdDevice);
|
||||||
|
dest[0x0e] = id->iManufacturer;
|
||||||
|
dest[0x0f] = id->iProduct;
|
||||||
|
dest[0x10] = id->iSerialNumber;
|
||||||
|
|
||||||
|
dest[0x11] = dev->bNumConfigurations;
|
||||||
|
|
||||||
|
return bLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
||||||
|
uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength = 0x0a;
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x00] = bLength;
|
||||||
|
dest[0x01] = USB_DT_DEVICE_QUALIFIER;
|
||||||
|
|
||||||
|
dest[0x02] = usb_lo(dev->bcdUSB);
|
||||||
|
dest[0x03] = usb_hi(dev->bcdUSB);
|
||||||
|
dest[0x04] = dev->bDeviceClass;
|
||||||
|
dest[0x05] = dev->bDeviceSubClass;
|
||||||
|
dest[0x06] = dev->bDeviceProtocol;
|
||||||
|
dest[0x07] = dev->bMaxPacketSize0;
|
||||||
|
dest[0x08] = dev->bNumConfigurations;
|
||||||
|
dest[0x09] = 0; /* reserved */
|
||||||
|
|
||||||
|
return bLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength = 0x09;
|
||||||
|
uint16_t wTotalLength = 0;
|
||||||
|
int i, rc, count;
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x00] = bLength;
|
||||||
|
dest[0x01] = USB_DT_CONFIG;
|
||||||
|
dest[0x04] = conf->bNumInterfaces;
|
||||||
|
dest[0x05] = conf->bConfigurationValue;
|
||||||
|
dest[0x06] = conf->iConfiguration;
|
||||||
|
dest[0x07] = conf->bmAttributes;
|
||||||
|
dest[0x08] = conf->bMaxPower;
|
||||||
|
wTotalLength += bLength;
|
||||||
|
|
||||||
|
count = conf->nif ? conf->nif : conf->bNumInterfaces;
|
||||||
|
for (i = 0; i < count; i++) {
|
||||||
|
rc = usb_desc_iface(conf->ifs + i, dest + wTotalLength, len - wTotalLength);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
wTotalLength += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x02] = usb_lo(wTotalLength);
|
||||||
|
dest[0x03] = usb_hi(wTotalLength);
|
||||||
|
return wTotalLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength = 0x09;
|
||||||
|
int i, rc, pos = 0;
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x00] = bLength;
|
||||||
|
dest[0x01] = USB_DT_INTERFACE;
|
||||||
|
dest[0x02] = iface->bInterfaceNumber;
|
||||||
|
dest[0x03] = iface->bAlternateSetting;
|
||||||
|
dest[0x04] = iface->bNumEndpoints;
|
||||||
|
dest[0x05] = iface->bInterfaceClass;
|
||||||
|
dest[0x06] = iface->bInterfaceSubClass;
|
||||||
|
dest[0x07] = iface->bInterfaceProtocol;
|
||||||
|
dest[0x08] = iface->iInterface;
|
||||||
|
pos += bLength;
|
||||||
|
|
||||||
|
for (i = 0; i < iface->ndesc; i++) {
|
||||||
|
rc = usb_desc_other(iface->descs + i, dest + pos, len - pos);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
pos += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < iface->bNumEndpoints; i++) {
|
||||||
|
rc = usb_desc_endpoint(iface->eps + i, dest + pos, len - pos);
|
||||||
|
if (rc < 0) {
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
pos += rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength = 0x07;
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
dest[0x00] = bLength;
|
||||||
|
dest[0x01] = USB_DT_ENDPOINT;
|
||||||
|
dest[0x02] = ep->bEndpointAddress;
|
||||||
|
dest[0x03] = ep->bmAttributes;
|
||||||
|
dest[0x04] = usb_lo(ep->wMaxPacketSize);
|
||||||
|
dest[0x05] = usb_hi(ep->wMaxPacketSize);
|
||||||
|
dest[0x06] = ep->bInterval;
|
||||||
|
|
||||||
|
return bLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
int bLength = desc->length ? desc->length : desc->data[0];
|
||||||
|
|
||||||
|
if (len < bLength) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(dest, desc->data, bLength);
|
||||||
|
return bLength;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ------------------------------------------------------------------ */
|
||||||
|
|
||||||
|
static void usb_desc_setdefaults(USBDevice *dev)
|
||||||
|
{
|
||||||
|
const USBDesc *desc = dev->info->usb_desc;
|
||||||
|
|
||||||
|
assert(desc != NULL);
|
||||||
|
switch (dev->speed) {
|
||||||
|
case USB_SPEED_LOW:
|
||||||
|
case USB_SPEED_FULL:
|
||||||
|
dev->device = desc->full;
|
||||||
|
break;
|
||||||
|
case USB_SPEED_HIGH:
|
||||||
|
dev->device = desc->high;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
dev->config = dev->device->confs;
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_desc_init(USBDevice *dev)
|
||||||
|
{
|
||||||
|
dev->speed = USB_SPEED_FULL;
|
||||||
|
usb_desc_setdefaults(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_desc_attach(USBDevice *dev)
|
||||||
|
{
|
||||||
|
const USBDesc *desc = dev->info->usb_desc;
|
||||||
|
|
||||||
|
assert(desc != NULL);
|
||||||
|
if (desc->high && (dev->port->speedmask & USB_SPEED_MASK_HIGH)) {
|
||||||
|
dev->speed = USB_SPEED_HIGH;
|
||||||
|
} else if (desc->full && (dev->port->speedmask & USB_SPEED_MASK_FULL)) {
|
||||||
|
dev->speed = USB_SPEED_FULL;
|
||||||
|
} else {
|
||||||
|
fprintf(stderr, "usb: port/device speed mismatch for \"%s\"\n",
|
||||||
|
dev->info->product_desc);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
usb_desc_setdefaults(dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str)
|
||||||
|
{
|
||||||
|
USBDescString *s;
|
||||||
|
|
||||||
|
QLIST_FOREACH(s, &dev->strings, next) {
|
||||||
|
if (s->index == index) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (s == NULL) {
|
||||||
|
s = qemu_mallocz(sizeof(*s));
|
||||||
|
s->index = index;
|
||||||
|
QLIST_INSERT_HEAD(&dev->strings, s, next);
|
||||||
|
}
|
||||||
|
qemu_free(s->str);
|
||||||
|
s->str = qemu_strdup(str);
|
||||||
|
}
|
||||||
|
|
||||||
|
const char *usb_desc_get_string(USBDevice *dev, uint8_t index)
|
||||||
|
{
|
||||||
|
USBDescString *s;
|
||||||
|
|
||||||
|
QLIST_FOREACH(s, &dev->strings, next) {
|
||||||
|
if (s->index == index) {
|
||||||
|
return s->str;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
uint8_t bLength, pos, i;
|
||||||
|
const char *str;
|
||||||
|
|
||||||
|
if (len < 4) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
/* language ids */
|
||||||
|
dest[0] = 4;
|
||||||
|
dest[1] = USB_DT_STRING;
|
||||||
|
dest[2] = 0x09;
|
||||||
|
dest[3] = 0x04;
|
||||||
|
return 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
str = usb_desc_get_string(dev, index);
|
||||||
|
if (str == NULL) {
|
||||||
|
str = dev->info->usb_desc->str[index];
|
||||||
|
if (str == NULL) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bLength = strlen(str) * 2 + 2;
|
||||||
|
dest[0] = bLength;
|
||||||
|
dest[1] = USB_DT_STRING;
|
||||||
|
i = 0; pos = 2;
|
||||||
|
while (pos+1 < bLength && pos+1 < len) {
|
||||||
|
dest[pos++] = str[i++];
|
||||||
|
dest[pos++] = 0;
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len)
|
||||||
|
{
|
||||||
|
const USBDesc *desc = dev->info->usb_desc;
|
||||||
|
const USBDescDevice *other_dev;
|
||||||
|
uint8_t buf[256];
|
||||||
|
uint8_t type = value >> 8;
|
||||||
|
uint8_t index = value & 0xff;
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
if (dev->speed == USB_SPEED_HIGH) {
|
||||||
|
other_dev = dev->info->usb_desc->full;
|
||||||
|
} else {
|
||||||
|
other_dev = dev->info->usb_desc->high;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch(type) {
|
||||||
|
case USB_DT_DEVICE:
|
||||||
|
ret = usb_desc_device(&desc->id, dev->device, buf, sizeof(buf));
|
||||||
|
trace_usb_desc_device(dev->addr, len, ret);
|
||||||
|
break;
|
||||||
|
case USB_DT_CONFIG:
|
||||||
|
if (index < dev->device->bNumConfigurations) {
|
||||||
|
ret = usb_desc_config(dev->device->confs + index, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
trace_usb_desc_config(dev->addr, index, len, ret);
|
||||||
|
break;
|
||||||
|
case USB_DT_STRING:
|
||||||
|
ret = usb_desc_string(dev, index, buf, sizeof(buf));
|
||||||
|
trace_usb_desc_string(dev->addr, index, len, ret);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_DT_DEVICE_QUALIFIER:
|
||||||
|
if (other_dev != NULL) {
|
||||||
|
ret = usb_desc_device_qualifier(other_dev, buf, sizeof(buf));
|
||||||
|
}
|
||||||
|
trace_usb_desc_device_qualifier(dev->addr, len, ret);
|
||||||
|
break;
|
||||||
|
case USB_DT_OTHER_SPEED_CONFIG:
|
||||||
|
if (other_dev != NULL && index < other_dev->bNumConfigurations) {
|
||||||
|
ret = usb_desc_config(other_dev->confs + index, buf, sizeof(buf));
|
||||||
|
buf[0x01] = USB_DT_OTHER_SPEED_CONFIG;
|
||||||
|
}
|
||||||
|
trace_usb_desc_other_speed_config(dev->addr, index, len, ret);
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
fprintf(stderr, "%s: %d unknown type %d (len %zd)\n", __FUNCTION__,
|
||||||
|
dev->addr, type, len);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret > 0) {
|
||||||
|
if (ret > len) {
|
||||||
|
ret = len;
|
||||||
|
}
|
||||||
|
memcpy(dest, buf, ret);
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_desc_handle_control(USBDevice *dev, int request, int value,
|
||||||
|
int index, int length, uint8_t *data)
|
||||||
|
{
|
||||||
|
const USBDesc *desc = dev->info->usb_desc;
|
||||||
|
int i, ret = -1;
|
||||||
|
|
||||||
|
assert(desc != NULL);
|
||||||
|
switch(request) {
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
||||||
|
dev->addr = value;
|
||||||
|
trace_usb_set_addr(dev->addr);
|
||||||
|
ret = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
||||||
|
ret = usb_desc_get_descriptor(dev, value, data, length);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
||||||
|
data[0] = dev->config->bConfigurationValue;
|
||||||
|
ret = 1;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
||||||
|
for (i = 0; i < dev->device->bNumConfigurations; i++) {
|
||||||
|
if (dev->device->confs[i].bConfigurationValue == value) {
|
||||||
|
dev->config = dev->device->confs + i;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
trace_usb_set_config(dev->addr, value, ret);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case DeviceRequest | USB_REQ_GET_STATUS:
|
||||||
|
data[0] = 0;
|
||||||
|
if (dev->config->bmAttributes & 0x40) {
|
||||||
|
data[0] |= 1 << USB_DEVICE_SELF_POWERED;
|
||||||
|
}
|
||||||
|
if (dev->remote_wakeup) {
|
||||||
|
data[0] |= 1 << USB_DEVICE_REMOTE_WAKEUP;
|
||||||
|
}
|
||||||
|
data[1] = 0x00;
|
||||||
|
ret = 2;
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||||
|
dev->remote_wakeup = 0;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
trace_usb_clear_device_feature(dev->addr, value, ret);
|
||||||
|
break;
|
||||||
|
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
||||||
|
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
||||||
|
dev->remote_wakeup = 1;
|
||||||
|
ret = 0;
|
||||||
|
}
|
||||||
|
trace_usb_set_device_feature(dev->addr, value, ret);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
|
@ -0,0 +1,92 @@
|
||||||
|
#ifndef QEMU_HW_USB_DESC_H
|
||||||
|
#define QEMU_HW_USB_DESC_H
|
||||||
|
|
||||||
|
#include <inttypes.h>
|
||||||
|
|
||||||
|
struct USBDescID {
|
||||||
|
uint16_t idVendor;
|
||||||
|
uint16_t idProduct;
|
||||||
|
uint16_t bcdDevice;
|
||||||
|
uint8_t iManufacturer;
|
||||||
|
uint8_t iProduct;
|
||||||
|
uint8_t iSerialNumber;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USBDescDevice {
|
||||||
|
uint16_t bcdUSB;
|
||||||
|
uint8_t bDeviceClass;
|
||||||
|
uint8_t bDeviceSubClass;
|
||||||
|
uint8_t bDeviceProtocol;
|
||||||
|
uint8_t bMaxPacketSize0;
|
||||||
|
uint8_t bNumConfigurations;
|
||||||
|
|
||||||
|
const USBDescConfig *confs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USBDescConfig {
|
||||||
|
uint8_t bNumInterfaces;
|
||||||
|
uint8_t bConfigurationValue;
|
||||||
|
uint8_t iConfiguration;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint8_t bMaxPower;
|
||||||
|
|
||||||
|
uint8_t nif;
|
||||||
|
const USBDescIface *ifs;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USBDescIface {
|
||||||
|
uint8_t bInterfaceNumber;
|
||||||
|
uint8_t bAlternateSetting;
|
||||||
|
uint8_t bNumEndpoints;
|
||||||
|
uint8_t bInterfaceClass;
|
||||||
|
uint8_t bInterfaceSubClass;
|
||||||
|
uint8_t bInterfaceProtocol;
|
||||||
|
uint8_t iInterface;
|
||||||
|
|
||||||
|
uint8_t ndesc;
|
||||||
|
USBDescOther *descs;
|
||||||
|
USBDescEndpoint *eps;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USBDescEndpoint {
|
||||||
|
uint8_t bEndpointAddress;
|
||||||
|
uint8_t bmAttributes;
|
||||||
|
uint16_t wMaxPacketSize;
|
||||||
|
uint8_t bInterval;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct USBDescOther {
|
||||||
|
uint8_t length;
|
||||||
|
uint8_t *data;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef const char *USBDescStrings[256];
|
||||||
|
|
||||||
|
struct USBDesc {
|
||||||
|
USBDescID id;
|
||||||
|
const USBDescDevice *full;
|
||||||
|
const USBDescDevice *high;
|
||||||
|
const char* const *str;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* generate usb packages from structs */
|
||||||
|
int usb_desc_device(const USBDescID *id, const USBDescDevice *dev,
|
||||||
|
uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_device_qualifier(const USBDescDevice *dev,
|
||||||
|
uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_config(const USBDescConfig *conf, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_iface(const USBDescIface *iface, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_endpoint(const USBDescEndpoint *ep, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_other(const USBDescOther *desc, uint8_t *dest, size_t len);
|
||||||
|
|
||||||
|
/* control message emulation helpers */
|
||||||
|
void usb_desc_init(USBDevice *dev);
|
||||||
|
void usb_desc_attach(USBDevice *dev);
|
||||||
|
void usb_desc_set_string(USBDevice *dev, uint8_t index, const char *str);
|
||||||
|
const char *usb_desc_get_string(USBDevice *dev, uint8_t index);
|
||||||
|
int usb_desc_string(USBDevice *dev, int index, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_get_descriptor(USBDevice *dev, int value, uint8_t *dest, size_t len);
|
||||||
|
int usb_desc_handle_control(USBDevice *dev, int request, int value,
|
||||||
|
int index, int length, uint8_t *data);
|
||||||
|
|
||||||
|
#endif /* QEMU_HW_USB_DESC_H */
|
478
hw/usb-hid.c
478
hw/usb-hid.c
|
@ -25,6 +25,7 @@
|
||||||
#include "hw.h"
|
#include "hw.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
#include "sysemu.h"
|
#include "sysemu.h"
|
||||||
|
|
||||||
/* HID interface requests */
|
/* HID interface requests */
|
||||||
|
@ -73,190 +74,206 @@ typedef struct USBHIDState {
|
||||||
void (*datain)(void *);
|
void (*datain)(void *);
|
||||||
} USBHIDState;
|
} USBHIDState;
|
||||||
|
|
||||||
/* mostly the same values as the Bochs USB Mouse device */
|
enum {
|
||||||
static const uint8_t qemu_mouse_dev_descriptor[] = {
|
STR_MANUFACTURER = 1,
|
||||||
0x12, /* u8 bLength; */
|
STR_PRODUCT_MOUSE,
|
||||||
0x01, /* u8 bDescriptorType; Device */
|
STR_PRODUCT_TABLET,
|
||||||
0x00, 0x01, /* u16 bcdUSB; v1.0 */
|
STR_PRODUCT_KEYBOARD,
|
||||||
|
STR_SERIALNUMBER,
|
||||||
0x00, /* u8 bDeviceClass; */
|
STR_CONFIG_MOUSE,
|
||||||
0x00, /* u8 bDeviceSubClass; */
|
STR_CONFIG_TABLET,
|
||||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
STR_CONFIG_KEYBOARD,
|
||||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
|
||||||
|
|
||||||
0x27, 0x06, /* u16 idVendor; */
|
|
||||||
0x01, 0x00, /* u16 idProduct; */
|
|
||||||
0x00, 0x00, /* u16 bcdDevice */
|
|
||||||
|
|
||||||
0x03, /* u8 iManufacturer; */
|
|
||||||
0x02, /* u8 iProduct; */
|
|
||||||
0x01, /* u8 iSerialNumber; */
|
|
||||||
0x01 /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_mouse_config_descriptor[] = {
|
static const USBDescStrings desc_strings = {
|
||||||
/* one configuration */
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
0x09, /* u8 bLength; */
|
[STR_PRODUCT_MOUSE] = "QEMU USB Mouse",
|
||||||
0x02, /* u8 bDescriptorType; Configuration */
|
[STR_PRODUCT_TABLET] = "QEMU USB Tablet",
|
||||||
0x22, 0x00, /* u16 wTotalLength; */
|
[STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
[STR_SERIALNUMBER] = "42", /* == remote wakeup works */
|
||||||
0x01, /* u8 bConfigurationValue; */
|
[STR_CONFIG_MOUSE] = "HID Mouse",
|
||||||
0x04, /* u8 iConfiguration; */
|
[STR_CONFIG_TABLET] = "HID Tablet",
|
||||||
0xe0, /* u8 bmAttributes;
|
[STR_CONFIG_KEYBOARD] = "HID Keyboard",
|
||||||
Bit 7: must be set,
|
|
||||||
6: Self-powered,
|
|
||||||
5: Remote wakeup,
|
|
||||||
4..0: resvd */
|
|
||||||
50, /* u8 MaxPower; */
|
|
||||||
|
|
||||||
/* USB 1.1:
|
|
||||||
* USB 2.0, single TT organization (mandatory):
|
|
||||||
* one interface, protocol 0
|
|
||||||
*
|
|
||||||
* USB 2.0, multiple TT organization (optional):
|
|
||||||
* two interfaces, protocols 1 (like single TT)
|
|
||||||
* and 2 (multiple TT mode) ... config is
|
|
||||||
* sometimes settable
|
|
||||||
* NOT IMPLEMENTED
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* one interface */
|
|
||||||
0x09, /* u8 if_bLength; */
|
|
||||||
0x04, /* u8 if_bDescriptorType; Interface */
|
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
|
||||||
0x01, /* u8 if_bNumEndpoints; */
|
|
||||||
0x03, /* u8 if_bInterfaceClass; */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; */
|
|
||||||
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
|
||||||
0x07, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* HID descriptor */
|
|
||||||
0x09, /* u8 bLength; */
|
|
||||||
0x21, /* u8 bDescriptorType; */
|
|
||||||
0x01, 0x00, /* u16 HID_class */
|
|
||||||
0x00, /* u8 country_code */
|
|
||||||
0x01, /* u8 num_descriptors */
|
|
||||||
0x22, /* u8 type; Report */
|
|
||||||
52, 0, /* u16 len */
|
|
||||||
|
|
||||||
/* one endpoint (status change endpoint) */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
|
||||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
|
||||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
|
||||||
0x04, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_tablet_config_descriptor[] = {
|
static const USBDescIface desc_iface_mouse = {
|
||||||
/* one configuration */
|
.bInterfaceNumber = 0,
|
||||||
0x09, /* u8 bLength; */
|
.bNumEndpoints = 1,
|
||||||
0x02, /* u8 bDescriptorType; Configuration */
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
0x22, 0x00, /* u16 wTotalLength; */
|
.bInterfaceSubClass = 0x01, /* boot */
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
.bInterfaceProtocol = 0x02,
|
||||||
0x01, /* u8 bConfigurationValue; */
|
.ndesc = 1,
|
||||||
0x05, /* u8 iConfiguration; */
|
.descs = (USBDescOther[]) {
|
||||||
0xa0, /* u8 bmAttributes;
|
{
|
||||||
Bit 7: must be set,
|
/* HID descriptor */
|
||||||
6: Self-powered,
|
.data = (uint8_t[]) {
|
||||||
5: Remote wakeup,
|
0x09, /* u8 bLength */
|
||||||
4..0: resvd */
|
USB_DT_HID, /* u8 bDescriptorType */
|
||||||
50, /* u8 MaxPower; */
|
0x01, 0x00, /* u16 HID_class */
|
||||||
|
0x00, /* u8 country_code */
|
||||||
/* USB 1.1:
|
0x01, /* u8 num_descriptors */
|
||||||
* USB 2.0, single TT organization (mandatory):
|
USB_DT_REPORT, /* u8 type: Report */
|
||||||
* one interface, protocol 0
|
52, 0, /* u16 len */
|
||||||
*
|
},
|
||||||
* USB 2.0, multiple TT organization (optional):
|
},
|
||||||
* two interfaces, protocols 1 (like single TT)
|
},
|
||||||
* and 2 (multiple TT mode) ... config is
|
.eps = (USBDescEndpoint[]) {
|
||||||
* sometimes settable
|
{
|
||||||
* NOT IMPLEMENTED
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
*/
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 4,
|
||||||
/* one interface */
|
.bInterval = 0x0a,
|
||||||
0x09, /* u8 if_bLength; */
|
},
|
||||||
0x04, /* u8 if_bDescriptorType; Interface */
|
},
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
|
||||||
0x01, /* u8 if_bNumEndpoints; */
|
|
||||||
0x03, /* u8 if_bInterfaceClass; */
|
|
||||||
0x01, /* u8 if_bInterfaceSubClass; */
|
|
||||||
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
|
||||||
0x07, /* u8 if_iInterface; */
|
|
||||||
|
|
||||||
/* HID descriptor */
|
|
||||||
0x09, /* u8 bLength; */
|
|
||||||
0x21, /* u8 bDescriptorType; */
|
|
||||||
0x01, 0x00, /* u16 HID_class */
|
|
||||||
0x00, /* u8 country_code */
|
|
||||||
0x01, /* u8 num_descriptors */
|
|
||||||
0x22, /* u8 type; Report */
|
|
||||||
74, 0, /* u16 len */
|
|
||||||
|
|
||||||
/* one endpoint (status change endpoint) */
|
|
||||||
0x07, /* u8 ep_bLength; */
|
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
|
||||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
|
||||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
|
||||||
0x08, 0x00, /* u16 ep_wMaxPacketSize; */
|
|
||||||
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_keyboard_config_descriptor[] = {
|
static const USBDescIface desc_iface_tablet = {
|
||||||
/* one configuration */
|
.bInterfaceNumber = 0,
|
||||||
0x09, /* u8 bLength; */
|
.bNumEndpoints = 1,
|
||||||
USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
0x22, 0x00, /* u16 wTotalLength; */
|
.bInterfaceSubClass = 0x01, /* boot */
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
.bInterfaceProtocol = 0x02,
|
||||||
0x01, /* u8 bConfigurationValue; */
|
.ndesc = 1,
|
||||||
0x06, /* u8 iConfiguration; */
|
.descs = (USBDescOther[]) {
|
||||||
0xa0, /* u8 bmAttributes;
|
{
|
||||||
Bit 7: must be set,
|
/* HID descriptor */
|
||||||
6: Self-powered,
|
.data = (uint8_t[]) {
|
||||||
5: Remote wakeup,
|
0x09, /* u8 bLength */
|
||||||
4..0: resvd */
|
USB_DT_HID, /* u8 bDescriptorType */
|
||||||
0x32, /* u8 MaxPower; */
|
0x01, 0x00, /* u16 HID_class */
|
||||||
|
0x00, /* u8 country_code */
|
||||||
|
0x01, /* u8 num_descriptors */
|
||||||
|
USB_DT_REPORT, /* u8 type: Report */
|
||||||
|
74, 0, /* u16 len */
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 8,
|
||||||
|
.bInterval = 0x0a,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* USB 1.1:
|
static const USBDescIface desc_iface_keyboard = {
|
||||||
* USB 2.0, single TT organization (mandatory):
|
.bInterfaceNumber = 0,
|
||||||
* one interface, protocol 0
|
.bNumEndpoints = 1,
|
||||||
*
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
* USB 2.0, multiple TT organization (optional):
|
.bInterfaceSubClass = 0x01, /* boot */
|
||||||
* two interfaces, protocols 1 (like single TT)
|
.bInterfaceProtocol = 0x01, /* keyboard */
|
||||||
* and 2 (multiple TT mode) ... config is
|
.ndesc = 1,
|
||||||
* sometimes settable
|
.descs = (USBDescOther[]) {
|
||||||
* NOT IMPLEMENTED
|
{
|
||||||
*/
|
/* HID descriptor */
|
||||||
|
.data = (uint8_t[]) {
|
||||||
|
0x09, /* u8 bLength */
|
||||||
|
USB_DT_HID, /* u8 bDescriptorType */
|
||||||
|
0x11, 0x01, /* u16 HID_class */
|
||||||
|
0x00, /* u8 country_code */
|
||||||
|
0x01, /* u8 num_descriptors */
|
||||||
|
USB_DT_REPORT, /* u8 type: Report */
|
||||||
|
0x3f, 0, /* u16 len */
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 8,
|
||||||
|
.bInterval = 0x0a,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* one interface */
|
static const USBDescDevice desc_device_mouse = {
|
||||||
0x09, /* u8 if_bLength; */
|
.bcdUSB = 0x0100,
|
||||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */
|
.bMaxPacketSize0 = 8,
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
.bNumConfigurations = 1,
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
.confs = (USBDescConfig[]) {
|
||||||
0x01, /* u8 if_bNumEndpoints; */
|
{
|
||||||
0x03, /* u8 if_bInterfaceClass; HID */
|
.bNumInterfaces = 1,
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Boot */
|
.bConfigurationValue = 1,
|
||||||
0x01, /* u8 if_bInterfaceProtocol; Keyboard */
|
.iConfiguration = STR_CONFIG_MOUSE,
|
||||||
0x07, /* u8 if_iInterface; */
|
.bmAttributes = 0xa0,
|
||||||
|
.bMaxPower = 50,
|
||||||
|
.ifs = &desc_iface_mouse,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* HID descriptor */
|
static const USBDescDevice desc_device_tablet = {
|
||||||
0x09, /* u8 bLength; */
|
.bcdUSB = 0x0100,
|
||||||
USB_DT_HID, /* u8 bDescriptorType; */
|
.bMaxPacketSize0 = 8,
|
||||||
0x11, 0x01, /* u16 HID_class */
|
.bNumConfigurations = 1,
|
||||||
0x00, /* u8 country_code */
|
.confs = (USBDescConfig[]) {
|
||||||
0x01, /* u8 num_descriptors */
|
{
|
||||||
USB_DT_REPORT, /* u8 type; Report */
|
.bNumInterfaces = 1,
|
||||||
0x3f, 0x00, /* u16 len */
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = STR_CONFIG_TABLET,
|
||||||
|
.bmAttributes = 0xa0,
|
||||||
|
.bMaxPower = 50,
|
||||||
|
.ifs = &desc_iface_tablet,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* one endpoint (status change endpoint) */
|
static const USBDescDevice desc_device_keyboard = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.bcdUSB = 0x0100,
|
||||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */
|
.bMaxPacketSize0 = 8,
|
||||||
USB_DIR_IN | 0x01, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
.bNumConfigurations = 1,
|
||||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
.confs = (USBDescConfig[]) {
|
||||||
0x08, 0x00, /* u16 ep_wMaxPacketSize; */
|
{
|
||||||
0x0a, /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
.bNumInterfaces = 1,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = STR_CONFIG_KEYBOARD,
|
||||||
|
.bmAttributes = 0xa0,
|
||||||
|
.bMaxPower = 50,
|
||||||
|
.ifs = &desc_iface_keyboard,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc_mouse = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = 0x0627,
|
||||||
|
.idProduct = 0x0001,
|
||||||
|
.bcdDevice = 0,
|
||||||
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
|
.iProduct = STR_PRODUCT_MOUSE,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_mouse,
|
||||||
|
.str = desc_strings,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc_tablet = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = 0x0627,
|
||||||
|
.idProduct = 0x0001,
|
||||||
|
.bcdDevice = 0,
|
||||||
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
|
.iProduct = STR_PRODUCT_TABLET,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_tablet,
|
||||||
|
.str = desc_strings,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc_keyboard = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = 0x0627,
|
||||||
|
.idProduct = 0x0001,
|
||||||
|
.bcdDevice = 0,
|
||||||
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
|
.iProduct = STR_PRODUCT_KEYBOARD,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_keyboard,
|
||||||
|
.str = desc_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
|
static const uint8_t qemu_mouse_hid_report_descriptor[] = {
|
||||||
|
@ -412,6 +429,8 @@ static void usb_hid_changed(USBHIDState *hs)
|
||||||
|
|
||||||
if (hs->datain)
|
if (hs->datain)
|
||||||
hs->datain(hs->datain_opaque);
|
hs->datain(hs->datain_opaque);
|
||||||
|
|
||||||
|
usb_wakeup(&hs->dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_mouse_event(void *opaque,
|
static void usb_mouse_event(void *opaque,
|
||||||
|
@ -650,106 +669,15 @@ static int usb_hid_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
USBHIDState *s = (USBHIDState *)dev;
|
USBHIDState *s = (USBHIDState *)dev;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch(request) {
|
switch(request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch(value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
memcpy(data, qemu_mouse_dev_descriptor,
|
|
||||||
sizeof(qemu_mouse_dev_descriptor));
|
|
||||||
ret = sizeof(qemu_mouse_dev_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
if (s->kind == USB_MOUSE) {
|
|
||||||
memcpy(data, qemu_mouse_config_descriptor,
|
|
||||||
sizeof(qemu_mouse_config_descriptor));
|
|
||||||
ret = sizeof(qemu_mouse_config_descriptor);
|
|
||||||
} else if (s->kind == USB_TABLET) {
|
|
||||||
memcpy(data, qemu_tablet_config_descriptor,
|
|
||||||
sizeof(qemu_tablet_config_descriptor));
|
|
||||||
ret = sizeof(qemu_tablet_config_descriptor);
|
|
||||||
} else if (s->kind == USB_KEYBOARD) {
|
|
||||||
memcpy(data, qemu_keyboard_config_descriptor,
|
|
||||||
sizeof(qemu_keyboard_config_descriptor));
|
|
||||||
ret = sizeof(qemu_keyboard_config_descriptor);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch(value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* serial number */
|
|
||||||
ret = set_usb_string(data, "1");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* product description */
|
|
||||||
ret = set_usb_string(data, s->dev.product_desc);
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* vendor description */
|
|
||||||
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
ret = set_usb_string(data, "HID Mouse");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
ret = set_usb_string(data, "HID Tablet");
|
|
||||||
break;
|
|
||||||
case 6:
|
|
||||||
ret = set_usb_string(data, "HID Keyboard");
|
|
||||||
break;
|
|
||||||
case 7:
|
|
||||||
ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = 1;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -870,7 +798,8 @@ static void usb_hid_handle_destroy(USBDevice *dev)
|
||||||
static int usb_hid_initfn(USBDevice *dev, int kind)
|
static int usb_hid_initfn(USBDevice *dev, int kind)
|
||||||
{
|
{
|
||||||
USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
|
USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
|
||||||
|
usb_desc_init(dev);
|
||||||
s->kind = kind;
|
s->kind = kind;
|
||||||
|
|
||||||
if (s->kind == USB_MOUSE) {
|
if (s->kind == USB_MOUSE) {
|
||||||
|
@ -915,6 +844,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||||
.qdev.name = "usb-tablet",
|
.qdev.name = "usb-tablet",
|
||||||
.usbdevice_name = "tablet",
|
.usbdevice_name = "tablet",
|
||||||
.qdev.size = sizeof(USBHIDState),
|
.qdev.size = sizeof(USBHIDState),
|
||||||
|
.usb_desc = &desc_tablet,
|
||||||
.init = usb_tablet_initfn,
|
.init = usb_tablet_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_mouse_handle_reset,
|
.handle_reset = usb_mouse_handle_reset,
|
||||||
|
@ -926,6 +856,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||||
.qdev.name = "usb-mouse",
|
.qdev.name = "usb-mouse",
|
||||||
.usbdevice_name = "mouse",
|
.usbdevice_name = "mouse",
|
||||||
.qdev.size = sizeof(USBHIDState),
|
.qdev.size = sizeof(USBHIDState),
|
||||||
|
.usb_desc = &desc_mouse,
|
||||||
.init = usb_mouse_initfn,
|
.init = usb_mouse_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_mouse_handle_reset,
|
.handle_reset = usb_mouse_handle_reset,
|
||||||
|
@ -937,6 +868,7 @@ static struct USBDeviceInfo hid_info[] = {
|
||||||
.qdev.name = "usb-kbd",
|
.qdev.name = "usb-kbd",
|
||||||
.usbdevice_name = "keyboard",
|
.usbdevice_name = "keyboard",
|
||||||
.qdev.size = sizeof(USBHIDState),
|
.qdev.size = sizeof(USBHIDState),
|
||||||
|
.usb_desc = &desc_keyboard,
|
||||||
.init = usb_keyboard_initfn,
|
.init = usb_keyboard_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_keyboard_handle_reset,
|
.handle_reset = usb_keyboard_handle_reset,
|
||||||
|
|
250
hw/usb-hub.c
250
hw/usb-hub.c
|
@ -23,10 +23,11 @@
|
||||||
*/
|
*/
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
|
|
||||||
//#define DEBUG
|
//#define DEBUG
|
||||||
|
|
||||||
#define MAX_PORTS 8
|
#define NUM_PORTS 8
|
||||||
|
|
||||||
typedef struct USBHubPort {
|
typedef struct USBHubPort {
|
||||||
USBPort port;
|
USBPort port;
|
||||||
|
@ -36,8 +37,7 @@ typedef struct USBHubPort {
|
||||||
|
|
||||||
typedef struct USBHubState {
|
typedef struct USBHubState {
|
||||||
USBDevice dev;
|
USBDevice dev;
|
||||||
int nb_ports;
|
USBHubPort ports[NUM_PORTS];
|
||||||
USBHubPort ports[MAX_PORTS];
|
|
||||||
} USBHubState;
|
} USBHubState;
|
||||||
|
|
||||||
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
|
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
|
||||||
|
@ -83,6 +83,60 @@ typedef struct USBHubState {
|
||||||
|
|
||||||
/* same as Linux kernel root hubs */
|
/* same as Linux kernel root hubs */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
STR_MANUFACTURER = 1,
|
||||||
|
STR_PRODUCT,
|
||||||
|
STR_SERIALNUMBER,
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDescStrings desc_strings = {
|
||||||
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
|
[STR_PRODUCT] = "QEMU USB Hub",
|
||||||
|
[STR_SERIALNUMBER] = "314159",
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDescIface desc_iface_hub = {
|
||||||
|
.bInterfaceNumber = 0,
|
||||||
|
.bNumEndpoints = 1,
|
||||||
|
.bInterfaceClass = USB_CLASS_HUB,
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8,
|
||||||
|
.bInterval = 0xff,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDescDevice desc_device_hub = {
|
||||||
|
.bcdUSB = 0x0110,
|
||||||
|
.bDeviceClass = USB_CLASS_HUB,
|
||||||
|
.bMaxPacketSize0 = 8,
|
||||||
|
.bNumConfigurations = 1,
|
||||||
|
.confs = (USBDescConfig[]) {
|
||||||
|
{
|
||||||
|
.bNumInterfaces = 1,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = 0xe0,
|
||||||
|
.ifs = &desc_iface_hub,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc_hub = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = 0,
|
||||||
|
.idProduct = 0,
|
||||||
|
.bcdDevice = 0x0101,
|
||||||
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
|
.iProduct = STR_PRODUCT,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_hub,
|
||||||
|
.str = desc_strings,
|
||||||
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_hub_dev_descriptor[] = {
|
static const uint8_t qemu_hub_dev_descriptor[] = {
|
||||||
0x12, /* u8 bLength; */
|
0x12, /* u8 bLength; */
|
||||||
0x01, /* u8 bDescriptorType; Device */
|
0x01, /* u8 bDescriptorType; Device */
|
||||||
|
@ -164,37 +218,51 @@ static const uint8_t qemu_hub_hub_descriptor[] =
|
||||||
/* DeviceRemovable and PortPwrCtrlMask patched in later */
|
/* DeviceRemovable and PortPwrCtrlMask patched in later */
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_hub_attach(USBPort *port1, USBDevice *dev)
|
static void usb_hub_attach(USBPort *port1)
|
||||||
{
|
{
|
||||||
USBHubState *s = port1->opaque;
|
USBHubState *s = port1->opaque;
|
||||||
USBHubPort *port = &s->ports[port1->index];
|
USBHubPort *port = &s->ports[port1->index];
|
||||||
|
|
||||||
if (dev) {
|
port->wPortStatus |= PORT_STAT_CONNECTION;
|
||||||
if (port->port.dev)
|
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||||
usb_attach(port1, NULL);
|
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||||
|
port->wPortStatus |= PORT_STAT_LOW_SPEED;
|
||||||
port->wPortStatus |= PORT_STAT_CONNECTION;
|
|
||||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
|
||||||
if (dev->speed == USB_SPEED_LOW)
|
|
||||||
port->wPortStatus |= PORT_STAT_LOW_SPEED;
|
|
||||||
else
|
|
||||||
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
|
||||||
port->port.dev = dev;
|
|
||||||
/* send the attach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
|
||||||
} else {
|
} else {
|
||||||
dev = port->port.dev;
|
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
||||||
if (dev) {
|
}
|
||||||
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
}
|
||||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
|
||||||
if (port->wPortStatus & PORT_STAT_ENABLE) {
|
static void usb_hub_detach(USBPort *port1)
|
||||||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
{
|
||||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
USBHubState *s = port1->opaque;
|
||||||
}
|
USBHubPort *port = &s->ports[port1->index];
|
||||||
/* send the detach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_DETACH);
|
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
||||||
port->port.dev = NULL;
|
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||||
}
|
if (port->wPortStatus & PORT_STAT_ENABLE) {
|
||||||
|
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||||
|
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_hub_wakeup(USBDevice *dev)
|
||||||
|
{
|
||||||
|
USBHubState *s = dev->port->opaque;
|
||||||
|
USBHubPort *port = &s->ports[dev->port->index];
|
||||||
|
|
||||||
|
if (port->wPortStatus & PORT_STAT_SUSPEND) {
|
||||||
|
port->wPortChange |= PORT_STAT_C_SUSPEND;
|
||||||
|
usb_wakeup(&s->dev);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_hub_handle_attach(USBDevice *dev)
|
||||||
|
{
|
||||||
|
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
|
||||||
|
int i;
|
||||||
|
|
||||||
|
for (i = 0; i < NUM_PORTS; i++) {
|
||||||
|
usb_port_location(&s->ports[i].port, dev->port, i+1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -209,93 +277,18 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
USBHubState *s = (USBHubState *)dev;
|
USBHubState *s = (USBHubState *)dev;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
switch(request) {
|
switch(request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||||
if (value == 0 && index != 0x81) { /* clear ep halt */
|
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||||
goto fail;
|
goto fail;
|
||||||
}
|
}
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch(value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
memcpy(data, qemu_hub_dev_descriptor,
|
|
||||||
sizeof(qemu_hub_dev_descriptor));
|
|
||||||
ret = sizeof(qemu_hub_dev_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
memcpy(data, qemu_hub_config_descriptor,
|
|
||||||
sizeof(qemu_hub_config_descriptor));
|
|
||||||
|
|
||||||
/* status change endpoint size based on number
|
|
||||||
* of ports */
|
|
||||||
data[22] = (s->nb_ports + 1 + 7) / 8;
|
|
||||||
|
|
||||||
ret = sizeof(qemu_hub_config_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch(value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* serial number */
|
|
||||||
ret = set_usb_string(data, "314159");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* product description */
|
|
||||||
ret = set_usb_string(data, "QEMU USB Hub");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* vendor description */
|
|
||||||
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = 1;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -315,8 +308,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
{
|
{
|
||||||
unsigned int n = index - 1;
|
unsigned int n = index - 1;
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
if (n >= s->nb_ports)
|
if (n >= NUM_PORTS) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
port = &s->ports[n];
|
port = &s->ports[n];
|
||||||
data[0] = port->wPortStatus;
|
data[0] = port->wPortStatus;
|
||||||
data[1] = port->wPortStatus >> 8;
|
data[1] = port->wPortStatus >> 8;
|
||||||
|
@ -338,8 +332,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
unsigned int n = index - 1;
|
unsigned int n = index - 1;
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
if (n >= s->nb_ports)
|
if (n >= NUM_PORTS) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
port = &s->ports[n];
|
port = &s->ports[n];
|
||||||
dev = port->port.dev;
|
dev = port->port.dev;
|
||||||
switch(value) {
|
switch(value) {
|
||||||
|
@ -367,8 +362,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
unsigned int n = index - 1;
|
unsigned int n = index - 1;
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
|
|
||||||
if (n >= s->nb_ports)
|
if (n >= NUM_PORTS) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
port = &s->ports[n];
|
port = &s->ports[n];
|
||||||
switch(value) {
|
switch(value) {
|
||||||
case PORT_ENABLE:
|
case PORT_ENABLE:
|
||||||
|
@ -403,17 +399,17 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||||
unsigned int n, limit, var_hub_size = 0;
|
unsigned int n, limit, var_hub_size = 0;
|
||||||
memcpy(data, qemu_hub_hub_descriptor,
|
memcpy(data, qemu_hub_hub_descriptor,
|
||||||
sizeof(qemu_hub_hub_descriptor));
|
sizeof(qemu_hub_hub_descriptor));
|
||||||
data[2] = s->nb_ports;
|
data[2] = NUM_PORTS;
|
||||||
|
|
||||||
/* fill DeviceRemovable bits */
|
/* fill DeviceRemovable bits */
|
||||||
limit = ((s->nb_ports + 1 + 7) / 8) + 7;
|
limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
|
||||||
for (n = 7; n < limit; n++) {
|
for (n = 7; n < limit; n++) {
|
||||||
data[n] = 0x00;
|
data[n] = 0x00;
|
||||||
var_hub_size++;
|
var_hub_size++;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* fill PortPwrCtrlMask bits */
|
/* fill PortPwrCtrlMask bits */
|
||||||
limit = limit + ((s->nb_ports + 7) / 8);
|
limit = limit + ((NUM_PORTS + 7) / 8);
|
||||||
for (;n < limit; n++) {
|
for (;n < limit; n++) {
|
||||||
data[n] = 0xff;
|
data[n] = 0xff;
|
||||||
var_hub_size++;
|
var_hub_size++;
|
||||||
|
@ -442,14 +438,14 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
unsigned int status;
|
unsigned int status;
|
||||||
int i, n;
|
int i, n;
|
||||||
n = (s->nb_ports + 1 + 7) / 8;
|
n = (NUM_PORTS + 1 + 7) / 8;
|
||||||
if (p->len == 1) { /* FreeBSD workaround */
|
if (p->len == 1) { /* FreeBSD workaround */
|
||||||
n = 1;
|
n = 1;
|
||||||
} else if (n > p->len) {
|
} else if (n > p->len) {
|
||||||
return USB_RET_BABBLE;
|
return USB_RET_BABBLE;
|
||||||
}
|
}
|
||||||
status = 0;
|
status = 0;
|
||||||
for(i = 0; i < s->nb_ports; i++) {
|
for(i = 0; i < NUM_PORTS; i++) {
|
||||||
port = &s->ports[i];
|
port = &s->ports[i];
|
||||||
if (port->wPortChange)
|
if (port->wPortChange)
|
||||||
status |= (1 << (i + 1));
|
status |= (1 << (i + 1));
|
||||||
|
@ -481,7 +477,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
int i, ret;
|
int i, ret;
|
||||||
|
|
||||||
for(i = 0; i < s->nb_ports; i++) {
|
for(i = 0; i < NUM_PORTS; i++) {
|
||||||
port = &s->ports[i];
|
port = &s->ports[i];
|
||||||
dev = port->port.dev;
|
dev = port->port.dev;
|
||||||
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||||
|
@ -518,24 +514,30 @@ static void usb_hub_handle_destroy(USBDevice *dev)
|
||||||
USBHubState *s = (USBHubState *)dev;
|
USBHubState *s = (USBHubState *)dev;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
for (i = 0; i < s->nb_ports; i++) {
|
for (i = 0; i < NUM_PORTS; i++) {
|
||||||
usb_unregister_port(usb_bus_from_device(dev),
|
usb_unregister_port(usb_bus_from_device(dev),
|
||||||
&s->ports[i].port);
|
&s->ports[i].port);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static USBPortOps usb_hub_port_ops = {
|
||||||
|
.attach = usb_hub_attach,
|
||||||
|
.detach = usb_hub_detach,
|
||||||
|
.wakeup = usb_hub_wakeup,
|
||||||
|
};
|
||||||
|
|
||||||
static int usb_hub_initfn(USBDevice *dev)
|
static int usb_hub_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
|
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
|
||||||
USBHubPort *port;
|
USBHubPort *port;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
s->dev.speed = USB_SPEED_FULL,
|
usb_desc_init(dev);
|
||||||
s->nb_ports = MAX_PORTS; /* FIXME: make configurable */
|
for (i = 0; i < NUM_PORTS; i++) {
|
||||||
for (i = 0; i < s->nb_ports; i++) {
|
|
||||||
port = &s->ports[i];
|
port = &s->ports[i];
|
||||||
usb_register_port(usb_bus_from_device(dev),
|
usb_register_port(usb_bus_from_device(dev),
|
||||||
&port->port, s, i, &s->dev, usb_hub_attach);
|
&port->port, s, i, &usb_hub_port_ops,
|
||||||
|
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||||
port->wPortStatus = PORT_STAT_POWER;
|
port->wPortStatus = PORT_STAT_POWER;
|
||||||
port->wPortChange = 0;
|
port->wPortChange = 0;
|
||||||
}
|
}
|
||||||
|
@ -547,8 +549,10 @@ static struct USBDeviceInfo hub_info = {
|
||||||
.qdev.name = "usb-hub",
|
.qdev.name = "usb-hub",
|
||||||
.qdev.fw_name = "hub",
|
.qdev.fw_name = "hub",
|
||||||
.qdev.size = sizeof(USBHubState),
|
.qdev.size = sizeof(USBHubState),
|
||||||
|
.usb_desc = &desc_hub,
|
||||||
.init = usb_hub_initfn,
|
.init = usb_hub_initfn,
|
||||||
.handle_packet = usb_hub_handle_packet,
|
.handle_packet = usb_hub_handle_packet,
|
||||||
|
.handle_attach = usb_hub_handle_attach,
|
||||||
.handle_reset = usb_hub_handle_reset,
|
.handle_reset = usb_hub_handle_reset,
|
||||||
.handle_control = usb_hub_handle_control,
|
.handle_control = usb_hub_handle_control,
|
||||||
.handle_data = usb_hub_handle_data,
|
.handle_data = usb_hub_handle_data,
|
||||||
|
|
259
hw/usb-msd.c
259
hw/usb-msd.c
|
@ -11,6 +11,7 @@
|
||||||
#include "qemu-option.h"
|
#include "qemu-option.h"
|
||||||
#include "qemu-config.h"
|
#include "qemu-config.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
#include "scsi.h"
|
#include "scsi.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "monitor.h"
|
#include "monitor.h"
|
||||||
|
@ -72,69 +73,102 @@ struct usb_msd_csw {
|
||||||
uint8_t status;
|
uint8_t status;
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_msd_dev_descriptor[] = {
|
enum {
|
||||||
0x12, /* u8 bLength; */
|
STR_MANUFACTURER = 1,
|
||||||
0x01, /* u8 bDescriptorType; Device */
|
STR_PRODUCT,
|
||||||
0x00, 0x01, /* u16 bcdUSB; v1.0 */
|
STR_SERIALNUMBER,
|
||||||
|
STR_CONFIG_FULL,
|
||||||
0x00, /* u8 bDeviceClass; */
|
STR_CONFIG_HIGH,
|
||||||
0x00, /* u8 bDeviceSubClass; */
|
|
||||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
|
||||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
|
||||||
|
|
||||||
/* Vendor and product id are arbitrary. */
|
|
||||||
0x00, 0x00, /* u16 idVendor; */
|
|
||||||
0x00, 0x00, /* u16 idProduct; */
|
|
||||||
0x00, 0x00, /* u16 bcdDevice */
|
|
||||||
|
|
||||||
0x01, /* u8 iManufacturer; */
|
|
||||||
0x02, /* u8 iProduct; */
|
|
||||||
0x03, /* u8 iSerialNumber; */
|
|
||||||
0x01 /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_msd_config_descriptor[] = {
|
static const USBDescStrings desc_strings = {
|
||||||
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
|
[STR_PRODUCT] = "QEMU USB HARDDRIVE",
|
||||||
|
[STR_SERIALNUMBER] = "1",
|
||||||
|
[STR_CONFIG_FULL] = "Full speed config (usb 1.1)",
|
||||||
|
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
|
||||||
|
};
|
||||||
|
|
||||||
/* one configuration */
|
static const USBDescIface desc_iface_full = {
|
||||||
0x09, /* u8 bLength; */
|
.bInterfaceNumber = 0,
|
||||||
0x02, /* u8 bDescriptorType; Configuration */
|
.bNumEndpoints = 2,
|
||||||
0x20, 0x00, /* u16 wTotalLength; */
|
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
.bInterfaceSubClass = 0x06, /* SCSI */
|
||||||
0x01, /* u8 bConfigurationValue; */
|
.bInterfaceProtocol = 0x50, /* Bulk */
|
||||||
0x00, /* u8 iConfiguration; */
|
.eps = (USBDescEndpoint[]) {
|
||||||
0xc0, /* u8 bmAttributes;
|
{
|
||||||
Bit 7: must be set,
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
6: Self-powered,
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
5: Remote wakeup,
|
.wMaxPacketSize = 64,
|
||||||
4..0: resvd */
|
},{
|
||||||
0x00, /* u8 MaxPower; */
|
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* one interface */
|
static const USBDescDevice desc_device_full = {
|
||||||
0x09, /* u8 if_bLength; */
|
.bcdUSB = 0x0200,
|
||||||
0x04, /* u8 if_bDescriptorType; Interface */
|
.bMaxPacketSize0 = 8,
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
.bNumConfigurations = 1,
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
.confs = (USBDescConfig[]) {
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
{
|
||||||
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
|
.bNumInterfaces = 1,
|
||||||
0x06, /* u8 if_bInterfaceSubClass; SCSI */
|
.bConfigurationValue = 1,
|
||||||
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
|
.iConfiguration = STR_CONFIG_FULL,
|
||||||
0x00, /* u8 if_iInterface; */
|
.bmAttributes = 0xc0,
|
||||||
|
.ifs = &desc_iface_full,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* Bulk-In endpoint */
|
static const USBDescIface desc_iface_high = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.bInterfaceNumber = 0,
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
.bNumEndpoints = 2,
|
||||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
.bInterfaceSubClass = 0x06, /* SCSI */
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
.bInterfaceProtocol = 0x50, /* Bulk */
|
||||||
0x00, /* u8 ep_bInterval; */
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 512,
|
||||||
|
},{
|
||||||
|
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 512,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* Bulk-Out endpoint */
|
static const USBDescDevice desc_device_high = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.bcdUSB = 0x0200,
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
.bMaxPacketSize0 = 64,
|
||||||
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
.bNumConfigurations = 1,
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
.confs = (USBDescConfig[]) {
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
{
|
||||||
0x00 /* u8 ep_bInterval; */
|
.bNumInterfaces = 1,
|
||||||
|
.bConfigurationValue = 1,
|
||||||
|
.iConfiguration = STR_CONFIG_HIGH,
|
||||||
|
.bmAttributes = 0xc0,
|
||||||
|
.ifs = &desc_iface_high,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = 0,
|
||||||
|
.idProduct = 0,
|
||||||
|
.bcdDevice = 0,
|
||||||
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
|
.iProduct = STR_PRODUCT,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_full,
|
||||||
|
.high = &desc_device_high,
|
||||||
|
.str = desc_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_msd_copy_data(MSDState *s)
|
static void usb_msd_copy_data(MSDState *s)
|
||||||
|
@ -153,7 +187,7 @@ static void usb_msd_copy_data(MSDState *s)
|
||||||
s->usb_buf += len;
|
s->usb_buf += len;
|
||||||
s->scsi_buf += len;
|
s->scsi_buf += len;
|
||||||
s->data_len -= len;
|
s->data_len -= len;
|
||||||
if (s->scsi_len == 0) {
|
if (s->scsi_len == 0 || s->data_len == 0) {
|
||||||
if (s->mode == USB_MSDM_DATAIN) {
|
if (s->mode == USB_MSDM_DATAIN) {
|
||||||
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
||||||
} else if (s->mode == USB_MSDM_DATAOUT) {
|
} else if (s->mode == USB_MSDM_DATAOUT) {
|
||||||
|
@ -162,15 +196,18 @@ static void usb_msd_copy_data(MSDState *s)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_send_status(MSDState *s)
|
static void usb_msd_send_status(MSDState *s, USBPacket *p)
|
||||||
{
|
{
|
||||||
struct usb_msd_csw csw;
|
struct usb_msd_csw csw;
|
||||||
|
int len;
|
||||||
|
|
||||||
csw.sig = cpu_to_le32(0x53425355);
|
csw.sig = cpu_to_le32(0x53425355);
|
||||||
csw.tag = cpu_to_le32(s->tag);
|
csw.tag = cpu_to_le32(s->tag);
|
||||||
csw.residue = s->residue;
|
csw.residue = s->residue;
|
||||||
csw.status = s->result;
|
csw.status = s->result;
|
||||||
memcpy(s->usb_buf, &csw, 13);
|
|
||||||
|
len = MIN(sizeof(csw), p->len);
|
||||||
|
memcpy(p->data, &csw, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||||
|
@ -190,7 +227,7 @@ static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag,
|
||||||
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
|
if (s->data_len == 0 && s->mode == USB_MSDM_DATAOUT) {
|
||||||
/* A deferred packet with no write data remaining must be
|
/* A deferred packet with no write data remaining must be
|
||||||
the status read packet. */
|
the status read packet. */
|
||||||
usb_msd_send_status(s);
|
usb_msd_send_status(s, p);
|
||||||
s->mode = USB_MSDM_CBW;
|
s->mode = USB_MSDM_CBW;
|
||||||
} else {
|
} else {
|
||||||
if (s->data_len) {
|
if (s->data_len) {
|
||||||
|
@ -236,84 +273,15 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
MSDState *s = (MSDState *)dev;
|
MSDState *s = (MSDState *)dev;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch(value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
memcpy(data, qemu_msd_dev_descriptor,
|
|
||||||
sizeof(qemu_msd_dev_descriptor));
|
|
||||||
ret = sizeof(qemu_msd_dev_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
memcpy(data, qemu_msd_config_descriptor,
|
|
||||||
sizeof(qemu_msd_config_descriptor));
|
|
||||||
ret = sizeof(qemu_msd_config_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch(value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* vendor description */
|
|
||||||
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* product description */
|
|
||||||
ret = set_usb_string(data, "QEMU USB HARDDRIVE");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* serial number */
|
|
||||||
ret = set_usb_string(data, "1");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = 1;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -338,7 +306,6 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||||
ret = 1;
|
ret = 1;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fail:
|
|
||||||
ret = USB_RET_STALL;
|
ret = USB_RET_STALL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -461,15 +428,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||||
if (len < 13)
|
if (len < 13)
|
||||||
goto fail;
|
goto fail;
|
||||||
|
|
||||||
s->usb_len = len;
|
usb_msd_send_status(s, p);
|
||||||
s->usb_buf = data;
|
|
||||||
usb_msd_send_status(s);
|
|
||||||
s->mode = USB_MSDM_CBW;
|
s->mode = USB_MSDM_CBW;
|
||||||
ret = 13;
|
ret = 13;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case USB_MSDM_DATAIN:
|
case USB_MSDM_DATAIN:
|
||||||
DPRINTF("Data in %d/%d\n", len, s->data_len);
|
DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
|
||||||
if (len > s->data_len)
|
if (len > s->data_len)
|
||||||
len = s->data_len;
|
len = s->data_len;
|
||||||
s->usb_buf = data;
|
s->usb_buf = data;
|
||||||
|
@ -524,6 +489,7 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||||
BlockDriverState *bs = s->conf.bs;
|
BlockDriverState *bs = s->conf.bs;
|
||||||
|
DriveInfo *dinfo;
|
||||||
|
|
||||||
if (!bs) {
|
if (!bs) {
|
||||||
error_report("usb-msd: drive property not set");
|
error_report("usb-msd: drive property not set");
|
||||||
|
@ -542,7 +508,12 @@ static int usb_msd_initfn(USBDevice *dev)
|
||||||
bdrv_detach(bs, &s->dev.qdev);
|
bdrv_detach(bs, &s->dev.qdev);
|
||||||
s->conf.bs = NULL;
|
s->conf.bs = NULL;
|
||||||
|
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
dinfo = drive_get_by_blockdev(bs);
|
||||||
|
if (dinfo && dinfo->serial) {
|
||||||
|
usb_desc_set_string(dev, STR_SERIALNUMBER, dinfo->serial);
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_desc_init(dev);
|
||||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
|
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
|
||||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0);
|
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0);
|
||||||
if (!s->scsi_dev) {
|
if (!s->scsi_dev) {
|
||||||
|
@ -625,8 +596,10 @@ static struct USBDeviceInfo msd_info = {
|
||||||
.product_desc = "QEMU USB MSD",
|
.product_desc = "QEMU USB MSD",
|
||||||
.qdev.name = "usb-storage",
|
.qdev.name = "usb-storage",
|
||||||
.qdev.size = sizeof(MSDState),
|
.qdev.size = sizeof(MSDState),
|
||||||
|
.usb_desc = &desc,
|
||||||
.init = usb_msd_initfn,
|
.init = usb_msd_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
|
.handle_attach = usb_desc_attach,
|
||||||
.handle_reset = usb_msd_handle_reset,
|
.handle_reset = usb_msd_handle_reset,
|
||||||
.handle_control = usb_msd_handle_control,
|
.handle_control = usb_msd_handle_control,
|
||||||
.handle_data = usb_msd_handle_data,
|
.handle_data = usb_msd_handle_data,
|
||||||
|
|
|
@ -259,7 +259,13 @@
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static void musb_attach(USBPort *port, USBDevice *dev);
|
static void musb_attach(USBPort *port);
|
||||||
|
static void musb_detach(USBPort *port);
|
||||||
|
|
||||||
|
static USBPortOps musb_port_ops = {
|
||||||
|
.attach = musb_attach,
|
||||||
|
.detach = musb_detach,
|
||||||
|
};
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
uint16_t faddr[2];
|
uint16_t faddr[2];
|
||||||
|
@ -343,7 +349,9 @@ struct MUSBState {
|
||||||
}
|
}
|
||||||
|
|
||||||
usb_bus_new(&s->bus, NULL /* FIXME */);
|
usb_bus_new(&s->bus, NULL /* FIXME */);
|
||||||
usb_register_port(&s->bus, &s->port, s, 0, NULL, musb_attach);
|
usb_register_port(&s->bus, &s->port, s, 0, &musb_port_ops,
|
||||||
|
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||||
|
usb_port_location(&s->port, NULL, 1);
|
||||||
|
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
@ -460,34 +468,20 @@ static void musb_session_update(MUSBState *s, int prev_dev, int prev_sess)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach or detach a device on our only port. */
|
/* Attach or detach a device on our only port. */
|
||||||
static void musb_attach(USBPort *port, USBDevice *dev)
|
static void musb_attach(USBPort *port)
|
||||||
{
|
{
|
||||||
MUSBState *s = (MUSBState *) port->opaque;
|
MUSBState *s = (MUSBState *) port->opaque;
|
||||||
USBDevice *curr;
|
|
||||||
|
|
||||||
port = &s->port;
|
musb_intr_set(s, musb_irq_vbus_request, 1);
|
||||||
curr = port->dev;
|
musb_session_update(s, 0, s->session);
|
||||||
|
}
|
||||||
|
|
||||||
if (dev) {
|
static void musb_detach(USBPort *port)
|
||||||
if (curr) {
|
{
|
||||||
usb_attach(port, NULL);
|
MUSBState *s = (MUSBState *) port->opaque;
|
||||||
/* TODO: signal some interrupts */
|
|
||||||
}
|
|
||||||
|
|
||||||
musb_intr_set(s, musb_irq_vbus_request, 1);
|
musb_intr_set(s, musb_irq_disconnect, 1);
|
||||||
|
musb_session_update(s, 1, s->session);
|
||||||
/* Send the attach message to device */
|
|
||||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
|
||||||
} else if (curr) {
|
|
||||||
/* Send the detach message */
|
|
||||||
usb_send_msg(curr, USB_MSG_DETACH);
|
|
||||||
|
|
||||||
musb_intr_set(s, musb_irq_disconnect, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
port->dev = dev;
|
|
||||||
|
|
||||||
musb_session_update(s, !!curr, s->session);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static inline void musb_cb_tick0(void *opaque)
|
static inline void musb_cb_tick0(void *opaque)
|
||||||
|
|
528
hw/usb-net.c
528
hw/usb-net.c
|
@ -25,6 +25,7 @@
|
||||||
|
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
#include "net.h"
|
#include "net.h"
|
||||||
#include "qemu-queue.h"
|
#include "qemu-queue.h"
|
||||||
#include "sysemu.h"
|
#include "sysemu.h"
|
||||||
|
@ -89,182 +90,209 @@ enum usbstring_idx {
|
||||||
|
|
||||||
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
|
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
|
||||||
|
|
||||||
/*
|
static const USBDescStrings usb_net_stringtable = {
|
||||||
* mostly the same descriptor as the linux gadget rndis driver
|
[STRING_MANUFACTURER] = "QEMU",
|
||||||
*/
|
[STRING_PRODUCT] = "RNDIS/QEMU USB Network Device",
|
||||||
static const uint8_t qemu_net_dev_descriptor[] = {
|
[STRING_ETHADDR] = "400102030405",
|
||||||
0x12, /* u8 bLength; */
|
[STRING_DATA] = "QEMU USB Net Data Interface",
|
||||||
USB_DT_DEVICE, /* u8 bDescriptorType; Device */
|
[STRING_CONTROL] = "QEMU USB Net Control Interface",
|
||||||
0x00, 0x02, /* u16 bcdUSB; v2.0 */
|
[STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface",
|
||||||
USB_CLASS_COMM, /* u8 bDeviceClass; */
|
[STRING_CDC] = "QEMU USB Net CDC",
|
||||||
0x00, /* u8 bDeviceSubClass; */
|
[STRING_SUBSET] = "QEMU USB Net Subset",
|
||||||
0x00, /* u8 bDeviceProtocol; [ low/full only ] */
|
[STRING_RNDIS] = "QEMU USB Net RNDIS",
|
||||||
0x40, /* u8 bMaxPacketSize0 */
|
[STRING_SERIALNUMBER] = "1",
|
||||||
RNDIS_VENDOR_NUM & 0xff, RNDIS_VENDOR_NUM >> 8, /* u16 idVendor; */
|
|
||||||
RNDIS_PRODUCT_NUM & 0xff, RNDIS_PRODUCT_NUM >> 8, /* u16 idProduct; */
|
|
||||||
0x00, 0x00, /* u16 bcdDevice */
|
|
||||||
STRING_MANUFACTURER, /* u8 iManufacturer; */
|
|
||||||
STRING_PRODUCT, /* u8 iProduct; */
|
|
||||||
STRING_SERIALNUMBER, /* u8 iSerialNumber; */
|
|
||||||
0x02, /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_net_rndis_config_descriptor[] = {
|
static const USBDescIface desc_iface_rndis[] = {
|
||||||
/* Configuration Descriptor */
|
{
|
||||||
0x09, /* u8 bLength */
|
/* RNDIS Control Interface */
|
||||||
USB_DT_CONFIG, /* u8 bDescriptorType */
|
.bInterfaceNumber = 0,
|
||||||
0x43, 0x00, /* le16 wTotalLength */
|
.bNumEndpoints = 1,
|
||||||
0x02, /* u8 bNumInterfaces */
|
.bInterfaceClass = USB_CLASS_COMM,
|
||||||
DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */
|
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||||
STRING_RNDIS, /* u8 iConfiguration */
|
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
|
||||||
0xc0, /* u8 bmAttributes */
|
.iInterface = STRING_RNDIS_CONTROL,
|
||||||
0x32, /* u8 bMaxPower */
|
.ndesc = 4,
|
||||||
/* RNDIS Control Interface */
|
.descs = (USBDescOther[]) {
|
||||||
0x09, /* u8 bLength */
|
{
|
||||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
/* Header Descriptor */
|
||||||
0x00, /* u8 bInterfaceNumber */
|
.data = (uint8_t[]) {
|
||||||
0x00, /* u8 bAlternateSetting */
|
0x05, /* u8 bLength */
|
||||||
0x01, /* u8 bNumEndpoints */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
USB_CLASS_COMM, /* u8 bInterfaceClass */
|
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||||
USB_CDC_SUBCLASS_ACM, /* u8 bInterfaceSubClass */
|
0x10, 0x01, /* le16 bcdCDC */
|
||||||
USB_CDC_ACM_PROTO_VENDOR, /* u8 bInterfaceProtocol */
|
},
|
||||||
STRING_RNDIS_CONTROL, /* u8 iInterface */
|
},{
|
||||||
/* Header Descriptor */
|
/* Call Management Descriptor */
|
||||||
0x05, /* u8 bLength */
|
.data = (uint8_t[]) {
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
0x05, /* u8 bLength */
|
||||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
0x10, 0x01, /* le16 bcdCDC */
|
USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */
|
||||||
/* Call Management Descriptor */
|
0x00, /* u8 bmCapabilities */
|
||||||
0x05, /* u8 bLength */
|
0x01, /* u8 bDataInterface */
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
},
|
||||||
USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */
|
},{
|
||||||
0x00, /* u8 bmCapabilities */
|
/* ACM Descriptor */
|
||||||
0x01, /* u8 bDataInterface */
|
.data = (uint8_t[]) {
|
||||||
/* ACM Descriptor */
|
0x04, /* u8 bLength */
|
||||||
0x04, /* u8 bLength */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */
|
||||||
USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */
|
0x00, /* u8 bmCapabilities */
|
||||||
0x00, /* u8 bmCapabilities */
|
},
|
||||||
/* Union Descriptor */
|
},{
|
||||||
0x05, /* u8 bLength */
|
/* Union Descriptor */
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
.data = (uint8_t[]) {
|
||||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
0x05, /* u8 bLength */
|
||||||
0x00, /* u8 bMasterInterface0 */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
0x01, /* u8 bSlaveInterface0 */
|
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||||
/* Status Descriptor */
|
0x00, /* u8 bMasterInterface0 */
|
||||||
0x07, /* u8 bLength */
|
0x01, /* u8 bSlaveInterface0 */
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
},
|
||||||
USB_DIR_IN | 1, /* u8 bEndpointAddress */
|
},
|
||||||
USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */
|
},
|
||||||
STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */
|
.eps = (USBDescEndpoint[]) {
|
||||||
1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */
|
{
|
||||||
/* RNDIS Data Interface */
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
0x09, /* u8 bLength */
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
.wMaxPacketSize = STATUS_BYTECOUNT,
|
||||||
0x01, /* u8 bInterfaceNumber */
|
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||||
0x00, /* u8 bAlternateSetting */
|
},
|
||||||
0x02, /* u8 bNumEndpoints */
|
}
|
||||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
},{
|
||||||
0x00, /* u8 bInterfaceSubClass */
|
/* RNDIS Data Interface */
|
||||||
0x00, /* u8 bInterfaceProtocol */
|
.bInterfaceNumber = 1,
|
||||||
STRING_DATA, /* u8 iInterface */
|
.bNumEndpoints = 2,
|
||||||
/* Source Endpoint */
|
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||||
0x07, /* u8 bLength */
|
.iInterface = STRING_DATA,
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
.eps = (USBDescEndpoint[]) {
|
||||||
USB_DIR_IN | 2, /* u8 bEndpointAddress */
|
{
|
||||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
0x00, /* u8 bInterval */
|
.wMaxPacketSize = 0x40,
|
||||||
/* Sink Endpoint */
|
},{
|
||||||
0x07, /* u8 bLength */
|
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
USB_DIR_OUT | 2, /* u8 bEndpointAddress */
|
.wMaxPacketSize = 0x40,
|
||||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
}
|
||||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
}
|
||||||
0x00 /* u8 bInterval */
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_net_cdc_config_descriptor[] = {
|
static const USBDescIface desc_iface_cdc[] = {
|
||||||
/* Configuration Descriptor */
|
{
|
||||||
0x09, /* u8 bLength */
|
/* CDC Control Interface */
|
||||||
USB_DT_CONFIG, /* u8 bDescriptorType */
|
.bInterfaceNumber = 0,
|
||||||
0x50, 0x00, /* le16 wTotalLength */
|
.bNumEndpoints = 1,
|
||||||
0x02, /* u8 bNumInterfaces */
|
.bInterfaceClass = USB_CLASS_COMM,
|
||||||
DEV_CONFIG_VALUE, /* u8 bConfigurationValue */
|
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
|
||||||
STRING_CDC, /* u8 iConfiguration */
|
.bInterfaceProtocol = USB_CDC_PROTO_NONE,
|
||||||
0xc0, /* u8 bmAttributes */
|
.iInterface = STRING_CONTROL,
|
||||||
0x32, /* u8 bMaxPower */
|
.ndesc = 3,
|
||||||
/* CDC Control Interface */
|
.descs = (USBDescOther[]) {
|
||||||
0x09, /* u8 bLength */
|
{
|
||||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
/* Header Descriptor */
|
||||||
0x00, /* u8 bInterfaceNumber */
|
.data = (uint8_t[]) {
|
||||||
0x00, /* u8 bAlternateSetting */
|
0x05, /* u8 bLength */
|
||||||
0x01, /* u8 bNumEndpoints */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
USB_CLASS_COMM, /* u8 bInterfaceClass */
|
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||||
USB_CDC_SUBCLASS_ETHERNET, /* u8 bInterfaceSubClass */
|
0x10, 0x01, /* le16 bcdCDC */
|
||||||
USB_CDC_PROTO_NONE, /* u8 bInterfaceProtocol */
|
},
|
||||||
STRING_CONTROL, /* u8 iInterface */
|
},{
|
||||||
/* Header Descriptor */
|
/* Union Descriptor */
|
||||||
0x05, /* u8 bLength */
|
.data = (uint8_t[]) {
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
0x05, /* u8 bLength */
|
||||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
0x10, 0x01, /* le16 bcdCDC */
|
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||||
/* Union Descriptor */
|
0x00, /* u8 bMasterInterface0 */
|
||||||
0x05, /* u8 bLength */
|
0x01, /* u8 bSlaveInterface0 */
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
},
|
||||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
},{
|
||||||
0x00, /* u8 bMasterInterface0 */
|
/* Ethernet Descriptor */
|
||||||
0x01, /* u8 bSlaveInterface0 */
|
.data = (uint8_t[]) {
|
||||||
/* Ethernet Descriptor */
|
0x0d, /* u8 bLength */
|
||||||
0x0d, /* u8 bLength */
|
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */
|
||||||
USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */
|
STRING_ETHADDR, /* u8 iMACAddress */
|
||||||
STRING_ETHADDR, /* u8 iMACAddress */
|
0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */
|
||||||
0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */
|
ETH_FRAME_LEN & 0xff,
|
||||||
ETH_FRAME_LEN & 0xff, ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */
|
ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */
|
||||||
0x00, 0x00, /* le16 wNumberMCFilters */
|
0x00, 0x00, /* le16 wNumberMCFilters */
|
||||||
0x00, /* u8 bNumberPowerFilters */
|
0x00, /* u8 bNumberPowerFilters */
|
||||||
/* Status Descriptor */
|
},
|
||||||
0x07, /* u8 bLength */
|
},
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
},
|
||||||
USB_DIR_IN | 1, /* u8 bEndpointAddress */
|
.eps = (USBDescEndpoint[]) {
|
||||||
USB_ENDPOINT_XFER_INT, /* u8 bmAttributes */
|
{
|
||||||
STATUS_BYTECOUNT & 0xff, STATUS_BYTECOUNT >> 8, /* le16 wMaxPacketSize */
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
/* CDC Data (nop) Interface */
|
.wMaxPacketSize = STATUS_BYTECOUNT,
|
||||||
0x09, /* u8 bLength */
|
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
},
|
||||||
0x01, /* u8 bInterfaceNumber */
|
}
|
||||||
0x00, /* u8 bAlternateSetting */
|
},{
|
||||||
0x00, /* u8 bNumEndpoints */
|
/* CDC Data Interface (off) */
|
||||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
.bInterfaceNumber = 1,
|
||||||
0x00, /* u8 bInterfaceSubClass */
|
.bAlternateSetting = 0,
|
||||||
0x00, /* u8 bInterfaceProtocol */
|
.bNumEndpoints = 0,
|
||||||
0x00, /* u8 iInterface */
|
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||||
/* CDC Data Interface */
|
},{
|
||||||
0x09, /* u8 bLength */
|
/* CDC Data Interface */
|
||||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
.bInterfaceNumber = 1,
|
||||||
0x01, /* u8 bInterfaceNumber */
|
.bAlternateSetting = 1,
|
||||||
0x01, /* u8 bAlternateSetting */
|
.bNumEndpoints = 2,
|
||||||
0x02, /* u8 bNumEndpoints */
|
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
.iInterface = STRING_DATA,
|
||||||
0x00, /* u8 bInterfaceSubClass */
|
.eps = (USBDescEndpoint[]) {
|
||||||
0x00, /* u8 bInterfaceProtocol */
|
{
|
||||||
STRING_DATA, /* u8 iInterface */
|
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||||
/* Source Endpoint */
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
0x07, /* u8 bLength */
|
.wMaxPacketSize = 0x40,
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
},{
|
||||||
USB_DIR_IN | 2, /* u8 bEndpointAddress */
|
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
.wMaxPacketSize = 0x40,
|
||||||
0x00, /* u8 bInterval */
|
}
|
||||||
/* Sink Endpoint */
|
}
|
||||||
0x07, /* u8 bLength */
|
}
|
||||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
};
|
||||||
USB_DIR_OUT | 2, /* u8 bEndpointAddress */
|
|
||||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
static const USBDescDevice desc_device_net = {
|
||||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
.bcdUSB = 0x0200,
|
||||||
0x00 /* u8 bInterval */
|
.bDeviceClass = USB_CLASS_COMM,
|
||||||
|
.bMaxPacketSize0 = 0x40,
|
||||||
|
.bNumConfigurations = 2,
|
||||||
|
.confs = (USBDescConfig[]) {
|
||||||
|
{
|
||||||
|
.bNumInterfaces = 2,
|
||||||
|
.bConfigurationValue = DEV_RNDIS_CONFIG_VALUE,
|
||||||
|
.iConfiguration = STRING_RNDIS,
|
||||||
|
.bmAttributes = 0xc0,
|
||||||
|
.bMaxPower = 0x32,
|
||||||
|
.nif = ARRAY_SIZE(desc_iface_rndis),
|
||||||
|
.ifs = desc_iface_rndis,
|
||||||
|
},{
|
||||||
|
.bNumInterfaces = 2,
|
||||||
|
.bConfigurationValue = DEV_CONFIG_VALUE,
|
||||||
|
.iConfiguration = STRING_CDC,
|
||||||
|
.bmAttributes = 0xc0,
|
||||||
|
.bMaxPower = 0x32,
|
||||||
|
.nif = ARRAY_SIZE(desc_iface_cdc),
|
||||||
|
.ifs = desc_iface_cdc,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static const USBDesc desc_net = {
|
||||||
|
.id = {
|
||||||
|
.idVendor = RNDIS_VENDOR_NUM,
|
||||||
|
.idProduct = RNDIS_PRODUCT_NUM,
|
||||||
|
.bcdDevice = 0,
|
||||||
|
.iManufacturer = STRING_MANUFACTURER,
|
||||||
|
.iProduct = STRING_PRODUCT,
|
||||||
|
.iSerialNumber = STRING_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_net,
|
||||||
|
.str = usb_net_stringtable,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -599,7 +627,6 @@ struct rndis_response {
|
||||||
typedef struct USBNetState {
|
typedef struct USBNetState {
|
||||||
USBDevice dev;
|
USBDevice dev;
|
||||||
|
|
||||||
unsigned int rndis;
|
|
||||||
enum rndis_state rndis_state;
|
enum rndis_state rndis_state;
|
||||||
uint32_t medium;
|
uint32_t medium;
|
||||||
uint32_t speed;
|
uint32_t speed;
|
||||||
|
@ -620,6 +647,11 @@ typedef struct USBNetState {
|
||||||
QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
|
QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
|
||||||
} USBNetState;
|
} USBNetState;
|
||||||
|
|
||||||
|
static int is_rndis(USBNetState *s)
|
||||||
|
{
|
||||||
|
return s->dev.config->bConfigurationValue == DEV_RNDIS_CONFIG_VALUE;
|
||||||
|
}
|
||||||
|
|
||||||
static int ndis_query(USBNetState *s, uint32_t oid,
|
static int ndis_query(USBNetState *s, uint32_t oid,
|
||||||
uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
|
uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
|
||||||
size_t outlen)
|
size_t outlen)
|
||||||
|
@ -1010,59 +1042,23 @@ static void usb_net_handle_reset(USBDevice *dev)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
static const char * const usb_net_stringtable[] = {
|
|
||||||
[STRING_MANUFACTURER] = "QEMU",
|
|
||||||
[STRING_PRODUCT] = "RNDIS/QEMU USB Network Device",
|
|
||||||
[STRING_ETHADDR] = "400102030405",
|
|
||||||
[STRING_DATA] = "QEMU USB Net Data Interface",
|
|
||||||
[STRING_CONTROL] = "QEMU USB Net Control Interface",
|
|
||||||
[STRING_RNDIS_CONTROL] = "QEMU USB Net RNDIS Control Interface",
|
|
||||||
[STRING_CDC] = "QEMU USB Net CDC",
|
|
||||||
[STRING_SUBSET] = "QEMU USB Net Subset",
|
|
||||||
[STRING_RNDIS] = "QEMU USB Net RNDIS",
|
|
||||||
[STRING_SERIALNUMBER] = "1",
|
|
||||||
};
|
|
||||||
|
|
||||||
static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
USBNetState *s = (USBNetState *) dev;
|
USBNetState *s = (USBNetState *) dev;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch(request) {
|
switch(request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
|
case ClassInterfaceOutRequest | USB_CDC_SEND_ENCAPSULATED_COMMAND:
|
||||||
if (!s->rndis || value || index != 0)
|
if (!is_rndis(s) || value || index != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
#ifdef TRAFFIC_DEBUG
|
#ifdef TRAFFIC_DEBUG
|
||||||
{
|
{
|
||||||
unsigned int i;
|
unsigned int i;
|
||||||
|
@ -1079,8 +1075,9 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
|
case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
|
||||||
if (!s->rndis || value || index != 0)
|
if (!is_rndis(s) || value || index != 0) {
|
||||||
goto fail;
|
goto fail;
|
||||||
|
}
|
||||||
ret = rndis_get_response(s, data);
|
ret = rndis_get_response(s, data);
|
||||||
if (!ret) {
|
if (!ret) {
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
|
@ -1100,85 +1097,6 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch(value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
ret = sizeof(qemu_net_dev_descriptor);
|
|
||||||
memcpy(data, qemu_net_dev_descriptor, ret);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
switch (value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
ret = sizeof(qemu_net_rndis_config_descriptor);
|
|
||||||
memcpy(data, qemu_net_rndis_config_descriptor, ret);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 1:
|
|
||||||
ret = sizeof(qemu_net_cdc_config_descriptor);
|
|
||||||
memcpy(data, qemu_net_cdc_config_descriptor, ret);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
|
|
||||||
data[2] = ret & 0xff;
|
|
||||||
data[3] = ret >> 8;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch (value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case STRING_ETHADDR:
|
|
||||||
ret = set_usb_string(data, s->usbstring_mac);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
if (ARRAY_SIZE(usb_net_stringtable) > (value & 0xff)) {
|
|
||||||
ret = set_usb_string(data,
|
|
||||||
usb_net_stringtable[value & 0xff]);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = s->rndis ? DEV_RNDIS_CONFIG_VALUE : DEV_CONFIG_VALUE;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
switch (value & 0xff) {
|
|
||||||
case DEV_CONFIG_VALUE:
|
|
||||||
s->rndis = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DEV_RNDIS_CONFIG_VALUE:
|
|
||||||
s->rndis = 1;
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
|
@ -1249,7 +1167,7 @@ static int usb_net_handle_datain(USBNetState *s, USBPacket *p)
|
||||||
memcpy(p->data, &s->in_buf[s->in_ptr], ret);
|
memcpy(p->data, &s->in_buf[s->in_ptr], ret);
|
||||||
s->in_ptr += ret;
|
s->in_ptr += ret;
|
||||||
if (s->in_ptr >= s->in_len &&
|
if (s->in_ptr >= s->in_len &&
|
||||||
(s->rndis || (s->in_len & (64 - 1)) || !ret)) {
|
(is_rndis(s) || (s->in_len & (64 - 1)) || !ret)) {
|
||||||
/* no short packet necessary */
|
/* no short packet necessary */
|
||||||
s->in_ptr = s->in_len = 0;
|
s->in_ptr = s->in_len = 0;
|
||||||
}
|
}
|
||||||
|
@ -1298,7 +1216,7 @@ static int usb_net_handle_dataout(USBNetState *s, USBPacket *p)
|
||||||
memcpy(&s->out_buf[s->out_ptr], p->data, sz);
|
memcpy(&s->out_buf[s->out_ptr], p->data, sz);
|
||||||
s->out_ptr += sz;
|
s->out_ptr += sz;
|
||||||
|
|
||||||
if (!s->rndis) {
|
if (!is_rndis(s)) {
|
||||||
if (ret < 64) {
|
if (ret < 64) {
|
||||||
qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
|
qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
|
||||||
s->out_ptr = 0;
|
s->out_ptr = 0;
|
||||||
|
@ -1369,7 +1287,7 @@ static ssize_t usbnet_receive(VLANClientState *nc, const uint8_t *buf, size_t si
|
||||||
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
|
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
|
||||||
struct rndis_packet_msg_type *msg;
|
struct rndis_packet_msg_type *msg;
|
||||||
|
|
||||||
if (s->rndis) {
|
if (is_rndis(s)) {
|
||||||
msg = (struct rndis_packet_msg_type *) s->in_buf;
|
msg = (struct rndis_packet_msg_type *) s->in_buf;
|
||||||
if (!s->rndis_state == RNDIS_DATA_INITIALIZED)
|
if (!s->rndis_state == RNDIS_DATA_INITIALIZED)
|
||||||
return -1;
|
return -1;
|
||||||
|
@ -1405,8 +1323,9 @@ static int usbnet_can_receive(VLANClientState *nc)
|
||||||
{
|
{
|
||||||
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
|
USBNetState *s = DO_UPCAST(NICState, nc, nc)->opaque;
|
||||||
|
|
||||||
if (s->rndis && !s->rndis_state == RNDIS_DATA_INITIALIZED)
|
if (is_rndis(s) && !s->rndis_state == RNDIS_DATA_INITIALIZED) {
|
||||||
return 1;
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
return !s->in_len;
|
return !s->in_len;
|
||||||
}
|
}
|
||||||
|
@ -1439,9 +1358,8 @@ static int usb_net_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
|
USBNetState *s = DO_UPCAST(USBNetState, dev, dev);
|
||||||
|
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
usb_desc_init(dev);
|
||||||
|
|
||||||
s->rndis = 1;
|
|
||||||
s->rndis_state = RNDIS_UNINITIALIZED;
|
s->rndis_state = RNDIS_UNINITIALIZED;
|
||||||
QTAILQ_INIT(&s->rndis_resp);
|
QTAILQ_INIT(&s->rndis_resp);
|
||||||
|
|
||||||
|
@ -1463,6 +1381,7 @@ static int usb_net_initfn(USBDevice *dev)
|
||||||
s->conf.macaddr.a[3],
|
s->conf.macaddr.a[3],
|
||||||
s->conf.macaddr.a[4],
|
s->conf.macaddr.a[4],
|
||||||
s->conf.macaddr.a[5]);
|
s->conf.macaddr.a[5]);
|
||||||
|
usb_desc_set_string(dev, STRING_ETHADDR, s->usbstring_mac);
|
||||||
|
|
||||||
add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0");
|
add_boot_device_path(s->conf.bootindex, &dev->qdev, "/ethernet@0");
|
||||||
return 0;
|
return 0;
|
||||||
|
@ -1500,6 +1419,7 @@ static struct USBDeviceInfo net_info = {
|
||||||
.qdev.name = "usb-net",
|
.qdev.name = "usb-net",
|
||||||
.qdev.fw_name = "network",
|
.qdev.fw_name = "network",
|
||||||
.qdev.size = sizeof(USBNetState),
|
.qdev.size = sizeof(USBNetState),
|
||||||
|
.usb_desc = &desc_net,
|
||||||
.init = usb_net_initfn,
|
.init = usb_net_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_net_handle_reset,
|
.handle_reset = usb_net_handle_reset,
|
||||||
|
|
|
@ -322,52 +322,46 @@ static inline void ohci_set_interrupt(OHCIState *ohci, uint32_t intr)
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach or detach a device on a root hub port. */
|
/* Attach or detach a device on a root hub port. */
|
||||||
static void ohci_attach(USBPort *port1, USBDevice *dev)
|
static void ohci_attach(USBPort *port1)
|
||||||
|
{
|
||||||
|
OHCIState *s = port1->opaque;
|
||||||
|
OHCIPort *port = &s->rhport[port1->index];
|
||||||
|
|
||||||
|
/* set connect status */
|
||||||
|
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
||||||
|
|
||||||
|
/* update speed */
|
||||||
|
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||||
|
port->ctrl |= OHCI_PORT_LSDA;
|
||||||
|
} else {
|
||||||
|
port->ctrl &= ~OHCI_PORT_LSDA;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notify of remote-wakeup */
|
||||||
|
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
||||||
|
ohci_set_interrupt(s, OHCI_INTR_RD);
|
||||||
|
}
|
||||||
|
|
||||||
|
DPRINTF("usb-ohci: Attached port %d\n", port1->index);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void ohci_detach(USBPort *port1)
|
||||||
{
|
{
|
||||||
OHCIState *s = port1->opaque;
|
OHCIState *s = port1->opaque;
|
||||||
OHCIPort *port = &s->rhport[port1->index];
|
OHCIPort *port = &s->rhport[port1->index];
|
||||||
uint32_t old_state = port->ctrl;
|
uint32_t old_state = port->ctrl;
|
||||||
|
|
||||||
if (dev) {
|
/* set connect status */
|
||||||
if (port->port.dev) {
|
if (port->ctrl & OHCI_PORT_CCS) {
|
||||||
usb_attach(port1, NULL);
|
port->ctrl &= ~OHCI_PORT_CCS;
|
||||||
}
|
port->ctrl |= OHCI_PORT_CSC;
|
||||||
/* set connect status */
|
|
||||||
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
|
|
||||||
|
|
||||||
/* update speed */
|
|
||||||
if (dev->speed == USB_SPEED_LOW)
|
|
||||||
port->ctrl |= OHCI_PORT_LSDA;
|
|
||||||
else
|
|
||||||
port->ctrl &= ~OHCI_PORT_LSDA;
|
|
||||||
port->port.dev = dev;
|
|
||||||
|
|
||||||
/* notify of remote-wakeup */
|
|
||||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND)
|
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RD);
|
|
||||||
|
|
||||||
/* send the attach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
|
||||||
DPRINTF("usb-ohci: Attached port %d\n", port1->index);
|
|
||||||
} else {
|
|
||||||
/* set connect status */
|
|
||||||
if (port->ctrl & OHCI_PORT_CCS) {
|
|
||||||
port->ctrl &= ~OHCI_PORT_CCS;
|
|
||||||
port->ctrl |= OHCI_PORT_CSC;
|
|
||||||
}
|
|
||||||
/* disable port */
|
|
||||||
if (port->ctrl & OHCI_PORT_PES) {
|
|
||||||
port->ctrl &= ~OHCI_PORT_PES;
|
|
||||||
port->ctrl |= OHCI_PORT_PESC;
|
|
||||||
}
|
|
||||||
dev = port->port.dev;
|
|
||||||
if (dev) {
|
|
||||||
/* send the detach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_DETACH);
|
|
||||||
}
|
|
||||||
port->port.dev = NULL;
|
|
||||||
DPRINTF("usb-ohci: Detached port %d\n", port1->index);
|
|
||||||
}
|
}
|
||||||
|
/* disable port */
|
||||||
|
if (port->ctrl & OHCI_PORT_PES) {
|
||||||
|
port->ctrl &= ~OHCI_PORT_PES;
|
||||||
|
port->ctrl |= OHCI_PORT_PESC;
|
||||||
|
}
|
||||||
|
DPRINTF("usb-ohci: Detached port %d\n", port1->index);
|
||||||
|
|
||||||
if (old_state != port->ctrl)
|
if (old_state != port->ctrl)
|
||||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||||
|
@ -413,8 +407,9 @@ static void ohci_reset(void *opaque)
|
||||||
{
|
{
|
||||||
port = &ohci->rhport[i];
|
port = &ohci->rhport[i];
|
||||||
port->ctrl = 0;
|
port->ctrl = 0;
|
||||||
if (port->port.dev)
|
if (port->port.dev) {
|
||||||
ohci_attach(&port->port, port->port.dev);
|
usb_attach(&port->port, port->port.dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (ohci->async_td) {
|
if (ohci->async_td) {
|
||||||
usb_cancel_packet(&ohci->usb_packet);
|
usb_cancel_packet(&ohci->usb_packet);
|
||||||
|
@ -1669,6 +1664,11 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={
|
||||||
ohci_mem_write
|
ohci_mem_write
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static USBPortOps ohci_port_ops = {
|
||||||
|
.attach = ohci_attach,
|
||||||
|
.detach = ohci_detach,
|
||||||
|
};
|
||||||
|
|
||||||
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||||
int num_ports, uint32_t localmem_base)
|
int num_ports, uint32_t localmem_base)
|
||||||
{
|
{
|
||||||
|
@ -1699,7 +1699,9 @@ static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||||
usb_bus_new(&ohci->bus, dev);
|
usb_bus_new(&ohci->bus, dev);
|
||||||
ohci->num_ports = num_ports;
|
ohci->num_ports = num_ports;
|
||||||
for (i = 0; i < num_ports; i++) {
|
for (i = 0; i < num_ports; i++) {
|
||||||
usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, NULL, ohci_attach);
|
usb_register_port(&ohci->bus, &ohci->rhport[i].port, ohci, i, &ohci_port_ops,
|
||||||
|
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||||
|
usb_port_location(&ohci->rhport[i].port, NULL, i+1);
|
||||||
}
|
}
|
||||||
|
|
||||||
ohci->async_td = 0;
|
ohci->async_td = 0;
|
||||||
|
|
228
hw/usb-serial.c
228
hw/usb-serial.c
|
@ -11,6 +11,7 @@
|
||||||
#include "qemu-common.h"
|
#include "qemu-common.h"
|
||||||
#include "qemu-error.h"
|
#include "qemu-error.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
#include "qemu-char.h"
|
#include "qemu-char.h"
|
||||||
|
|
||||||
//#define DEBUG_Serial
|
//#define DEBUG_Serial
|
||||||
|
@ -91,8 +92,6 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
USBDevice dev;
|
USBDevice dev;
|
||||||
uint32_t vendorid;
|
|
||||||
uint32_t productid;
|
|
||||||
uint8_t recv_buf[RECV_BUF];
|
uint8_t recv_buf[RECV_BUF];
|
||||||
uint16_t recv_ptr;
|
uint16_t recv_ptr;
|
||||||
uint16_t recv_used;
|
uint16_t recv_used;
|
||||||
|
@ -104,69 +103,78 @@ typedef struct {
|
||||||
CharDriverState *cs;
|
CharDriverState *cs;
|
||||||
} USBSerialState;
|
} USBSerialState;
|
||||||
|
|
||||||
static const uint8_t qemu_serial_dev_descriptor[] = {
|
enum {
|
||||||
0x12, /* u8 bLength; */
|
STR_MANUFACTURER = 1,
|
||||||
0x01, /* u8 bDescriptorType; Device */
|
STR_PRODUCT_SERIAL,
|
||||||
0x00, 0x02, /* u16 bcdUSB; v2.0 */
|
STR_PRODUCT_BRAILLE,
|
||||||
|
STR_SERIALNUMBER,
|
||||||
0x00, /* u8 bDeviceClass; */
|
|
||||||
0x00, /* u8 bDeviceSubClass; */
|
|
||||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
|
||||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
|
||||||
|
|
||||||
/* Vendor and product id are arbitrary. */
|
|
||||||
0x03, 0x04, /* u16 idVendor; */
|
|
||||||
0x00, 0xFF, /* u16 idProduct; */
|
|
||||||
0x00, 0x04, /* u16 bcdDevice */
|
|
||||||
|
|
||||||
0x01, /* u8 iManufacturer; */
|
|
||||||
0x02, /* u8 iProduct; */
|
|
||||||
0x03, /* u8 iSerialNumber; */
|
|
||||||
0x01 /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_serial_config_descriptor[] = {
|
static const USBDescStrings desc_strings = {
|
||||||
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
|
[STR_PRODUCT_SERIAL] = "QEMU USB SERIAL",
|
||||||
|
[STR_PRODUCT_BRAILLE] = "QEMU USB BRAILLE",
|
||||||
|
[STR_SERIALNUMBER] = "1",
|
||||||
|
};
|
||||||
|
|
||||||
/* one configuration */
|
static const USBDescIface desc_iface0 = {
|
||||||
0x09, /* u8 bLength; */
|
.bInterfaceNumber = 0,
|
||||||
0x02, /* u8 bDescriptorType; Configuration */
|
.bNumEndpoints = 2,
|
||||||
0x20, 0x00, /* u16 wTotalLength; */
|
.bInterfaceClass = 0xff,
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
.bInterfaceSubClass = 0xff,
|
||||||
0x01, /* u8 bConfigurationValue; */
|
.bInterfaceProtocol = 0xff,
|
||||||
0x00, /* u8 iConfiguration; */
|
.eps = (USBDescEndpoint[]) {
|
||||||
0x80, /* u8 bmAttributes;
|
{
|
||||||
Bit 7: must be set,
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
6: Self-powered,
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
5: Remote wakeup,
|
.wMaxPacketSize = 64,
|
||||||
4..0: resvd */
|
},{
|
||||||
100/2, /* u8 MaxPower; */
|
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||||
|
.wMaxPacketSize = 64,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
/* one interface */
|
static const USBDescDevice desc_device = {
|
||||||
0x09, /* u8 if_bLength; */
|
.bcdUSB = 0x0200,
|
||||||
0x04, /* u8 if_bDescriptorType; Interface */
|
.bMaxPacketSize0 = 8,
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
.bNumConfigurations = 1,
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
.confs = (USBDescConfig[]) {
|
||||||
0x02, /* u8 if_bNumEndpoints; */
|
{
|
||||||
0xff, /* u8 if_bInterfaceClass; Vendor Specific */
|
.bNumInterfaces = 1,
|
||||||
0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
|
.bConfigurationValue = 1,
|
||||||
0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
|
.bmAttributes = 0x80,
|
||||||
0x02, /* u8 if_iInterface; */
|
.bMaxPower = 50,
|
||||||
|
.ifs = &desc_iface0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* Bulk-In endpoint */
|
static const USBDesc desc_serial = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.id = {
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
.idVendor = 0x0403,
|
||||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
.idProduct = 0x6001,
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
.bcdDevice = 0x0400,
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
0x00, /* u8 ep_bInterval; */
|
.iProduct = STR_PRODUCT_SERIAL,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device,
|
||||||
|
.str = desc_strings,
|
||||||
|
};
|
||||||
|
|
||||||
/* Bulk-Out endpoint */
|
static const USBDesc desc_braille = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.id = {
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
.idVendor = 0x0403,
|
||||||
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
.idProduct = 0xfe72,
|
||||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
.bcdDevice = 0x0400,
|
||||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
0x00 /* u8 ep_bInterval; */
|
.iProduct = STR_PRODUCT_BRAILLE,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device,
|
||||||
|
.str = desc_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_serial_reset(USBSerialState *s)
|
static void usb_serial_reset(USBSerialState *s)
|
||||||
|
@ -214,89 +222,16 @@ static int usb_serial_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
USBSerialState *s = (USBSerialState *)dev;
|
USBSerialState *s = (USBSerialState *)dev;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
//DPRINTF("got control %x, value %x\n",request, value);
|
DPRINTF("got control %x, value %x\n",request, value);
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (0 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch(value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
memcpy(data, qemu_serial_dev_descriptor,
|
|
||||||
sizeof(qemu_serial_dev_descriptor));
|
|
||||||
data[8] = s->vendorid & 0xff;
|
|
||||||
data[9] = ((s->vendorid) >> 8) & 0xff;
|
|
||||||
data[10] = s->productid & 0xff;
|
|
||||||
data[11] = ((s->productid) >> 8) & 0xff;
|
|
||||||
ret = sizeof(qemu_serial_dev_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
memcpy(data, qemu_serial_config_descriptor,
|
|
||||||
sizeof(qemu_serial_config_descriptor));
|
|
||||||
ret = sizeof(qemu_serial_config_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch(value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* vendor description */
|
|
||||||
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
/* product description */
|
|
||||||
ret = set_usb_string(data, "QEMU USB SERIAL");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* serial number */
|
|
||||||
ret = set_usb_string(data, "1");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = 1;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -543,7 +478,8 @@ static void usb_serial_event(void *opaque, int event)
|
||||||
static int usb_serial_initfn(USBDevice *dev)
|
static int usb_serial_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
|
USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
|
||||||
|
usb_desc_init(dev);
|
||||||
|
|
||||||
if (!s->cs) {
|
if (!s->cs) {
|
||||||
error_report("Property chardev is required");
|
error_report("Property chardev is required");
|
||||||
|
@ -633,6 +569,7 @@ static struct USBDeviceInfo serial_info = {
|
||||||
.product_desc = "QEMU USB Serial",
|
.product_desc = "QEMU USB Serial",
|
||||||
.qdev.name = "usb-serial",
|
.qdev.name = "usb-serial",
|
||||||
.qdev.size = sizeof(USBSerialState),
|
.qdev.size = sizeof(USBSerialState),
|
||||||
|
.usb_desc = &desc_serial,
|
||||||
.init = usb_serial_initfn,
|
.init = usb_serial_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_serial_handle_reset,
|
.handle_reset = usb_serial_handle_reset,
|
||||||
|
@ -642,9 +579,7 @@ static struct USBDeviceInfo serial_info = {
|
||||||
.usbdevice_name = "serial",
|
.usbdevice_name = "serial",
|
||||||
.usbdevice_init = usb_serial_init,
|
.usbdevice_init = usb_serial_init,
|
||||||
.qdev.props = (Property[]) {
|
.qdev.props = (Property[]) {
|
||||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||||
DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403),
|
|
||||||
DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0x6001),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
@ -653,6 +588,7 @@ static struct USBDeviceInfo braille_info = {
|
||||||
.product_desc = "QEMU USB Braille",
|
.product_desc = "QEMU USB Braille",
|
||||||
.qdev.name = "usb-braille",
|
.qdev.name = "usb-braille",
|
||||||
.qdev.size = sizeof(USBSerialState),
|
.qdev.size = sizeof(USBSerialState),
|
||||||
|
.usb_desc = &desc_braille,
|
||||||
.init = usb_serial_initfn,
|
.init = usb_serial_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
.handle_reset = usb_serial_handle_reset,
|
.handle_reset = usb_serial_handle_reset,
|
||||||
|
@ -662,9 +598,7 @@ static struct USBDeviceInfo braille_info = {
|
||||||
.usbdevice_name = "braille",
|
.usbdevice_name = "braille",
|
||||||
.usbdevice_init = usb_braille_init,
|
.usbdevice_init = usb_braille_init,
|
||||||
.qdev.props = (Property[]) {
|
.qdev.props = (Property[]) {
|
||||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||||
DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403),
|
|
||||||
DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0xfe72),
|
|
||||||
DEFINE_PROP_END_OF_LIST(),
|
DEFINE_PROP_END_OF_LIST(),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
100
hw/usb-uhci.c
100
hw/usb-uhci.c
|
@ -57,13 +57,18 @@
|
||||||
#define TD_CTRL_NAK (1 << 19)
|
#define TD_CTRL_NAK (1 << 19)
|
||||||
#define TD_CTRL_TIMEOUT (1 << 18)
|
#define TD_CTRL_TIMEOUT (1 << 18)
|
||||||
|
|
||||||
|
#define UHCI_PORT_SUSPEND (1 << 12)
|
||||||
#define UHCI_PORT_RESET (1 << 9)
|
#define UHCI_PORT_RESET (1 << 9)
|
||||||
#define UHCI_PORT_LSDA (1 << 8)
|
#define UHCI_PORT_LSDA (1 << 8)
|
||||||
|
#define UHCI_PORT_RD (1 << 6)
|
||||||
#define UHCI_PORT_ENC (1 << 3)
|
#define UHCI_PORT_ENC (1 << 3)
|
||||||
#define UHCI_PORT_EN (1 << 2)
|
#define UHCI_PORT_EN (1 << 2)
|
||||||
#define UHCI_PORT_CSC (1 << 1)
|
#define UHCI_PORT_CSC (1 << 1)
|
||||||
#define UHCI_PORT_CCS (1 << 0)
|
#define UHCI_PORT_CCS (1 << 0)
|
||||||
|
|
||||||
|
#define UHCI_PORT_READ_ONLY (0x1bb)
|
||||||
|
#define UHCI_PORT_WRITE_CLEAR (UHCI_PORT_CSC | UHCI_PORT_ENC)
|
||||||
|
|
||||||
#define FRAME_TIMER_FREQ 1000
|
#define FRAME_TIMER_FREQ 1000
|
||||||
|
|
||||||
#define FRAME_MAX_LOOPS 100
|
#define FRAME_MAX_LOOPS 100
|
||||||
|
@ -307,8 +312,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token
|
||||||
return match;
|
return match;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_attach(USBPort *port1, USBDevice *dev);
|
|
||||||
|
|
||||||
static void uhci_update_irq(UHCIState *s)
|
static void uhci_update_irq(UHCIState *s)
|
||||||
{
|
{
|
||||||
int level;
|
int level;
|
||||||
|
@ -348,8 +351,9 @@ static void uhci_reset(void *opaque)
|
||||||
for(i = 0; i < NB_PORTS; i++) {
|
for(i = 0; i < NB_PORTS; i++) {
|
||||||
port = &s->ports[i];
|
port = &s->ports[i];
|
||||||
port->ctrl = 0x0080;
|
port->ctrl = 0x0080;
|
||||||
if (port->port.dev)
|
if (port->port.dev) {
|
||||||
uhci_attach(&port->port, port->port.dev);
|
usb_attach(&port->port, port->port.dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
uhci_async_cancel_all(s);
|
uhci_async_cancel_all(s);
|
||||||
|
@ -498,9 +502,10 @@ static void uhci_ioport_writew(void *opaque, uint32_t addr, uint32_t val)
|
||||||
usb_send_msg(dev, USB_MSG_RESET);
|
usb_send_msg(dev, USB_MSG_RESET);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
port->ctrl = (port->ctrl & 0x01fb) | (val & ~0x01fb);
|
port->ctrl &= UHCI_PORT_READ_ONLY;
|
||||||
|
port->ctrl |= (val & ~UHCI_PORT_READ_ONLY);
|
||||||
/* some bits are reset when a '1' is written to them */
|
/* some bits are reset when a '1' is written to them */
|
||||||
port->ctrl &= ~(val & 0x000a);
|
port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -593,49 +598,52 @@ static void uhci_resume (void *opaque)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void uhci_attach(USBPort *port1, USBDevice *dev)
|
static void uhci_attach(USBPort *port1)
|
||||||
{
|
{
|
||||||
UHCIState *s = port1->opaque;
|
UHCIState *s = port1->opaque;
|
||||||
UHCIPort *port = &s->ports[port1->index];
|
UHCIPort *port = &s->ports[port1->index];
|
||||||
|
|
||||||
if (dev) {
|
/* set connect status */
|
||||||
if (port->port.dev) {
|
port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
|
||||||
usb_attach(port1, NULL);
|
|
||||||
}
|
|
||||||
/* set connect status */
|
|
||||||
port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
|
|
||||||
|
|
||||||
/* update speed */
|
/* update speed */
|
||||||
if (dev->speed == USB_SPEED_LOW)
|
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||||
port->ctrl |= UHCI_PORT_LSDA;
|
port->ctrl |= UHCI_PORT_LSDA;
|
||||||
else
|
|
||||||
port->ctrl &= ~UHCI_PORT_LSDA;
|
|
||||||
|
|
||||||
uhci_resume(s);
|
|
||||||
|
|
||||||
port->port.dev = dev;
|
|
||||||
/* send the attach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_ATTACH);
|
|
||||||
} else {
|
} else {
|
||||||
/* set connect status */
|
port->ctrl &= ~UHCI_PORT_LSDA;
|
||||||
if (port->ctrl & UHCI_PORT_CCS) {
|
}
|
||||||
port->ctrl &= ~UHCI_PORT_CCS;
|
|
||||||
port->ctrl |= UHCI_PORT_CSC;
|
|
||||||
}
|
|
||||||
/* disable port */
|
|
||||||
if (port->ctrl & UHCI_PORT_EN) {
|
|
||||||
port->ctrl &= ~UHCI_PORT_EN;
|
|
||||||
port->ctrl |= UHCI_PORT_ENC;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
uhci_resume(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uhci_detach(USBPort *port1)
|
||||||
|
{
|
||||||
|
UHCIState *s = port1->opaque;
|
||||||
|
UHCIPort *port = &s->ports[port1->index];
|
||||||
|
|
||||||
|
/* set connect status */
|
||||||
|
if (port->ctrl & UHCI_PORT_CCS) {
|
||||||
|
port->ctrl &= ~UHCI_PORT_CCS;
|
||||||
|
port->ctrl |= UHCI_PORT_CSC;
|
||||||
|
}
|
||||||
|
/* disable port */
|
||||||
|
if (port->ctrl & UHCI_PORT_EN) {
|
||||||
|
port->ctrl &= ~UHCI_PORT_EN;
|
||||||
|
port->ctrl |= UHCI_PORT_ENC;
|
||||||
|
}
|
||||||
|
|
||||||
|
uhci_resume(s);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void uhci_wakeup(USBDevice *dev)
|
||||||
|
{
|
||||||
|
USBBus *bus = usb_bus_from_device(dev);
|
||||||
|
UHCIState *s = container_of(bus, UHCIState, bus);
|
||||||
|
UHCIPort *port = s->ports + dev->port->index;
|
||||||
|
|
||||||
|
if (port->ctrl & UHCI_PORT_SUSPEND && !(port->ctrl & UHCI_PORT_RD)) {
|
||||||
|
port->ctrl |= UHCI_PORT_RD;
|
||||||
uhci_resume(s);
|
uhci_resume(s);
|
||||||
|
|
||||||
dev = port->port.dev;
|
|
||||||
if (dev) {
|
|
||||||
/* send the detach message */
|
|
||||||
usb_send_msg(dev, USB_MSG_DETACH);
|
|
||||||
}
|
|
||||||
port->port.dev = NULL;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1101,6 +1109,12 @@ static void uhci_map(PCIDevice *pci_dev, int region_num,
|
||||||
register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
|
register_ioport_read(addr, 32, 1, uhci_ioport_readb, s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static USBPortOps uhci_port_ops = {
|
||||||
|
.attach = uhci_attach,
|
||||||
|
.detach = uhci_detach,
|
||||||
|
.wakeup = uhci_wakeup,
|
||||||
|
};
|
||||||
|
|
||||||
static int usb_uhci_common_initfn(UHCIState *s)
|
static int usb_uhci_common_initfn(UHCIState *s)
|
||||||
{
|
{
|
||||||
uint8_t *pci_conf = s->dev.config;
|
uint8_t *pci_conf = s->dev.config;
|
||||||
|
@ -1115,7 +1129,9 @@ static int usb_uhci_common_initfn(UHCIState *s)
|
||||||
|
|
||||||
usb_bus_new(&s->bus, &s->dev.qdev);
|
usb_bus_new(&s->bus, &s->dev.qdev);
|
||||||
for(i = 0; i < NB_PORTS; i++) {
|
for(i = 0; i < NB_PORTS; i++) {
|
||||||
usb_register_port(&s->bus, &s->ports[i].port, s, i, NULL, uhci_attach);
|
usb_register_port(&s->bus, &s->ports[i].port, s, i, &uhci_port_ops,
|
||||||
|
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||||
|
usb_port_location(&s->ports[i].port, NULL, i+1);
|
||||||
}
|
}
|
||||||
s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
|
s->frame_timer = qemu_new_timer(vm_clock, uhci_frame_timer, s);
|
||||||
s->expire_time = qemu_get_clock(vm_clock) +
|
s->expire_time = qemu_get_clock(vm_clock) +
|
||||||
|
|
214
hw/usb-wacom.c
214
hw/usb-wacom.c
|
@ -28,6 +28,7 @@
|
||||||
#include "hw.h"
|
#include "hw.h"
|
||||||
#include "console.h"
|
#include "console.h"
|
||||||
#include "usb.h"
|
#include "usb.h"
|
||||||
|
#include "usb-desc.h"
|
||||||
|
|
||||||
/* Interface requests */
|
/* Interface requests */
|
||||||
#define WACOM_GET_REPORT 0x2101
|
#define WACOM_GET_REPORT 0x2101
|
||||||
|
@ -54,68 +55,75 @@ typedef struct USBWacomState {
|
||||||
int changed;
|
int changed;
|
||||||
} USBWacomState;
|
} USBWacomState;
|
||||||
|
|
||||||
static const uint8_t qemu_wacom_dev_descriptor[] = {
|
enum {
|
||||||
0x12, /* u8 bLength; */
|
STR_MANUFACTURER = 1,
|
||||||
0x01, /* u8 bDescriptorType; Device */
|
STR_PRODUCT,
|
||||||
0x10, 0x10, /* u16 bcdUSB; v1.10 */
|
STR_SERIALNUMBER,
|
||||||
|
|
||||||
0x00, /* u8 bDeviceClass; */
|
|
||||||
0x00, /* u8 bDeviceSubClass; */
|
|
||||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
|
||||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
|
||||||
|
|
||||||
0x6a, 0x05, /* u16 idVendor; */
|
|
||||||
0x00, 0x00, /* u16 idProduct; */
|
|
||||||
0x10, 0x42, /* u16 bcdDevice */
|
|
||||||
|
|
||||||
0x01, /* u8 iManufacturer; */
|
|
||||||
0x02, /* u8 iProduct; */
|
|
||||||
0x00, /* u8 iSerialNumber; */
|
|
||||||
0x01, /* u8 bNumConfigurations; */
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static const uint8_t qemu_wacom_config_descriptor[] = {
|
static const USBDescStrings desc_strings = {
|
||||||
/* one configuration */
|
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||||
0x09, /* u8 bLength; */
|
[STR_PRODUCT] = "Wacom PenPartner",
|
||||||
0x02, /* u8 bDescriptorType; Configuration */
|
[STR_SERIALNUMBER] = "1",
|
||||||
0x22, 0x00, /* u16 wTotalLength; */
|
};
|
||||||
0x01, /* u8 bNumInterfaces; (1) */
|
|
||||||
0x01, /* u8 bConfigurationValue; */
|
|
||||||
0x00, /* u8 iConfiguration; */
|
|
||||||
0x80, /* u8 bmAttributes;
|
|
||||||
Bit 7: must be set,
|
|
||||||
6: Self-powered,
|
|
||||||
5: Remote wakeup,
|
|
||||||
4..0: resvd */
|
|
||||||
40, /* u8 MaxPower; */
|
|
||||||
|
|
||||||
/* one interface */
|
static const USBDescIface desc_iface_wacom = {
|
||||||
0x09, /* u8 if_bLength; */
|
.bInterfaceNumber = 0,
|
||||||
0x04, /* u8 if_bDescriptorType; Interface */
|
.bNumEndpoints = 1,
|
||||||
0x00, /* u8 if_bInterfaceNumber; */
|
.bInterfaceClass = USB_CLASS_HID,
|
||||||
0x00, /* u8 if_bAlternateSetting; */
|
.bInterfaceSubClass = 0x01, /* boot */
|
||||||
0x01, /* u8 if_bNumEndpoints; */
|
.bInterfaceProtocol = 0x02,
|
||||||
0x03, /* u8 if_bInterfaceClass; HID */
|
.ndesc = 1,
|
||||||
0x01, /* u8 if_bInterfaceSubClass; Boot */
|
.descs = (USBDescOther[]) {
|
||||||
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
{
|
||||||
0x00, /* u8 if_iInterface; */
|
/* HID descriptor */
|
||||||
|
.data = (uint8_t[]) {
|
||||||
|
0x09, /* u8 bLength */
|
||||||
|
0x21, /* u8 bDescriptorType */
|
||||||
|
0x01, 0x10, /* u16 HID_class */
|
||||||
|
0x00, /* u8 country_code */
|
||||||
|
0x01, /* u8 num_descriptors */
|
||||||
|
0x22, /* u8 type: Report */
|
||||||
|
0x6e, 0, /* u16 len */
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
.eps = (USBDescEndpoint[]) {
|
||||||
|
{
|
||||||
|
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||||
|
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||||
|
.wMaxPacketSize = 8,
|
||||||
|
.bInterval = 0x0a,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* HID descriptor */
|
static const USBDescDevice desc_device_wacom = {
|
||||||
0x09, /* u8 bLength; */
|
.bcdUSB = 0x0110,
|
||||||
0x21, /* u8 bDescriptorType; */
|
.bMaxPacketSize0 = 8,
|
||||||
0x01, 0x10, /* u16 HID_class */
|
.bNumConfigurations = 1,
|
||||||
0x00, /* u8 country_code */
|
.confs = (USBDescConfig[]) {
|
||||||
0x01, /* u8 num_descriptors */
|
{
|
||||||
0x22, /* u8 type; Report */
|
.bNumInterfaces = 1,
|
||||||
0x6e, 0x00, /* u16 len */
|
.bConfigurationValue = 1,
|
||||||
|
.bmAttributes = 0x80,
|
||||||
|
.bMaxPower = 40,
|
||||||
|
.ifs = &desc_iface_wacom,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
/* one endpoint (status change endpoint) */
|
static const USBDesc desc_wacom = {
|
||||||
0x07, /* u8 ep_bLength; */
|
.id = {
|
||||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
.idVendor = 0x056a,
|
||||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
.idProduct = 0x0000,
|
||||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
.bcdDevice = 0x4210,
|
||||||
0x08, 0x00, /* u16 ep_wMaxPacketSize; */
|
.iManufacturer = STR_MANUFACTURER,
|
||||||
0x0a, /* u8 ep_bInterval; */
|
.iProduct = STR_PRODUCT,
|
||||||
|
.iSerialNumber = STR_SERIALNUMBER,
|
||||||
|
},
|
||||||
|
.full = &desc_device_wacom,
|
||||||
|
.str = desc_strings,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void usb_mouse_event(void *opaque,
|
static void usb_mouse_event(void *opaque,
|
||||||
|
@ -245,89 +253,15 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
|
||||||
int index, int length, uint8_t *data)
|
int index, int length, uint8_t *data)
|
||||||
{
|
{
|
||||||
USBWacomState *s = (USBWacomState *) dev;
|
USBWacomState *s = (USBWacomState *) dev;
|
||||||
int ret = 0;
|
int ret;
|
||||||
|
|
||||||
|
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||||
|
if (ret >= 0) {
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = 0;
|
||||||
switch (request) {
|
switch (request) {
|
||||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
|
||||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
|
||||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
|
||||||
data[1] = 0x00;
|
|
||||||
ret = 2;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 0;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_FEATURE:
|
|
||||||
if (value == USB_DEVICE_REMOTE_WAKEUP) {
|
|
||||||
dev->remote_wakeup = 1;
|
|
||||||
} else {
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
|
|
||||||
dev->addr = value;
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
|
|
||||||
switch (value >> 8) {
|
|
||||||
case USB_DT_DEVICE:
|
|
||||||
memcpy(data, qemu_wacom_dev_descriptor,
|
|
||||||
sizeof(qemu_wacom_dev_descriptor));
|
|
||||||
ret = sizeof(qemu_wacom_dev_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_CONFIG:
|
|
||||||
memcpy(data, qemu_wacom_config_descriptor,
|
|
||||||
sizeof(qemu_wacom_config_descriptor));
|
|
||||||
ret = sizeof(qemu_wacom_config_descriptor);
|
|
||||||
break;
|
|
||||||
case USB_DT_STRING:
|
|
||||||
switch (value & 0xff) {
|
|
||||||
case 0:
|
|
||||||
/* language ids */
|
|
||||||
data[0] = 4;
|
|
||||||
data[1] = 3;
|
|
||||||
data[2] = 0x09;
|
|
||||||
data[3] = 0x04;
|
|
||||||
ret = 4;
|
|
||||||
break;
|
|
||||||
case 1:
|
|
||||||
/* serial number */
|
|
||||||
ret = set_usb_string(data, "1");
|
|
||||||
break;
|
|
||||||
case 2:
|
|
||||||
ret = set_usb_string(data, "Wacom PenPartner");
|
|
||||||
break;
|
|
||||||
case 3:
|
|
||||||
/* vendor description */
|
|
||||||
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
|
|
||||||
break;
|
|
||||||
case 4:
|
|
||||||
ret = set_usb_string(data, "Wacom Tablet");
|
|
||||||
break;
|
|
||||||
case 5:
|
|
||||||
ret = set_usb_string(data, "Endpoint1 Interrupt Pipe");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
goto fail;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
|
|
||||||
data[0] = 1;
|
|
||||||
ret = 1;
|
|
||||||
break;
|
|
||||||
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
|
|
||||||
ret = 0;
|
|
||||||
break;
|
|
||||||
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
case DeviceRequest | USB_REQ_GET_INTERFACE:
|
||||||
data[0] = 0;
|
data[0] = 0;
|
||||||
ret = 1;
|
ret = 1;
|
||||||
|
@ -364,7 +298,6 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
|
||||||
ret = 0;
|
ret = 0;
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
fail:
|
|
||||||
ret = USB_RET_STALL;
|
ret = USB_RET_STALL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -410,7 +343,7 @@ static void usb_wacom_handle_destroy(USBDevice *dev)
|
||||||
static int usb_wacom_initfn(USBDevice *dev)
|
static int usb_wacom_initfn(USBDevice *dev)
|
||||||
{
|
{
|
||||||
USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
|
USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
|
||||||
s->dev.speed = USB_SPEED_FULL;
|
usb_desc_init(dev);
|
||||||
s->changed = 1;
|
s->changed = 1;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -420,6 +353,7 @@ static struct USBDeviceInfo wacom_info = {
|
||||||
.qdev.name = "usb-wacom-tablet",
|
.qdev.name = "usb-wacom-tablet",
|
||||||
.qdev.desc = "QEMU PenPartner Tablet",
|
.qdev.desc = "QEMU PenPartner Tablet",
|
||||||
.usbdevice_name = "wacom-tablet",
|
.usbdevice_name = "wacom-tablet",
|
||||||
|
.usb_desc = &desc_wacom,
|
||||||
.qdev.size = sizeof(USBWacomState),
|
.qdev.size = sizeof(USBWacomState),
|
||||||
.init = usb_wacom_initfn,
|
.init = usb_wacom_initfn,
|
||||||
.handle_packet = usb_generic_handle_packet,
|
.handle_packet = usb_generic_handle_packet,
|
||||||
|
|
34
hw/usb.c
34
hw/usb.c
|
@ -28,7 +28,32 @@
|
||||||
|
|
||||||
void usb_attach(USBPort *port, USBDevice *dev)
|
void usb_attach(USBPort *port, USBDevice *dev)
|
||||||
{
|
{
|
||||||
port->attach(port, dev);
|
if (dev != NULL) {
|
||||||
|
/* attach */
|
||||||
|
if (port->dev) {
|
||||||
|
usb_attach(port, NULL);
|
||||||
|
}
|
||||||
|
dev->port = port;
|
||||||
|
port->dev = dev;
|
||||||
|
port->ops->attach(port);
|
||||||
|
usb_send_msg(dev, USB_MSG_ATTACH);
|
||||||
|
} else {
|
||||||
|
/* detach */
|
||||||
|
dev = port->dev;
|
||||||
|
port->ops->detach(port);
|
||||||
|
if (dev) {
|
||||||
|
usb_send_msg(dev, USB_MSG_DETACH);
|
||||||
|
dev->port = NULL;
|
||||||
|
port->dev = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void usb_wakeup(USBDevice *dev)
|
||||||
|
{
|
||||||
|
if (dev->remote_wakeup && dev->port && dev->port->ops->wakeup) {
|
||||||
|
dev->port->ops->wakeup(dev);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**********************/
|
/**********************/
|
||||||
|
@ -169,6 +194,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
||||||
switch(p->pid) {
|
switch(p->pid) {
|
||||||
case USB_MSG_ATTACH:
|
case USB_MSG_ATTACH:
|
||||||
s->state = USB_STATE_ATTACHED;
|
s->state = USB_STATE_ATTACHED;
|
||||||
|
if (s->info->handle_attach) {
|
||||||
|
s->info->handle_attach(s);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
case USB_MSG_DETACH:
|
case USB_MSG_DETACH:
|
||||||
|
@ -179,7 +207,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
||||||
s->remote_wakeup = 0;
|
s->remote_wakeup = 0;
|
||||||
s->addr = 0;
|
s->addr = 0;
|
||||||
s->state = USB_STATE_DEFAULT;
|
s->state = USB_STATE_DEFAULT;
|
||||||
s->info->handle_reset(s);
|
if (s->info->handle_reset) {
|
||||||
|
s->info->handle_reset(s);
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
50
hw/usb.h
50
hw/usb.h
|
@ -44,6 +44,12 @@
|
||||||
#define USB_SPEED_LOW 0
|
#define USB_SPEED_LOW 0
|
||||||
#define USB_SPEED_FULL 1
|
#define USB_SPEED_FULL 1
|
||||||
#define USB_SPEED_HIGH 2
|
#define USB_SPEED_HIGH 2
|
||||||
|
#define USB_SPEED_SUPER 3
|
||||||
|
|
||||||
|
#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW)
|
||||||
|
#define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL)
|
||||||
|
#define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH)
|
||||||
|
#define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER)
|
||||||
|
|
||||||
#define USB_STATE_NOTATTACHED 0
|
#define USB_STATE_NOTATTACHED 0
|
||||||
#define USB_STATE_ATTACHED 1
|
#define USB_STATE_ATTACHED 1
|
||||||
|
@ -116,6 +122,8 @@
|
||||||
#define USB_DT_STRING 0x03
|
#define USB_DT_STRING 0x03
|
||||||
#define USB_DT_INTERFACE 0x04
|
#define USB_DT_INTERFACE 0x04
|
||||||
#define USB_DT_ENDPOINT 0x05
|
#define USB_DT_ENDPOINT 0x05
|
||||||
|
#define USB_DT_DEVICE_QUALIFIER 0x06
|
||||||
|
#define USB_DT_OTHER_SPEED_CONFIG 0x07
|
||||||
|
|
||||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||||
#define USB_ENDPOINT_XFER_ISOC 1
|
#define USB_ENDPOINT_XFER_ISOC 1
|
||||||
|
@ -128,10 +136,27 @@ typedef struct USBDevice USBDevice;
|
||||||
typedef struct USBDeviceInfo USBDeviceInfo;
|
typedef struct USBDeviceInfo USBDeviceInfo;
|
||||||
typedef struct USBPacket USBPacket;
|
typedef struct USBPacket USBPacket;
|
||||||
|
|
||||||
|
typedef struct USBDesc USBDesc;
|
||||||
|
typedef struct USBDescID USBDescID;
|
||||||
|
typedef struct USBDescDevice USBDescDevice;
|
||||||
|
typedef struct USBDescConfig USBDescConfig;
|
||||||
|
typedef struct USBDescIface USBDescIface;
|
||||||
|
typedef struct USBDescEndpoint USBDescEndpoint;
|
||||||
|
typedef struct USBDescOther USBDescOther;
|
||||||
|
typedef struct USBDescString USBDescString;
|
||||||
|
|
||||||
|
struct USBDescString {
|
||||||
|
uint8_t index;
|
||||||
|
char *str;
|
||||||
|
QLIST_ENTRY(USBDescString) next;
|
||||||
|
};
|
||||||
|
|
||||||
/* definition of a USB device */
|
/* definition of a USB device */
|
||||||
struct USBDevice {
|
struct USBDevice {
|
||||||
DeviceState qdev;
|
DeviceState qdev;
|
||||||
USBDeviceInfo *info;
|
USBDeviceInfo *info;
|
||||||
|
USBPort *port;
|
||||||
|
char *port_path;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
|
|
||||||
int speed;
|
int speed;
|
||||||
|
@ -147,6 +172,10 @@ struct USBDevice {
|
||||||
int setup_state;
|
int setup_state;
|
||||||
int setup_len;
|
int setup_len;
|
||||||
int setup_index;
|
int setup_index;
|
||||||
|
|
||||||
|
QLIST_HEAD(, USBDescString) strings;
|
||||||
|
const USBDescDevice *device;
|
||||||
|
const USBDescConfig *config;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct USBDeviceInfo {
|
struct USBDeviceInfo {
|
||||||
|
@ -167,6 +196,11 @@ struct USBDeviceInfo {
|
||||||
*/
|
*/
|
||||||
void (*handle_destroy)(USBDevice *dev);
|
void (*handle_destroy)(USBDevice *dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Attach the device
|
||||||
|
*/
|
||||||
|
void (*handle_attach)(USBDevice *dev);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Reset the device
|
* Reset the device
|
||||||
*/
|
*/
|
||||||
|
@ -190,20 +224,26 @@ struct USBDeviceInfo {
|
||||||
int (*handle_data)(USBDevice *dev, USBPacket *p);
|
int (*handle_data)(USBDevice *dev, USBPacket *p);
|
||||||
|
|
||||||
const char *product_desc;
|
const char *product_desc;
|
||||||
|
const USBDesc *usb_desc;
|
||||||
|
|
||||||
/* handle legacy -usbdevice command line options */
|
/* handle legacy -usbdevice command line options */
|
||||||
const char *usbdevice_name;
|
const char *usbdevice_name;
|
||||||
USBDevice *(*usbdevice_init)(const char *params);
|
USBDevice *(*usbdevice_init)(const char *params);
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev);
|
typedef struct USBPortOps {
|
||||||
|
void (*attach)(USBPort *port);
|
||||||
|
void (*detach)(USBPort *port);
|
||||||
|
void (*wakeup)(USBDevice *dev);
|
||||||
|
} USBPortOps;
|
||||||
|
|
||||||
/* USB port on which a device can be connected */
|
/* USB port on which a device can be connected */
|
||||||
struct USBPort {
|
struct USBPort {
|
||||||
USBDevice *dev;
|
USBDevice *dev;
|
||||||
usb_attachfn attach;
|
int speedmask;
|
||||||
|
char path[16];
|
||||||
|
USBPortOps *ops;
|
||||||
void *opaque;
|
void *opaque;
|
||||||
USBDevice *pdev;
|
|
||||||
int index; /* internal port index, may be used with the opaque */
|
int index; /* internal port index, may be used with the opaque */
|
||||||
QTAILQ_ENTRY(USBPort) next;
|
QTAILQ_ENTRY(USBPort) next;
|
||||||
};
|
};
|
||||||
|
@ -251,6 +291,7 @@ static inline void usb_cancel_packet(USBPacket * p)
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_attach(USBPort *port, USBDevice *dev);
|
void usb_attach(USBPort *port, USBDevice *dev);
|
||||||
|
void usb_wakeup(USBDevice *dev);
|
||||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
||||||
int set_usb_string(uint8_t *buf, const char *str);
|
int set_usb_string(uint8_t *buf, const char *str);
|
||||||
void usb_send_msg(USBDevice *dev, int msg);
|
void usb_send_msg(USBDevice *dev, int msg);
|
||||||
|
@ -313,7 +354,8 @@ USBDevice *usb_create(USBBus *bus, const char *name);
|
||||||
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
USBDevice *usb_create_simple(USBBus *bus, const char *name);
|
||||||
USBDevice *usbdevice_create(const char *cmdline);
|
USBDevice *usbdevice_create(const char *cmdline);
|
||||||
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
void usb_register_port(USBBus *bus, USBPort *port, void *opaque, int index,
|
||||||
USBDevice *pdev, usb_attachfn attach);
|
USBPortOps *ops, int speedmask);
|
||||||
|
void usb_port_location(USBPort *downstream, USBPort *upstream, int portnr);
|
||||||
void usb_unregister_port(USBBus *bus, USBPort *port);
|
void usb_unregister_port(USBBus *bus, USBPort *port);
|
||||||
int usb_device_attach(USBDevice *dev);
|
int usb_device_attach(USBDevice *dev);
|
||||||
int usb_device_detach(USBDevice *dev);
|
int usb_device_detach(USBDevice *dev);
|
||||||
|
|
11
trace-events
11
trace-events
|
@ -190,6 +190,17 @@ disable sun4m_iommu_page_get_flags(uint64_t pa, uint64_t iopte, uint32_t ret) "g
|
||||||
disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
|
disable sun4m_iommu_translate_pa(uint64_t addr, uint64_t pa, uint32_t iopte) "xlate dva %"PRIx64" => pa %"PRIx64" iopte = %x"
|
||||||
disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
|
disable sun4m_iommu_bad_addr(uint64_t addr) "bad addr %"PRIx64""
|
||||||
|
|
||||||
|
# hw/usb-desc.c
|
||||||
|
disable usb_desc_device(int addr, int len, int ret) "dev %d query device, len %d, ret %d"
|
||||||
|
disable usb_desc_device_qualifier(int addr, int len, int ret) "dev %d query device qualifier, len %d, ret %d"
|
||||||
|
disable usb_desc_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
|
||||||
|
disable usb_desc_other_speed_config(int addr, int index, int len, int ret) "dev %d query config %d, len %d, ret %d"
|
||||||
|
disable usb_desc_string(int addr, int index, int len, int ret) "dev %d query string %d, len %d, ret %d"
|
||||||
|
disable usb_set_addr(int addr) "dev %d"
|
||||||
|
disable usb_set_config(int addr, int config, int ret) "dev %d, config %d, ret %d"
|
||||||
|
disable usb_clear_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
|
||||||
|
disable usb_set_device_feature(int addr, int feature, int ret) "dev %d, feature %d, ret %d"
|
||||||
|
|
||||||
# vl.c
|
# vl.c
|
||||||
disable vm_state_notify(int running, int reason) "running %d reason %d"
|
disable vm_state_notify(int running, int reason) "running %d reason %d"
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue