mirror of https://gitee.com/openkylin/qemu.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*
|
||||
|
||||
USB
|
||||
M: qemu-devel@nongnu.org
|
||||
S: Odd Fixes
|
||||
M: Gerd Hoffmann <kraxel@redhat.com>
|
||||
S: Maintained
|
||||
F: hw/usb*
|
||||
|
||||
vhost
|
||||
|
|
|
@ -88,7 +88,7 @@ common-obj-y += eeprom93xx.o
|
|||
common-obj-y += scsi-disk.o cdrom.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-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_SD) += ssi-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 "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "net.h"
|
||||
#include "bt.h"
|
||||
|
||||
|
@ -51,251 +52,202 @@ struct USBBtState {
|
|||
#define USB_ACL_EP 2
|
||||
#define USB_SCO_EP 3
|
||||
|
||||
static const uint8_t qemu_bt_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
USB_DT_DEVICE, /* u8 bDescriptorType; Device */
|
||||
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; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_SERIALNUMBER,
|
||||
};
|
||||
|
||||
static const uint8_t qemu_bt_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
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; */
|
||||
static const USBDescStrings desc_strings = {
|
||||
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||
[STR_SERIALNUMBER] = "1",
|
||||
};
|
||||
|
||||
/* 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
|
||||
*/
|
||||
static const USBDescIface desc_iface_bluetooth[] = {
|
||||
{
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 3,
|
||||
.bInterfaceClass = 0xe0, /* Wireless */
|
||||
.bInterfaceSubClass = 0x01, /* Radio Frequency */
|
||||
.bInterfaceProtocol = 0x01, /* Bluetooth */
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.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 */
|
||||
0x09, /* u8 if_bLength; */
|
||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x03, /* u8 if_bNumEndpoints; */
|
||||
0xe0, /* u8 if_bInterfaceClass; Wireless */
|
||||
0x01, /* u8 if_bInterfaceSubClass; Radio Frequency */
|
||||
0x01, /* u8 if_bInterfaceProtocol; Bluetooth */
|
||||
0x00, /* u8 if_iInterface; */
|
||||
static const USBDescDevice desc_device_bluetooth = {
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = 0xe0, /* Wireless */
|
||||
.bDeviceSubClass = 0x01, /* Radio Frequency */
|
||||
.bDeviceProtocol = 0x01, /* Bluetooth */
|
||||
.bMaxPacketSize0 = 64,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 2,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0xc0,
|
||||
.bMaxPower = 0,
|
||||
.nif = ARRAY_SIZE(desc_iface_bluetooth),
|
||||
.ifs = desc_iface_bluetooth,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* endpoint one */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
||||
USB_DIR_IN | USB_EVT_EP, /* u8 ep_bEndpointAddress; */
|
||||
0x03, /* u8 ep_bmAttributes; Interrupt */
|
||||
0x10, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||
0x02, /* u8 ep_bInterval; */
|
||||
|
||||
/* endpoint two */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; */
|
||||
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 const USBDesc desc_bluetooth = {
|
||||
.id = {
|
||||
.idVendor = 0x0a12,
|
||||
.idProduct = 0x0001,
|
||||
.bcdDevice = 0x1958,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = 0,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_bluetooth,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
case DeviceRequest | USB_REQ_GET_STATUS:
|
||||
case InterfaceRequest | USB_REQ_GET_STATUS:
|
||||
case EndpointRequest | USB_REQ_GET_STATUS:
|
||||
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
|
||||
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
|
||||
data[0] = 0x00;
|
||||
data[1] = 0x00;
|
||||
ret = 2;
|
||||
break;
|
||||
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
case InterfaceOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
case EndpointOutRequest | 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:
|
||||
goto fail;
|
||||
case InterfaceOutRequest | USB_REQ_SET_FEATURE:
|
||||
case EndpointOutRequest | 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:
|
||||
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);
|
||||
goto fail;
|
||||
break;
|
||||
case InterfaceRequest | USB_REQ_GET_INTERFACE:
|
||||
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)
|
||||
{
|
||||
struct USBBtState *s = DO_UPCAST(struct USBBtState, dev, dev);
|
||||
s->dev.speed = USB_SPEED_HIGH;
|
||||
usb_desc_init(dev);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -648,6 +552,7 @@ static struct USBDeviceInfo bt_info = {
|
|||
.product_desc = "QEMU BT dongle",
|
||||
.qdev.name = "usb-bt-dongle",
|
||||
.qdev.size = sizeof(struct USBBtState),
|
||||
.usb_desc = &desc_bluetooth,
|
||||
.init = usb_bt_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_bt_handle_reset,
|
||||
|
|
129
hw/usb-bus.c
129
hw/usb-bus.c
|
@ -5,13 +5,20 @@
|
|||
#include "monitor.h"
|
||||
|
||||
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 = {
|
||||
.name = "USB",
|
||||
.size = sizeof(USBBus),
|
||||
.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 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);
|
||||
dev->info = info;
|
||||
dev->auto_attach = 1;
|
||||
QLIST_INIT(&dev->strings);
|
||||
rc = dev->info->init(dev);
|
||||
if (rc == 0 && dev->auto_attach)
|
||||
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,
|
||||
USBDevice *pdev, usb_attachfn attach)
|
||||
USBPortOps *ops, int speedmask)
|
||||
{
|
||||
port->opaque = opaque;
|
||||
port->index = index;
|
||||
port->attach = attach;
|
||||
port->pdev = pdev;
|
||||
port->opaque = opaque;
|
||||
port->index = index;
|
||||
port->ops = ops;
|
||||
port->speedmask = speedmask;
|
||||
QTAILQ_INSERT_TAIL(&bus->free, port, next);
|
||||
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)
|
||||
{
|
||||
if (port->dev)
|
||||
|
@ -140,9 +160,22 @@ static void do_attach(USBDevice *dev)
|
|||
dev->product_desc);
|
||||
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);
|
||||
bus->nfree--;
|
||||
|
||||
|
@ -156,8 +189,9 @@ int usb_device_attach(USBDevice *dev)
|
|||
{
|
||||
USBBus *bus = usb_bus_from_device(dev);
|
||||
|
||||
if (bus->nfree == 1) {
|
||||
/* Create a new hub and chain it on. */
|
||||
if (bus->nfree == 1 && dev->port_path == NULL) {
|
||||
/* Create a new hub and chain it on
|
||||
(unless a physical port location is specified). */
|
||||
usb_create_simple(bus, "usb-hub");
|
||||
}
|
||||
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);
|
||||
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,
|
||||
dev->port ? dev->port->path : "-",
|
||||
usb_speed(dev->speed), dev->product_desc,
|
||||
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)
|
||||
{
|
||||
USBBus *bus;
|
||||
|
@ -253,8 +318,8 @@ void usb_info(Monitor *mon)
|
|||
dev = port->dev;
|
||||
if (!dev)
|
||||
continue;
|
||||
monitor_printf(mon, " Device %d.%d, Speed %s Mb/s, Product %s\n",
|
||||
bus->busnr, dev->addr, usb_speed(dev->speed),
|
||||
monitor_printf(mon, " Device %d.%d, Port %s, Speed %s Mb/s, Product %s\n",
|
||||
bus->busnr, dev->addr, port->path, usb_speed(dev->speed),
|
||||
dev->product_desc);
|
||||
}
|
||||
}
|
||||
|
@ -309,43 +374,3 @@ USBDevice *usbdevice_create(const char *cmdline)
|
|||
}
|
||||
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 "console.h"
|
||||
#include "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "sysemu.h"
|
||||
|
||||
/* HID interface requests */
|
||||
|
@ -73,190 +74,206 @@ typedef struct USBHIDState {
|
|||
void (*datain)(void *);
|
||||
} USBHIDState;
|
||||
|
||||
/* mostly the same values as the Bochs USB Mouse device */
|
||||
static const uint8_t qemu_mouse_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x00, 0x01, /* u16 bcdUSB; v1.0 */
|
||||
|
||||
0x00, /* u8 bDeviceClass; */
|
||||
0x00, /* u8 bDeviceSubClass; */
|
||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||
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; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT_MOUSE,
|
||||
STR_PRODUCT_TABLET,
|
||||
STR_PRODUCT_KEYBOARD,
|
||||
STR_SERIALNUMBER,
|
||||
STR_CONFIG_MOUSE,
|
||||
STR_CONFIG_TABLET,
|
||||
STR_CONFIG_KEYBOARD,
|
||||
};
|
||||
|
||||
static const uint8_t qemu_mouse_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x22, 0x00, /* u16 wTotalLength; */
|
||||
0x01, /* u8 bNumInterfaces; (1) */
|
||||
0x01, /* u8 bConfigurationValue; */
|
||||
0x04, /* u8 iConfiguration; */
|
||||
0xe0, /* u8 bmAttributes;
|
||||
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 USBDescStrings desc_strings = {
|
||||
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||
[STR_PRODUCT_MOUSE] = "QEMU USB Mouse",
|
||||
[STR_PRODUCT_TABLET] = "QEMU USB Tablet",
|
||||
[STR_PRODUCT_KEYBOARD] = "QEMU USB Keyboard",
|
||||
[STR_SERIALNUMBER] = "42", /* == remote wakeup works */
|
||||
[STR_CONFIG_MOUSE] = "HID Mouse",
|
||||
[STR_CONFIG_TABLET] = "HID Tablet",
|
||||
[STR_CONFIG_KEYBOARD] = "HID Keyboard",
|
||||
};
|
||||
|
||||
static const uint8_t qemu_tablet_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x22, 0x00, /* u16 wTotalLength; */
|
||||
0x01, /* u8 bNumInterfaces; (1) */
|
||||
0x01, /* u8 bConfigurationValue; */
|
||||
0x05, /* u8 iConfiguration; */
|
||||
0xa0, /* u8 bmAttributes;
|
||||
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 */
|
||||
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 USBDescIface desc_iface_mouse = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x01, /* boot */
|
||||
.bInterfaceProtocol = 0x02,
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* HID descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_HID, /* u8 bDescriptorType */
|
||||
0x01, 0x00, /* u16 HID_class */
|
||||
0x00, /* u8 country_code */
|
||||
0x01, /* u8 num_descriptors */
|
||||
USB_DT_REPORT, /* u8 type: Report */
|
||||
52, 0, /* u16 len */
|
||||
},
|
||||
},
|
||||
},
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = 4,
|
||||
.bInterval = 0x0a,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
static const uint8_t qemu_keyboard_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
USB_DT_CONFIG, /* u8 bDescriptorType; Configuration */
|
||||
0x22, 0x00, /* u16 wTotalLength; */
|
||||
0x01, /* u8 bNumInterfaces; (1) */
|
||||
0x01, /* u8 bConfigurationValue; */
|
||||
0x06, /* u8 iConfiguration; */
|
||||
0xa0, /* u8 bmAttributes;
|
||||
Bit 7: must be set,
|
||||
6: Self-powered,
|
||||
5: Remote wakeup,
|
||||
4..0: resvd */
|
||||
0x32, /* u8 MaxPower; */
|
||||
static const USBDescIface desc_iface_tablet = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x01, /* boot */
|
||||
.bInterfaceProtocol = 0x02,
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* HID descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_HID, /* u8 bDescriptorType */
|
||||
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:
|
||||
* 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
|
||||
*/
|
||||
static const USBDescIface desc_iface_keyboard = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x01, /* boot */
|
||||
.bInterfaceProtocol = 0x01, /* keyboard */
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* 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 */
|
||||
0x09, /* u8 if_bLength; */
|
||||
USB_DT_INTERFACE, /* u8 if_bDescriptorType; Interface */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x01, /* u8 if_bNumEndpoints; */
|
||||
0x03, /* u8 if_bInterfaceClass; HID */
|
||||
0x01, /* u8 if_bInterfaceSubClass; Boot */
|
||||
0x01, /* u8 if_bInterfaceProtocol; Keyboard */
|
||||
0x07, /* u8 if_iInterface; */
|
||||
static const USBDescDevice desc_device_mouse = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_MOUSE,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.ifs = &desc_iface_mouse,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* HID descriptor */
|
||||
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, 0x00, /* u16 len */
|
||||
static const USBDescDevice desc_device_tablet = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_TABLET,
|
||||
.bmAttributes = 0xa0,
|
||||
.bMaxPower = 50,
|
||||
.ifs = &desc_iface_tablet,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* one endpoint (status change endpoint) */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
USB_DT_ENDPOINT, /* u8 ep_bDescriptorType; Endpoint */
|
||||
USB_DIR_IN | 0x01, /* 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 USBDescDevice desc_device_keyboard = {
|
||||
.bcdUSB = 0x0100,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.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[] = {
|
||||
|
@ -412,6 +429,8 @@ static void usb_hid_changed(USBHIDState *hs)
|
|||
|
||||
if (hs->datain)
|
||||
hs->datain(hs->datain_opaque);
|
||||
|
||||
usb_wakeup(&hs->dev);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
|
@ -870,7 +798,8 @@ static void usb_hid_handle_destroy(USBDevice *dev)
|
|||
static int usb_hid_initfn(USBDevice *dev, int kind)
|
||||
{
|
||||
USBHIDState *s = DO_UPCAST(USBHIDState, dev, dev);
|
||||
s->dev.speed = USB_SPEED_FULL;
|
||||
|
||||
usb_desc_init(dev);
|
||||
s->kind = kind;
|
||||
|
||||
if (s->kind == USB_MOUSE) {
|
||||
|
@ -915,6 +844,7 @@ static struct USBDeviceInfo hid_info[] = {
|
|||
.qdev.name = "usb-tablet",
|
||||
.usbdevice_name = "tablet",
|
||||
.qdev.size = sizeof(USBHIDState),
|
||||
.usb_desc = &desc_tablet,
|
||||
.init = usb_tablet_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_mouse_handle_reset,
|
||||
|
@ -926,6 +856,7 @@ static struct USBDeviceInfo hid_info[] = {
|
|||
.qdev.name = "usb-mouse",
|
||||
.usbdevice_name = "mouse",
|
||||
.qdev.size = sizeof(USBHIDState),
|
||||
.usb_desc = &desc_mouse,
|
||||
.init = usb_mouse_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_mouse_handle_reset,
|
||||
|
@ -937,6 +868,7 @@ static struct USBDeviceInfo hid_info[] = {
|
|||
.qdev.name = "usb-kbd",
|
||||
.usbdevice_name = "keyboard",
|
||||
.qdev.size = sizeof(USBHIDState),
|
||||
.usb_desc = &desc_keyboard,
|
||||
.init = usb_keyboard_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.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 "usb.h"
|
||||
#include "usb-desc.h"
|
||||
|
||||
//#define DEBUG
|
||||
|
||||
#define MAX_PORTS 8
|
||||
#define NUM_PORTS 8
|
||||
|
||||
typedef struct USBHubPort {
|
||||
USBPort port;
|
||||
|
@ -36,8 +37,7 @@ typedef struct USBHubPort {
|
|||
|
||||
typedef struct USBHubState {
|
||||
USBDevice dev;
|
||||
int nb_ports;
|
||||
USBHubPort ports[MAX_PORTS];
|
||||
USBHubPort ports[NUM_PORTS];
|
||||
} USBHubState;
|
||||
|
||||
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
|
||||
|
@ -83,6 +83,60 @@ typedef struct USBHubState {
|
|||
|
||||
/* 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[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
|
@ -164,37 +218,51 @@ static const uint8_t qemu_hub_hub_descriptor[] =
|
|||
/* 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;
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
if (dev) {
|
||||
if (port->port.dev)
|
||||
usb_attach(port1, NULL);
|
||||
|
||||
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);
|
||||
port->wPortStatus |= PORT_STAT_CONNECTION;
|
||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||
port->wPortStatus |= PORT_STAT_LOW_SPEED;
|
||||
} else {
|
||||
dev = port->port.dev;
|
||||
if (dev) {
|
||||
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
||||
port->wPortChange |= PORT_STAT_C_CONNECTION;
|
||||
if (port->wPortStatus & PORT_STAT_ENABLE) {
|
||||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||
port->wPortChange |= PORT_STAT_C_ENABLE;
|
||||
}
|
||||
/* send the detach message */
|
||||
usb_send_msg(dev, USB_MSG_DETACH);
|
||||
port->port.dev = NULL;
|
||||
}
|
||||
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_hub_detach(USBPort *port1)
|
||||
{
|
||||
USBHubState *s = port1->opaque;
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
port->wPortStatus &= ~PORT_STAT_CONNECTION;
|
||||
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;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, request, value, index, length, data);
|
||||
if (ret >= 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
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:
|
||||
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||
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_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:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
|
@ -315,8 +308,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
|||
{
|
||||
unsigned int n = index - 1;
|
||||
USBHubPort *port;
|
||||
if (n >= s->nb_ports)
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
}
|
||||
port = &s->ports[n];
|
||||
data[0] = port->wPortStatus;
|
||||
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;
|
||||
USBHubPort *port;
|
||||
USBDevice *dev;
|
||||
if (n >= s->nb_ports)
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
}
|
||||
port = &s->ports[n];
|
||||
dev = port->port.dev;
|
||||
switch(value) {
|
||||
|
@ -367,8 +362,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
|||
unsigned int n = index - 1;
|
||||
USBHubPort *port;
|
||||
|
||||
if (n >= s->nb_ports)
|
||||
if (n >= NUM_PORTS) {
|
||||
goto fail;
|
||||
}
|
||||
port = &s->ports[n];
|
||||
switch(value) {
|
||||
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;
|
||||
memcpy(data, qemu_hub_hub_descriptor,
|
||||
sizeof(qemu_hub_hub_descriptor));
|
||||
data[2] = s->nb_ports;
|
||||
data[2] = NUM_PORTS;
|
||||
|
||||
/* fill DeviceRemovable bits */
|
||||
limit = ((s->nb_ports + 1 + 7) / 8) + 7;
|
||||
limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
|
||||
for (n = 7; n < limit; n++) {
|
||||
data[n] = 0x00;
|
||||
var_hub_size++;
|
||||
}
|
||||
|
||||
/* fill PortPwrCtrlMask bits */
|
||||
limit = limit + ((s->nb_ports + 7) / 8);
|
||||
limit = limit + ((NUM_PORTS + 7) / 8);
|
||||
for (;n < limit; n++) {
|
||||
data[n] = 0xff;
|
||||
var_hub_size++;
|
||||
|
@ -442,14 +438,14 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
|
|||
USBHubPort *port;
|
||||
unsigned int status;
|
||||
int i, n;
|
||||
n = (s->nb_ports + 1 + 7) / 8;
|
||||
n = (NUM_PORTS + 1 + 7) / 8;
|
||||
if (p->len == 1) { /* FreeBSD workaround */
|
||||
n = 1;
|
||||
} else if (n > p->len) {
|
||||
return USB_RET_BABBLE;
|
||||
}
|
||||
status = 0;
|
||||
for(i = 0; i < s->nb_ports; i++) {
|
||||
for(i = 0; i < NUM_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
if (port->wPortChange)
|
||||
status |= (1 << (i + 1));
|
||||
|
@ -481,7 +477,7 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
|
|||
USBDevice *dev;
|
||||
int i, ret;
|
||||
|
||||
for(i = 0; i < s->nb_ports; i++) {
|
||||
for(i = 0; i < NUM_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
dev = port->port.dev;
|
||||
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
|
||||
|
@ -518,24 +514,30 @@ static void usb_hub_handle_destroy(USBDevice *dev)
|
|||
USBHubState *s = (USBHubState *)dev;
|
||||
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),
|
||||
&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)
|
||||
{
|
||||
USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
|
||||
USBHubPort *port;
|
||||
int i;
|
||||
|
||||
s->dev.speed = USB_SPEED_FULL,
|
||||
s->nb_ports = MAX_PORTS; /* FIXME: make configurable */
|
||||
for (i = 0; i < s->nb_ports; i++) {
|
||||
usb_desc_init(dev);
|
||||
for (i = 0; i < NUM_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
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->wPortChange = 0;
|
||||
}
|
||||
|
@ -547,8 +549,10 @@ static struct USBDeviceInfo hub_info = {
|
|||
.qdev.name = "usb-hub",
|
||||
.qdev.fw_name = "hub",
|
||||
.qdev.size = sizeof(USBHubState),
|
||||
.usb_desc = &desc_hub,
|
||||
.init = usb_hub_initfn,
|
||||
.handle_packet = usb_hub_handle_packet,
|
||||
.handle_attach = usb_hub_handle_attach,
|
||||
.handle_reset = usb_hub_handle_reset,
|
||||
.handle_control = usb_hub_handle_control,
|
||||
.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-config.h"
|
||||
#include "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "scsi.h"
|
||||
#include "console.h"
|
||||
#include "monitor.h"
|
||||
|
@ -72,69 +73,102 @@ struct usb_msd_csw {
|
|||
uint8_t status;
|
||||
};
|
||||
|
||||
static const uint8_t qemu_msd_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x00, 0x01, /* u16 bcdUSB; v1.0 */
|
||||
|
||||
0x00, /* u8 bDeviceClass; */
|
||||
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; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT,
|
||||
STR_SERIALNUMBER,
|
||||
STR_CONFIG_FULL,
|
||||
STR_CONFIG_HIGH,
|
||||
};
|
||||
|
||||
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 */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x20, 0x00, /* u16 wTotalLength; */
|
||||
0x01, /* u8 bNumInterfaces; (1) */
|
||||
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; */
|
||||
static const USBDescIface desc_iface_full = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||
.bInterfaceSubClass = 0x06, /* SCSI */
|
||||
.bInterfaceProtocol = 0x50, /* Bulk */
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/* one interface */
|
||||
0x09, /* u8 if_bLength; */
|
||||
0x04, /* u8 if_bDescriptorType; Interface */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x02, /* u8 if_bNumEndpoints; */
|
||||
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */
|
||||
0x06, /* u8 if_bInterfaceSubClass; SCSI */
|
||||
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */
|
||||
0x00, /* u8 if_iInterface; */
|
||||
static const USBDescDevice desc_device_full = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.iConfiguration = STR_CONFIG_FULL,
|
||||
.bmAttributes = 0xc0,
|
||||
.ifs = &desc_iface_full,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* Bulk-In endpoint */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||
0x00, /* u8 ep_bInterval; */
|
||||
static const USBDescIface desc_iface_high = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_MASS_STORAGE,
|
||||
.bInterfaceSubClass = 0x06, /* SCSI */
|
||||
.bInterfaceProtocol = 0x50, /* Bulk */
|
||||
.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 */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||
0x00 /* u8 ep_bInterval; */
|
||||
static const USBDescDevice desc_device_high = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bMaxPacketSize0 = 64,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.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)
|
||||
|
@ -153,7 +187,7 @@ static void usb_msd_copy_data(MSDState *s)
|
|||
s->usb_buf += len;
|
||||
s->scsi_buf += 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) {
|
||||
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
||||
} 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;
|
||||
int len;
|
||||
|
||||
csw.sig = cpu_to_le32(0x53425355);
|
||||
csw.tag = cpu_to_le32(s->tag);
|
||||
csw.residue = s->residue;
|
||||
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,
|
||||
|
@ -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) {
|
||||
/* A deferred packet with no write data remaining must be
|
||||
the status read packet. */
|
||||
usb_msd_send_status(s);
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
|
@ -338,7 +306,6 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
|||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
@ -461,15 +428,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
if (len < 13)
|
||||
goto fail;
|
||||
|
||||
s->usb_len = len;
|
||||
s->usb_buf = data;
|
||||
usb_msd_send_status(s);
|
||||
usb_msd_send_status(s, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
ret = 13;
|
||||
break;
|
||||
|
||||
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)
|
||||
len = s->data_len;
|
||||
s->usb_buf = data;
|
||||
|
@ -524,6 +489,7 @@ static int usb_msd_initfn(USBDevice *dev)
|
|||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
BlockDriverState *bs = s->conf.bs;
|
||||
DriveInfo *dinfo;
|
||||
|
||||
if (!bs) {
|
||||
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);
|
||||
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);
|
||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0);
|
||||
if (!s->scsi_dev) {
|
||||
|
@ -625,8 +596,10 @@ static struct USBDeviceInfo msd_info = {
|
|||
.product_desc = "QEMU USB MSD",
|
||||
.qdev.name = "usb-storage",
|
||||
.qdev.size = sizeof(MSDState),
|
||||
.usb_desc = &desc,
|
||||
.init = usb_msd_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_attach = usb_desc_attach,
|
||||
.handle_reset = usb_msd_handle_reset,
|
||||
.handle_control = usb_msd_handle_control,
|
||||
.handle_data = usb_msd_handle_data,
|
||||
|
|
|
@ -259,7 +259,13 @@
|
|||
#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 {
|
||||
uint16_t faddr[2];
|
||||
|
@ -343,7 +349,9 @@ struct MUSBState {
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -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. */
|
||||
static void musb_attach(USBPort *port, USBDevice *dev)
|
||||
static void musb_attach(USBPort *port)
|
||||
{
|
||||
MUSBState *s = (MUSBState *) port->opaque;
|
||||
USBDevice *curr;
|
||||
|
||||
port = &s->port;
|
||||
curr = port->dev;
|
||||
musb_intr_set(s, musb_irq_vbus_request, 1);
|
||||
musb_session_update(s, 0, s->session);
|
||||
}
|
||||
|
||||
if (dev) {
|
||||
if (curr) {
|
||||
usb_attach(port, NULL);
|
||||
/* TODO: signal some interrupts */
|
||||
}
|
||||
static void musb_detach(USBPort *port)
|
||||
{
|
||||
MUSBState *s = (MUSBState *) port->opaque;
|
||||
|
||||
musb_intr_set(s, musb_irq_vbus_request, 1);
|
||||
|
||||
/* 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);
|
||||
musb_intr_set(s, musb_irq_disconnect, 1);
|
||||
musb_session_update(s, 1, s->session);
|
||||
}
|
||||
|
||||
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 "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "net.h"
|
||||
#include "qemu-queue.h"
|
||||
#include "sysemu.h"
|
||||
|
@ -89,182 +90,209 @@ enum usbstring_idx {
|
|||
|
||||
#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
|
||||
|
||||
/*
|
||||
* mostly the same descriptor as the linux gadget rndis driver
|
||||
*/
|
||||
static const uint8_t qemu_net_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
USB_DT_DEVICE, /* u8 bDescriptorType; Device */
|
||||
0x00, 0x02, /* u16 bcdUSB; v2.0 */
|
||||
USB_CLASS_COMM, /* u8 bDeviceClass; */
|
||||
0x00, /* u8 bDeviceSubClass; */
|
||||
0x00, /* u8 bDeviceProtocol; [ low/full only ] */
|
||||
0x40, /* u8 bMaxPacketSize0 */
|
||||
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 USBDescStrings 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 const uint8_t qemu_net_rndis_config_descriptor[] = {
|
||||
/* Configuration Descriptor */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_CONFIG, /* u8 bDescriptorType */
|
||||
0x43, 0x00, /* le16 wTotalLength */
|
||||
0x02, /* u8 bNumInterfaces */
|
||||
DEV_RNDIS_CONFIG_VALUE, /* u8 bConfigurationValue */
|
||||
STRING_RNDIS, /* u8 iConfiguration */
|
||||
0xc0, /* u8 bmAttributes */
|
||||
0x32, /* u8 bMaxPower */
|
||||
/* RNDIS Control Interface */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
||||
0x00, /* u8 bInterfaceNumber */
|
||||
0x00, /* u8 bAlternateSetting */
|
||||
0x01, /* u8 bNumEndpoints */
|
||||
USB_CLASS_COMM, /* u8 bInterfaceClass */
|
||||
USB_CDC_SUBCLASS_ACM, /* u8 bInterfaceSubClass */
|
||||
USB_CDC_ACM_PROTO_VENDOR, /* u8 bInterfaceProtocol */
|
||||
STRING_RNDIS_CONTROL, /* u8 iInterface */
|
||||
/* Header Descriptor */
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||
0x10, 0x01, /* le16 bcdCDC */
|
||||
/* Call Management Descriptor */
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bmCapabilities */
|
||||
0x01, /* u8 bDataInterface */
|
||||
/* ACM Descriptor */
|
||||
0x04, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bmCapabilities */
|
||||
/* Union Descriptor */
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bMasterInterface0 */
|
||||
0x01, /* u8 bSlaveInterface0 */
|
||||
/* Status Descriptor */
|
||||
0x07, /* u8 bLength */
|
||||
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 */
|
||||
1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */
|
||||
/* RNDIS Data Interface */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
||||
0x01, /* u8 bInterfaceNumber */
|
||||
0x00, /* u8 bAlternateSetting */
|
||||
0x02, /* u8 bNumEndpoints */
|
||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
||||
0x00, /* u8 bInterfaceSubClass */
|
||||
0x00, /* u8 bInterfaceProtocol */
|
||||
STRING_DATA, /* u8 iInterface */
|
||||
/* Source Endpoint */
|
||||
0x07, /* u8 bLength */
|
||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
||||
USB_DIR_IN | 2, /* u8 bEndpointAddress */
|
||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
||||
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 */
|
||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
||||
0x00 /* u8 bInterval */
|
||||
static const USBDescIface desc_iface_rndis[] = {
|
||||
{
|
||||
/* RNDIS Control Interface */
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ACM,
|
||||
.bInterfaceProtocol = USB_CDC_ACM_PROTO_VENDOR,
|
||||
.iInterface = STRING_RNDIS_CONTROL,
|
||||
.ndesc = 4,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* Header Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||
0x10, 0x01, /* le16 bcdCDC */
|
||||
},
|
||||
},{
|
||||
/* Call Management Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_CALL_MANAGEMENT_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bmCapabilities */
|
||||
0x01, /* u8 bDataInterface */
|
||||
},
|
||||
},{
|
||||
/* ACM Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x04, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_ACM_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bmCapabilities */
|
||||
},
|
||||
},{
|
||||
/* Union Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bMasterInterface0 */
|
||||
0x01, /* u8 bSlaveInterface0 */
|
||||
},
|
||||
},
|
||||
},
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = STATUS_BYTECOUNT,
|
||||
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||
},
|
||||
}
|
||||
},{
|
||||
/* RNDIS Data Interface */
|
||||
.bInterfaceNumber = 1,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
.iInterface = STRING_DATA,
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const uint8_t qemu_net_cdc_config_descriptor[] = {
|
||||
/* Configuration Descriptor */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_CONFIG, /* u8 bDescriptorType */
|
||||
0x50, 0x00, /* le16 wTotalLength */
|
||||
0x02, /* u8 bNumInterfaces */
|
||||
DEV_CONFIG_VALUE, /* u8 bConfigurationValue */
|
||||
STRING_CDC, /* u8 iConfiguration */
|
||||
0xc0, /* u8 bmAttributes */
|
||||
0x32, /* u8 bMaxPower */
|
||||
/* CDC Control Interface */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
||||
0x00, /* u8 bInterfaceNumber */
|
||||
0x00, /* u8 bAlternateSetting */
|
||||
0x01, /* u8 bNumEndpoints */
|
||||
USB_CLASS_COMM, /* u8 bInterfaceClass */
|
||||
USB_CDC_SUBCLASS_ETHERNET, /* u8 bInterfaceSubClass */
|
||||
USB_CDC_PROTO_NONE, /* u8 bInterfaceProtocol */
|
||||
STRING_CONTROL, /* u8 iInterface */
|
||||
/* Header Descriptor */
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||
0x10, 0x01, /* le16 bcdCDC */
|
||||
/* Union Descriptor */
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bMasterInterface0 */
|
||||
0x01, /* u8 bSlaveInterface0 */
|
||||
/* Ethernet Descriptor */
|
||||
0x0d, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */
|
||||
STRING_ETHADDR, /* u8 iMACAddress */
|
||||
0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */
|
||||
ETH_FRAME_LEN & 0xff, ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */
|
||||
0x00, 0x00, /* le16 wNumberMCFilters */
|
||||
0x00, /* u8 bNumberPowerFilters */
|
||||
/* Status Descriptor */
|
||||
0x07, /* u8 bLength */
|
||||
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 */
|
||||
1 << LOG2_STATUS_INTERVAL_MSEC, /* u8 bInterval */
|
||||
/* CDC Data (nop) Interface */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
||||
0x01, /* u8 bInterfaceNumber */
|
||||
0x00, /* u8 bAlternateSetting */
|
||||
0x00, /* u8 bNumEndpoints */
|
||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
||||
0x00, /* u8 bInterfaceSubClass */
|
||||
0x00, /* u8 bInterfaceProtocol */
|
||||
0x00, /* u8 iInterface */
|
||||
/* CDC Data Interface */
|
||||
0x09, /* u8 bLength */
|
||||
USB_DT_INTERFACE, /* u8 bDescriptorType */
|
||||
0x01, /* u8 bInterfaceNumber */
|
||||
0x01, /* u8 bAlternateSetting */
|
||||
0x02, /* u8 bNumEndpoints */
|
||||
USB_CLASS_CDC_DATA, /* u8 bInterfaceClass */
|
||||
0x00, /* u8 bInterfaceSubClass */
|
||||
0x00, /* u8 bInterfaceProtocol */
|
||||
STRING_DATA, /* u8 iInterface */
|
||||
/* Source Endpoint */
|
||||
0x07, /* u8 bLength */
|
||||
USB_DT_ENDPOINT, /* u8 bDescriptorType */
|
||||
USB_DIR_IN | 2, /* u8 bEndpointAddress */
|
||||
USB_ENDPOINT_XFER_BULK, /* u8 bmAttributes */
|
||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
||||
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 */
|
||||
0x40, 0x00, /* le16 wMaxPacketSize */
|
||||
0x00 /* u8 bInterval */
|
||||
static const USBDescIface desc_iface_cdc[] = {
|
||||
{
|
||||
/* CDC Control Interface */
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_COMM,
|
||||
.bInterfaceSubClass = USB_CDC_SUBCLASS_ETHERNET,
|
||||
.bInterfaceProtocol = USB_CDC_PROTO_NONE,
|
||||
.iInterface = STRING_CONTROL,
|
||||
.ndesc = 3,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* Header Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_HEADER_TYPE, /* u8 bDescriptorSubType */
|
||||
0x10, 0x01, /* le16 bcdCDC */
|
||||
},
|
||||
},{
|
||||
/* Union Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x05, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_UNION_TYPE, /* u8 bDescriptorSubType */
|
||||
0x00, /* u8 bMasterInterface0 */
|
||||
0x01, /* u8 bSlaveInterface0 */
|
||||
},
|
||||
},{
|
||||
/* Ethernet Descriptor */
|
||||
.data = (uint8_t[]) {
|
||||
0x0d, /* u8 bLength */
|
||||
USB_DT_CS_INTERFACE, /* u8 bDescriptorType */
|
||||
USB_CDC_ETHERNET_TYPE, /* u8 bDescriptorSubType */
|
||||
STRING_ETHADDR, /* u8 iMACAddress */
|
||||
0x00, 0x00, 0x00, 0x00, /* le32 bmEthernetStatistics */
|
||||
ETH_FRAME_LEN & 0xff,
|
||||
ETH_FRAME_LEN >> 8, /* le16 wMaxSegmentSize */
|
||||
0x00, 0x00, /* le16 wNumberMCFilters */
|
||||
0x00, /* u8 bNumberPowerFilters */
|
||||
},
|
||||
},
|
||||
},
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_INT,
|
||||
.wMaxPacketSize = STATUS_BYTECOUNT,
|
||||
.bInterval = 1 << LOG2_STATUS_INTERVAL_MSEC,
|
||||
},
|
||||
}
|
||||
},{
|
||||
/* CDC Data Interface (off) */
|
||||
.bInterfaceNumber = 1,
|
||||
.bAlternateSetting = 0,
|
||||
.bNumEndpoints = 0,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
},{
|
||||
/* CDC Data Interface */
|
||||
.bInterfaceNumber = 1,
|
||||
.bAlternateSetting = 1,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = USB_CLASS_CDC_DATA,
|
||||
.iInterface = STRING_DATA,
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 0x40,
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static const USBDescDevice desc_device_net = {
|
||||
.bcdUSB = 0x0200,
|
||||
.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 {
|
||||
USBDevice dev;
|
||||
|
||||
unsigned int rndis;
|
||||
enum rndis_state rndis_state;
|
||||
uint32_t medium;
|
||||
uint32_t speed;
|
||||
|
@ -620,6 +647,11 @@ typedef struct USBNetState {
|
|||
QTAILQ_HEAD(rndis_resp_head, rndis_response) rndis_resp;
|
||||
} 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,
|
||||
uint8_t *inbuf, unsigned int inlen, uint8_t *outbuf,
|
||||
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,
|
||||
int index, int length, uint8_t *data)
|
||||
{
|
||||
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) {
|
||||
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:
|
||||
if (!s->rndis || value || index != 0)
|
||||
if (!is_rndis(s) || value || index != 0) {
|
||||
goto fail;
|
||||
}
|
||||
#ifdef TRAFFIC_DEBUG
|
||||
{
|
||||
unsigned int i;
|
||||
|
@ -1079,8 +1075,9 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
|||
break;
|
||||
|
||||
case ClassInterfaceRequest | USB_CDC_GET_ENCAPSULATED_RESPONSE:
|
||||
if (!s->rndis || value || index != 0)
|
||||
if (!is_rndis(s) || value || index != 0) {
|
||||
goto fail;
|
||||
}
|
||||
ret = rndis_get_response(s, data);
|
||||
if (!ret) {
|
||||
data[0] = 0;
|
||||
|
@ -1100,85 +1097,6 @@ static int usb_net_handle_control(USBDevice *dev, int request, int value,
|
|||
#endif
|
||||
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 InterfaceRequest | USB_REQ_GET_INTERFACE:
|
||||
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);
|
||||
s->in_ptr += ret;
|
||||
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 */
|
||||
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);
|
||||
s->out_ptr += sz;
|
||||
|
||||
if (!s->rndis) {
|
||||
if (!is_rndis(s)) {
|
||||
if (ret < 64) {
|
||||
qemu_send_packet(&s->nic->nc, s->out_buf, s->out_ptr);
|
||||
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;
|
||||
struct rndis_packet_msg_type *msg;
|
||||
|
||||
if (s->rndis) {
|
||||
if (is_rndis(s)) {
|
||||
msg = (struct rndis_packet_msg_type *) s->in_buf;
|
||||
if (!s->rndis_state == RNDIS_DATA_INITIALIZED)
|
||||
return -1;
|
||||
|
@ -1405,8 +1323,9 @@ static int usbnet_can_receive(VLANClientState *nc)
|
|||
{
|
||||
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 !s->in_len;
|
||||
}
|
||||
|
@ -1439,9 +1358,8 @@ static int usb_net_initfn(USBDevice *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;
|
||||
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[4],
|
||||
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");
|
||||
return 0;
|
||||
|
@ -1500,6 +1419,7 @@ static struct USBDeviceInfo net_info = {
|
|||
.qdev.name = "usb-net",
|
||||
.qdev.fw_name = "network",
|
||||
.qdev.size = sizeof(USBNetState),
|
||||
.usb_desc = &desc_net,
|
||||
.init = usb_net_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.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. */
|
||||
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;
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t old_state = port->ctrl;
|
||||
|
||||
if (dev) {
|
||||
if (port->port.dev) {
|
||||
usb_attach(port1, NULL);
|
||||
}
|
||||
/* 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);
|
||||
/* 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;
|
||||
}
|
||||
DPRINTF("usb-ohci: Detached port %d\n", port1->index);
|
||||
|
||||
if (old_state != port->ctrl)
|
||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||
|
@ -413,8 +407,9 @@ static void ohci_reset(void *opaque)
|
|||
{
|
||||
port = &ohci->rhport[i];
|
||||
port->ctrl = 0;
|
||||
if (port->port.dev)
|
||||
ohci_attach(&port->port, port->port.dev);
|
||||
if (port->port.dev) {
|
||||
usb_attach(&port->port, port->port.dev);
|
||||
}
|
||||
}
|
||||
if (ohci->async_td) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
|
@ -1669,6 +1664,11 @@ static CPUWriteMemoryFunc * const ohci_writefn[3]={
|
|||
ohci_mem_write
|
||||
};
|
||||
|
||||
static USBPortOps ohci_port_ops = {
|
||||
.attach = ohci_attach,
|
||||
.detach = ohci_detach,
|
||||
};
|
||||
|
||||
static void usb_ohci_init(OHCIState *ohci, DeviceState *dev,
|
||||
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);
|
||||
ohci->num_ports = num_ports;
|
||||
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;
|
||||
|
|
228
hw/usb-serial.c
228
hw/usb-serial.c
|
@ -11,6 +11,7 @@
|
|||
#include "qemu-common.h"
|
||||
#include "qemu-error.h"
|
||||
#include "usb.h"
|
||||
#include "usb-desc.h"
|
||||
#include "qemu-char.h"
|
||||
|
||||
//#define DEBUG_Serial
|
||||
|
@ -91,8 +92,6 @@ do { printf("usb-serial: " fmt , ## __VA_ARGS__); } while (0)
|
|||
|
||||
typedef struct {
|
||||
USBDevice dev;
|
||||
uint32_t vendorid;
|
||||
uint32_t productid;
|
||||
uint8_t recv_buf[RECV_BUF];
|
||||
uint16_t recv_ptr;
|
||||
uint16_t recv_used;
|
||||
|
@ -104,69 +103,78 @@ typedef struct {
|
|||
CharDriverState *cs;
|
||||
} USBSerialState;
|
||||
|
||||
static const uint8_t qemu_serial_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x00, 0x02, /* u16 bcdUSB; v2.0 */
|
||||
|
||||
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; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT_SERIAL,
|
||||
STR_PRODUCT_BRAILLE,
|
||||
STR_SERIALNUMBER,
|
||||
};
|
||||
|
||||
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 */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x20, 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 */
|
||||
100/2, /* u8 MaxPower; */
|
||||
static const USBDescIface desc_iface0 = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 2,
|
||||
.bInterfaceClass = 0xff,
|
||||
.bInterfaceSubClass = 0xff,
|
||||
.bInterfaceProtocol = 0xff,
|
||||
.eps = (USBDescEndpoint[]) {
|
||||
{
|
||||
.bEndpointAddress = USB_DIR_IN | 0x01,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},{
|
||||
.bEndpointAddress = USB_DIR_OUT | 0x02,
|
||||
.bmAttributes = USB_ENDPOINT_XFER_BULK,
|
||||
.wMaxPacketSize = 64,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/* one interface */
|
||||
0x09, /* u8 if_bLength; */
|
||||
0x04, /* u8 if_bDescriptorType; Interface */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x02, /* u8 if_bNumEndpoints; */
|
||||
0xff, /* u8 if_bInterfaceClass; Vendor Specific */
|
||||
0xff, /* u8 if_bInterfaceSubClass; Vendor Specific */
|
||||
0xff, /* u8 if_bInterfaceProtocol; Vendor Specific */
|
||||
0x02, /* u8 if_iInterface; */
|
||||
static const USBDescDevice desc_device = {
|
||||
.bcdUSB = 0x0200,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 50,
|
||||
.ifs = &desc_iface0,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* Bulk-In endpoint */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */
|
||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||
0x00, /* u8 ep_bInterval; */
|
||||
static const USBDesc desc_serial = {
|
||||
.id = {
|
||||
.idVendor = 0x0403,
|
||||
.idProduct = 0x6001,
|
||||
.bcdDevice = 0x0400,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = STR_PRODUCT_SERIAL,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
/* Bulk-Out endpoint */
|
||||
0x07, /* u8 ep_bLength; */
|
||||
0x05, /* u8 ep_bDescriptorType; Endpoint */
|
||||
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */
|
||||
0x02, /* u8 ep_bmAttributes; Bulk */
|
||||
0x40, 0x00, /* u16 ep_wMaxPacketSize; */
|
||||
0x00 /* u8 ep_bInterval; */
|
||||
static const USBDesc desc_braille = {
|
||||
.id = {
|
||||
.idVendor = 0x0403,
|
||||
.idProduct = 0xfe72,
|
||||
.bcdDevice = 0x0400,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = STR_PRODUCT_BRAILLE,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
|
@ -543,7 +478,8 @@ static void usb_serial_event(void *opaque, int event)
|
|||
static int usb_serial_initfn(USBDevice *dev)
|
||||
{
|
||||
USBSerialState *s = DO_UPCAST(USBSerialState, dev, dev);
|
||||
s->dev.speed = USB_SPEED_FULL;
|
||||
|
||||
usb_desc_init(dev);
|
||||
|
||||
if (!s->cs) {
|
||||
error_report("Property chardev is required");
|
||||
|
@ -633,6 +569,7 @@ static struct USBDeviceInfo serial_info = {
|
|||
.product_desc = "QEMU USB Serial",
|
||||
.qdev.name = "usb-serial",
|
||||
.qdev.size = sizeof(USBSerialState),
|
||||
.usb_desc = &desc_serial,
|
||||
.init = usb_serial_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_serial_handle_reset,
|
||||
|
@ -642,9 +579,7 @@ static struct USBDeviceInfo serial_info = {
|
|||
.usbdevice_name = "serial",
|
||||
.usbdevice_init = usb_serial_init,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||
DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403),
|
||||
DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0x6001),
|
||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
||||
|
@ -653,6 +588,7 @@ static struct USBDeviceInfo braille_info = {
|
|||
.product_desc = "QEMU USB Braille",
|
||||
.qdev.name = "usb-braille",
|
||||
.qdev.size = sizeof(USBSerialState),
|
||||
.usb_desc = &desc_braille,
|
||||
.init = usb_serial_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.handle_reset = usb_serial_handle_reset,
|
||||
|
@ -662,9 +598,7 @@ static struct USBDeviceInfo braille_info = {
|
|||
.usbdevice_name = "braille",
|
||||
.usbdevice_init = usb_braille_init,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||
DEFINE_PROP_HEX32("vendorid", USBSerialState, vendorid, 0x0403),
|
||||
DEFINE_PROP_HEX32("productid", USBSerialState, productid, 0xfe72),
|
||||
DEFINE_PROP_CHR("chardev", USBSerialState, cs),
|
||||
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_TIMEOUT (1 << 18)
|
||||
|
||||
#define UHCI_PORT_SUSPEND (1 << 12)
|
||||
#define UHCI_PORT_RESET (1 << 9)
|
||||
#define UHCI_PORT_LSDA (1 << 8)
|
||||
#define UHCI_PORT_RD (1 << 6)
|
||||
#define UHCI_PORT_ENC (1 << 3)
|
||||
#define UHCI_PORT_EN (1 << 2)
|
||||
#define UHCI_PORT_CSC (1 << 1)
|
||||
#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_MAX_LOOPS 100
|
||||
|
@ -307,8 +312,6 @@ static UHCIAsync *uhci_async_find_td(UHCIState *s, uint32_t addr, uint32_t token
|
|||
return match;
|
||||
}
|
||||
|
||||
static void uhci_attach(USBPort *port1, USBDevice *dev);
|
||||
|
||||
static void uhci_update_irq(UHCIState *s)
|
||||
{
|
||||
int level;
|
||||
|
@ -348,8 +351,9 @@ static void uhci_reset(void *opaque)
|
|||
for(i = 0; i < NB_PORTS; i++) {
|
||||
port = &s->ports[i];
|
||||
port->ctrl = 0x0080;
|
||||
if (port->port.dev)
|
||||
uhci_attach(&port->port, port->port.dev);
|
||||
if (port->port.dev) {
|
||||
usb_attach(&port->port, port->port.dev);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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 */
|
||||
port->ctrl &= ~(val & 0x000a);
|
||||
port->ctrl &= ~(val & UHCI_PORT_WRITE_CLEAR);
|
||||
}
|
||||
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;
|
||||
UHCIPort *port = &s->ports[port1->index];
|
||||
|
||||
if (dev) {
|
||||
if (port->port.dev) {
|
||||
usb_attach(port1, NULL);
|
||||
}
|
||||
/* set connect status */
|
||||
port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
|
||||
/* set connect status */
|
||||
port->ctrl |= UHCI_PORT_CCS | UHCI_PORT_CSC;
|
||||
|
||||
/* update speed */
|
||||
if (dev->speed == USB_SPEED_LOW)
|
||||
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);
|
||||
/* update speed */
|
||||
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||
port->ctrl |= UHCI_PORT_LSDA;
|
||||
} else {
|
||||
/* 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;
|
||||
}
|
||||
port->ctrl &= ~UHCI_PORT_LSDA;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static USBPortOps uhci_port_ops = {
|
||||
.attach = uhci_attach,
|
||||
.detach = uhci_detach,
|
||||
.wakeup = uhci_wakeup,
|
||||
};
|
||||
|
||||
static int usb_uhci_common_initfn(UHCIState *s)
|
||||
{
|
||||
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);
|
||||
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->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 "console.h"
|
||||
#include "usb.h"
|
||||
#include "usb-desc.h"
|
||||
|
||||
/* Interface requests */
|
||||
#define WACOM_GET_REPORT 0x2101
|
||||
|
@ -54,68 +55,75 @@ typedef struct USBWacomState {
|
|||
int changed;
|
||||
} USBWacomState;
|
||||
|
||||
static const uint8_t qemu_wacom_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x10, 0x10, /* u16 bcdUSB; v1.10 */
|
||||
|
||||
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; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT,
|
||||
STR_SERIALNUMBER,
|
||||
};
|
||||
|
||||
static const uint8_t qemu_wacom_config_descriptor[] = {
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
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; */
|
||||
static const USBDescStrings desc_strings = {
|
||||
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||
[STR_PRODUCT] = "Wacom PenPartner",
|
||||
[STR_SERIALNUMBER] = "1",
|
||||
};
|
||||
|
||||
/* 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; HID */
|
||||
0x01, /* u8 if_bInterfaceSubClass; Boot */
|
||||
0x02, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
||||
0x00, /* u8 if_iInterface; */
|
||||
static const USBDescIface desc_iface_wacom = {
|
||||
.bInterfaceNumber = 0,
|
||||
.bNumEndpoints = 1,
|
||||
.bInterfaceClass = USB_CLASS_HID,
|
||||
.bInterfaceSubClass = 0x01, /* boot */
|
||||
.bInterfaceProtocol = 0x02,
|
||||
.ndesc = 1,
|
||||
.descs = (USBDescOther[]) {
|
||||
{
|
||||
/* 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 */
|
||||
0x09, /* u8 bLength; */
|
||||
0x21, /* u8 bDescriptorType; */
|
||||
0x01, 0x10, /* u16 HID_class */
|
||||
0x00, /* u8 country_code */
|
||||
0x01, /* u8 num_descriptors */
|
||||
0x22, /* u8 type; Report */
|
||||
0x6e, 0x00, /* u16 len */
|
||||
static const USBDescDevice desc_device_wacom = {
|
||||
.bcdUSB = 0x0110,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0x80,
|
||||
.bMaxPower = 40,
|
||||
.ifs = &desc_iface_wacom,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* 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; */
|
||||
static const USBDesc desc_wacom = {
|
||||
.id = {
|
||||
.idVendor = 0x056a,
|
||||
.idProduct = 0x0000,
|
||||
.bcdDevice = 0x4210,
|
||||
.iManufacturer = STR_MANUFACTURER,
|
||||
.iProduct = STR_PRODUCT,
|
||||
.iSerialNumber = STR_SERIALNUMBER,
|
||||
},
|
||||
.full = &desc_device_wacom,
|
||||
.str = desc_strings,
|
||||
};
|
||||
|
||||
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)
|
||||
{
|
||||
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) {
|
||||
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:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
|
@ -364,7 +298,6 @@ static int usb_wacom_handle_control(USBDevice *dev, int request, int value,
|
|||
ret = 0;
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
|
@ -410,7 +343,7 @@ static void usb_wacom_handle_destroy(USBDevice *dev)
|
|||
static int usb_wacom_initfn(USBDevice *dev)
|
||||
{
|
||||
USBWacomState *s = DO_UPCAST(USBWacomState, dev, dev);
|
||||
s->dev.speed = USB_SPEED_FULL;
|
||||
usb_desc_init(dev);
|
||||
s->changed = 1;
|
||||
return 0;
|
||||
}
|
||||
|
@ -420,6 +353,7 @@ static struct USBDeviceInfo wacom_info = {
|
|||
.qdev.name = "usb-wacom-tablet",
|
||||
.qdev.desc = "QEMU PenPartner Tablet",
|
||||
.usbdevice_name = "wacom-tablet",
|
||||
.usb_desc = &desc_wacom,
|
||||
.qdev.size = sizeof(USBWacomState),
|
||||
.init = usb_wacom_initfn,
|
||||
.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)
|
||||
{
|
||||
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) {
|
||||
case USB_MSG_ATTACH:
|
||||
s->state = USB_STATE_ATTACHED;
|
||||
if (s->info->handle_attach) {
|
||||
s->info->handle_attach(s);
|
||||
}
|
||||
return 0;
|
||||
|
||||
case USB_MSG_DETACH:
|
||||
|
@ -179,7 +207,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
|||
s->remote_wakeup = 0;
|
||||
s->addr = 0;
|
||||
s->state = USB_STATE_DEFAULT;
|
||||
s->info->handle_reset(s);
|
||||
if (s->info->handle_reset) {
|
||||
s->info->handle_reset(s);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
50
hw/usb.h
50
hw/usb.h
|
@ -44,6 +44,12 @@
|
|||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
#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_ATTACHED 1
|
||||
|
@ -116,6 +122,8 @@
|
|||
#define USB_DT_STRING 0x03
|
||||
#define USB_DT_INTERFACE 0x04
|
||||
#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_ISOC 1
|
||||
|
@ -128,10 +136,27 @@ typedef struct USBDevice USBDevice;
|
|||
typedef struct USBDeviceInfo USBDeviceInfo;
|
||||
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 */
|
||||
struct USBDevice {
|
||||
DeviceState qdev;
|
||||
USBDeviceInfo *info;
|
||||
USBPort *port;
|
||||
char *port_path;
|
||||
void *opaque;
|
||||
|
||||
int speed;
|
||||
|
@ -147,6 +172,10 @@ struct USBDevice {
|
|||
int setup_state;
|
||||
int setup_len;
|
||||
int setup_index;
|
||||
|
||||
QLIST_HEAD(, USBDescString) strings;
|
||||
const USBDescDevice *device;
|
||||
const USBDescConfig *config;
|
||||
};
|
||||
|
||||
struct USBDeviceInfo {
|
||||
|
@ -167,6 +196,11 @@ struct USBDeviceInfo {
|
|||
*/
|
||||
void (*handle_destroy)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Attach the device
|
||||
*/
|
||||
void (*handle_attach)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Reset the device
|
||||
*/
|
||||
|
@ -190,20 +224,26 @@ struct USBDeviceInfo {
|
|||
int (*handle_data)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
const char *product_desc;
|
||||
const USBDesc *usb_desc;
|
||||
|
||||
/* handle legacy -usbdevice command line options */
|
||||
const char *usbdevice_name;
|
||||
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 */
|
||||
struct USBPort {
|
||||
USBDevice *dev;
|
||||
usb_attachfn attach;
|
||||
int speedmask;
|
||||
char path[16];
|
||||
USBPortOps *ops;
|
||||
void *opaque;
|
||||
USBDevice *pdev;
|
||||
int index; /* internal port index, may be used with the opaque */
|
||||
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_wakeup(USBDevice *dev);
|
||||
int usb_generic_handle_packet(USBDevice *s, USBPacket *p);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
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 *usbdevice_create(const char *cmdline);
|
||||
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);
|
||||
int usb_device_attach(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_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
|
||||
disable vm_state_notify(int running, int reason) "running %d reason %d"
|
||||
|
||||
|
|
Loading…
Reference in New Issue