mirror of https://github.com/PCSX2/pcsx2.git
USBqemu: upgrade qemu core from version 0.12.5 to 0.15. Now its up to date with the latest qemu release.
git-svn-id: http://pcsx2.googlecode.com/svn/trunk@4904 96395faa-99c1-11dd-bbfe-3dabce05a288
This commit is contained in:
parent
34edc05ab8
commit
ef50bc898d
|
@ -142,14 +142,18 @@ s32 CALLBACK USBinit() {
|
|||
}
|
||||
|
||||
qemu_ohci = ohci_create(0x1f801600,2);
|
||||
qemu_ohci->rhport[0].port.attach(&(qemu_ohci->rhport[0].port),usb_keyboard_init());
|
||||
qemu_ohci->rhport[0].port.dev = usb_keyboard_init();
|
||||
qemu_ohci->rhport[0].port.ops->attach(&(qemu_ohci->rhport[0].port));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void CALLBACK USBshutdown() {
|
||||
|
||||
qemu_ohci->rhport[0].port.dev->info->handle_destroy(qemu_ohci->rhport[0].port.dev);
|
||||
USBDevice* device = qemu_ohci->rhport[0].port.dev;
|
||||
|
||||
qemu_ohci->rhport[0].port.ops->detach(&(qemu_ohci->rhport[0].port));
|
||||
device->info->handle_destroy(qemu_ohci->rhport[0].port.dev);
|
||||
|
||||
free(qemu_ohci);
|
||||
|
||||
|
@ -237,27 +241,66 @@ void CALLBACK USBsetRAM(void *mem) {
|
|||
|
||||
// extended funcs
|
||||
|
||||
char USBfreezeID[] = "USB STv0";
|
||||
char USBfreezeID[] = "USBqemu01";
|
||||
|
||||
typedef struct {
|
||||
char freezeID[10];
|
||||
OHCIState t;
|
||||
int extraData; // for future expansion with the device state
|
||||
} USBfreezeData;
|
||||
|
||||
s32 CALLBACK USBfreeze(int mode, freezeData *data) {
|
||||
USBfreezeData *usbd;
|
||||
USBfreezeData usbd;
|
||||
|
||||
if (mode == FREEZE_LOAD) {
|
||||
usbd = (USBfreezeData*)data->data;
|
||||
if (data->size != sizeof(USBfreezeData)) return -1;
|
||||
if (mode == FREEZE_LOAD)
|
||||
{
|
||||
if(data->size < sizeof(USBfreezeData))
|
||||
{
|
||||
SysMessage("ERROR: Unable to load freeze data! Got %d bytes, expected >= %d.", data->size, sizeof(USBfreezeData));
|
||||
return -1;
|
||||
}
|
||||
|
||||
usbd = *(USBfreezeData*)data->data;
|
||||
usbd.freezeID[9] = 0;
|
||||
|
||||
if( strcmp(usbd.freezeID, USBfreezeID) != 0)
|
||||
{
|
||||
SysMessage("ERROR: Unable to load freeze data! Found ID '%s', expected ID '%s'.", usbd.freezeID, USBfreezeID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data->size != sizeof(USBfreezeData))
|
||||
return -1;
|
||||
|
||||
memcpy(qemu_ohci, usbd, sizeof(OHCIState));
|
||||
} else
|
||||
if (mode == FREEZE_SAVE) {
|
||||
for(int i=0; i< qemu_ohci->num_ports; i++)
|
||||
{
|
||||
usbd.t.rhport[i].port.opaque = qemu_ohci;
|
||||
usbd.t.rhport[i].port.ops = qemu_ohci->rhport[i].port.ops;
|
||||
usbd.t.rhport[i].port.dev = qemu_ohci->rhport[i].port.dev; // pointers
|
||||
}
|
||||
*qemu_ohci = usbd.t;
|
||||
|
||||
// WARNING: TODO: Load the state of the attached devices!
|
||||
|
||||
}
|
||||
else if (mode == FREEZE_SAVE)
|
||||
{
|
||||
data->size = sizeof(USBfreezeData);
|
||||
data->data = (s8*)malloc(data->size);
|
||||
if (data->data == NULL) return -1;
|
||||
usbd = (USBfreezeData*)data->data;
|
||||
memcpy(usbd, qemu_ohci, sizeof(OHCIState));
|
||||
if (data->data == NULL)
|
||||
return -1;
|
||||
|
||||
|
||||
strcpy(usbd.freezeID, USBfreezeID);
|
||||
usbd.t = *qemu_ohci;
|
||||
for(int i=0; i< qemu_ohci->num_ports; i++)
|
||||
{
|
||||
usbd.t.rhport[i].port.ops = NULL; // pointers
|
||||
usbd.t.rhport[i].port.opaque = NULL; // pointers
|
||||
usbd.t.rhport[i].port.dev = NULL; // pointers
|
||||
}
|
||||
|
||||
// WARNING: TODO: Save the state of the attached devices!
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -57,7 +57,8 @@ void LoadConfig();
|
|||
extern FILE *usbLog;
|
||||
void __Log(char *fmt, ...);
|
||||
|
||||
void SysMessage(char *fmt, ...);
|
||||
extern void SysMessage(const char *fmt, ...);
|
||||
extern void SysMessage(const wchar_t *fmt, ...);
|
||||
|
||||
extern HWND gsWindowHandle;
|
||||
|
||||
|
|
|
@ -64,7 +64,7 @@ void CfgSetSettingsDir( const char* dir )
|
|||
}
|
||||
|
||||
|
||||
/*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\
|
||||
/*¯| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\
|
||||
+--+---------------------+------------------------+
|
||||
| |
|
||||
| Option=Value |
|
||||
|
@ -76,7 +76,7 @@ void CfgSetSettingsDir( const char* dir )
|
|||
| All Values are limited to 255 chars. |
|
||||
| |
|
||||
+-------------------------------------------------+
|
||||
\*_____________________________________________*/
|
||||
\*______________________________________________*/
|
||||
|
||||
|
||||
void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value)
|
||||
|
@ -136,7 +136,6 @@ int CfgReadInt(const TCHAR* Section, const TCHAR* Name,int Default)
|
|||
|
||||
void CfgReadStr(const TCHAR* Section, const TCHAR* Name, TCHAR* Data, int DataSize, const TCHAR* Default)
|
||||
{
|
||||
wchar_t workspace[512];
|
||||
int chars = GetPrivateProfileString(Section,Name,L"",Data,DataSize,CfgFile);
|
||||
|
||||
if(!chars)
|
||||
|
|
|
@ -3,7 +3,6 @@
|
|||
#include <tchar.h>
|
||||
|
||||
extern void CfgSetSettingsDir( const char* dir );
|
||||
extern void CfgSetLogDir( const char* dir );
|
||||
|
||||
extern bool CfgFindName( const TCHAR *Section, const TCHAR* Name);
|
||||
|
||||
|
|
|
@ -126,7 +126,9 @@
|
|||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\qemu-usb\usb-hub.cpp" />
|
||||
<ClCompile Include="..\qemu-usb\usb-hub.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\qemu-usb\usb-kbd.cpp" />
|
||||
<ClCompile Include="..\usb-mic\usb-mic-dummy.cpp">
|
||||
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>
|
||||
|
|
|
@ -28,7 +28,6 @@ typedef uint32_t target_phys_addr_t;
|
|||
typedef struct {
|
||||
//USBBus bus;
|
||||
//qemu_irq irq;
|
||||
enum ohci_type type;
|
||||
int mem;
|
||||
int num_ports;
|
||||
const char *name;
|
||||
|
@ -91,19 +90,8 @@ struct ohci_hcca {
|
|||
uint32_t done;
|
||||
};
|
||||
|
||||
|
||||
extern int64_t usb_frame_time;
|
||||
extern int64_t usb_bit_time;
|
||||
|
||||
enum ohci_type {
|
||||
OHCI_TYPE_PCI,
|
||||
OHCI_TYPE_PXA,
|
||||
OHCI_TYPE_SM501,
|
||||
};
|
||||
|
||||
int64_t get_ticks_per_sec();
|
||||
|
||||
static void ohci_bus_stop(OHCIState *ohci);
|
||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
|
||||
|
||||
/* Bitfields for the first word of an Endpoint Desciptor. */
|
||||
#define OHCI_ED_FA_SHIFT 0
|
||||
|
@ -282,6 +270,7 @@ struct ohci_iso_td {
|
|||
|
||||
#define OHCI_HRESET_FSBIR (1 << 0)
|
||||
|
||||
int64_t get_ticks_per_sec();
|
||||
|
||||
int64_t get_clock();
|
||||
|
||||
|
|
|
@ -26,9 +26,35 @@
|
|||
#include "USBinternal.h"
|
||||
//#include "usb.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
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;
|
||||
assert(dev);
|
||||
port->ops->detach(port);
|
||||
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->port);
|
||||
}
|
||||
}
|
||||
|
||||
/**********************/
|
||||
|
@ -38,9 +64,10 @@ void usb_attach(USBPort *port, USBDevice *dev)
|
|||
protocol)
|
||||
*/
|
||||
|
||||
#define SETUP_STATE_IDLE 0
|
||||
#define SETUP_STATE_DATA 1
|
||||
#define SETUP_STATE_ACK 2
|
||||
#define SETUP_STATE_IDLE 0
|
||||
#define SETUP_STATE_SETUP 1
|
||||
#define SETUP_STATE_DATA 2
|
||||
#define SETUP_STATE_ACK 3
|
||||
|
||||
static int do_token_setup(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
|
@ -57,10 +84,14 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
|
|||
request = (s->setup_buf[0] << 8) | s->setup_buf[1];
|
||||
value = (s->setup_buf[3] << 8) | s->setup_buf[2];
|
||||
index = (s->setup_buf[5] << 8) | s->setup_buf[4];
|
||||
|
||||
|
||||
if (s->setup_buf[0] & USB_DIR_IN) {
|
||||
ret = s->info->handle_control(s, request, value, index,
|
||||
ret = s->info->handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
s->setup_state = SETUP_STATE_SETUP;
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
|
@ -68,6 +99,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
|
|||
s->setup_len = ret;
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
} else {
|
||||
if (s->setup_len > sizeof(s->data_buf)) {
|
||||
fprintf(stderr,
|
||||
"usb_generic_handle_packet: ctrl buffer too small (%d > %zu)\n",
|
||||
s->setup_len, sizeof(s->data_buf));
|
||||
return USB_RET_STALL;
|
||||
}
|
||||
if (s->setup_len == 0)
|
||||
s->setup_state = SETUP_STATE_ACK;
|
||||
else
|
||||
|
@ -92,9 +129,12 @@ static int do_token_in(USBDevice *s, USBPacket *p)
|
|||
switch(s->setup_state) {
|
||||
case SETUP_STATE_ACK:
|
||||
if (!(s->setup_buf[0] & USB_DIR_IN)) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
ret = s->info->handle_control(s, request, value, index,
|
||||
ret = s->info->handle_control(s, p, request, value, index,
|
||||
s->setup_len, s->data_buf);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
return USB_RET_ASYNC;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
if (ret > 0)
|
||||
return 0;
|
||||
return ret;
|
||||
|
@ -169,6 +209,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 +222,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;
|
||||
}
|
||||
|
||||
|
@ -202,6 +247,36 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
|
|||
}
|
||||
}
|
||||
|
||||
/* ctrl complete function for devices which use usb_generic_handle_packet and
|
||||
may return USB_RET_ASYNC from their handle_control callback. Device code
|
||||
which does this *must* call this function instead of the normal
|
||||
usb_packet_complete to complete their async control packets. */
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p)
|
||||
{
|
||||
if (p->len < 0) {
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
}
|
||||
|
||||
switch (s->setup_state) {
|
||||
case SETUP_STATE_SETUP:
|
||||
if (p->len < s->setup_len) {
|
||||
s->setup_len = p->len;
|
||||
}
|
||||
s->setup_state = SETUP_STATE_DATA;
|
||||
p->len = 8;
|
||||
break;
|
||||
|
||||
case SETUP_STATE_ACK:
|
||||
s->setup_state = SETUP_STATE_IDLE;
|
||||
p->len = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
usb_packet_complete(s, p);
|
||||
}
|
||||
|
||||
/* XXX: fix overflow */
|
||||
int set_usb_string(uint8_t *buf, const char *str)
|
||||
{
|
||||
|
@ -223,9 +298,54 @@ int set_usb_string(uint8_t *buf, const char *str)
|
|||
void usb_send_msg(USBDevice *dev, int msg)
|
||||
{
|
||||
USBPacket p;
|
||||
int ret;
|
||||
|
||||
memset(&p, 0, sizeof(p));
|
||||
p.pid = msg;
|
||||
dev->info->handle_packet(dev, &p);
|
||||
|
||||
ret = usb_handle_packet(dev, &p);
|
||||
/* This _must_ be synchronous */
|
||||
assert(ret != USB_RET_ASYNC);
|
||||
}
|
||||
|
||||
/* Hand over a packet to a device for processing. Return value
|
||||
USB_RET_ASYNC indicates the processing isn't finished yet, the
|
||||
driver will call usb_packet_complete() when done processing it. */
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
int ret;
|
||||
|
||||
assert(p->owner == NULL);
|
||||
ret = dev->info->handle_packet(dev, p);
|
||||
if (ret == USB_RET_ASYNC) {
|
||||
if (p->owner == NULL) {
|
||||
p->owner = dev;
|
||||
} else {
|
||||
/* We'll end up here when usb_handle_packet is called
|
||||
* recursively due to a hub being in the chain. Nothing
|
||||
* to do. Leave p->owner pointing to the device, not the
|
||||
* hub. */;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred by returning USB_RET_ASYNC from
|
||||
handle_packet. */
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
/* Note: p->owner != dev is possible in case dev is a hub */
|
||||
assert(p->owner != NULL);
|
||||
dev->port->ops->complete(dev->port, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred by
|
||||
returning USB_RET_ASYNC from handle_packet, and not yet
|
||||
completed. */
|
||||
void usb_cancel_packet(USBPacket * p)
|
||||
{
|
||||
assert(p->owner != NULL);
|
||||
p->owner->info->cancel_packet(p->owner, p);
|
||||
p->owner = NULL;
|
||||
}
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -25,7 +25,7 @@
|
|||
|
||||
//#define DEBUG
|
||||
|
||||
#define MAX_PORTS 8
|
||||
#define NUM_PORTS 8
|
||||
|
||||
typedef struct USBHubPort {
|
||||
USBPort port;
|
||||
|
@ -35,8 +35,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)
|
||||
|
@ -82,72 +81,59 @@ typedef struct USBHubState {
|
|||
|
||||
/* same as Linux kernel root hubs */
|
||||
|
||||
static const uint8_t qemu_hub_dev_descriptor[] = {
|
||||
0x12, /* u8 bLength; */
|
||||
0x01, /* u8 bDescriptorType; Device */
|
||||
0x10, 0x01, /* u16 bcdUSB; v1.1 */
|
||||
|
||||
0x09, /* u8 bDeviceClass; HUB_CLASSCODE */
|
||||
0x00, /* u8 bDeviceSubClass; */
|
||||
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
|
||||
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
|
||||
|
||||
0x00, 0x00, /* u16 idVendor; */
|
||||
0x00, 0x00, /* u16 idProduct; */
|
||||
0x01, 0x01, /* u16 bcdDevice */
|
||||
|
||||
0x03, /* u8 iManufacturer; */
|
||||
0x02, /* u8 iProduct; */
|
||||
0x01, /* u8 iSerialNumber; */
|
||||
0x01 /* u8 bNumConfigurations; */
|
||||
enum {
|
||||
STR_MANUFACTURER = 1,
|
||||
STR_PRODUCT,
|
||||
STR_SERIALNUMBER,
|
||||
};
|
||||
|
||||
/* XXX: patch interrupt size */
|
||||
static const uint8_t qemu_hub_config_descriptor[] = {
|
||||
static const USBDescStrings desc_strings = {
|
||||
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
|
||||
[STR_PRODUCT] = "QEMU USB Hub",
|
||||
[STR_SERIALNUMBER] = "314159",
|
||||
};
|
||||
|
||||
/* one configuration */
|
||||
0x09, /* u8 bLength; */
|
||||
0x02, /* u8 bDescriptorType; Configuration */
|
||||
0x19, 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_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,
|
||||
},
|
||||
}
|
||||
};
|
||||
|
||||
/* 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 USBDescDevice desc_device_hub = {
|
||||
.bcdUSB = 0x0110,
|
||||
.bDeviceClass = USB_CLASS_HUB,
|
||||
.bMaxPacketSize0 = 8,
|
||||
.bNumConfigurations = 1,
|
||||
.confs = (USBDescConfig[]) {
|
||||
{
|
||||
.bNumInterfaces = 1,
|
||||
.bConfigurationValue = 1,
|
||||
.bmAttributes = 0xe0,
|
||||
.nif = 1,
|
||||
.ifs = &desc_iface_hub,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/* one interface */
|
||||
0x09, /* u8 if_bLength; */
|
||||
0x04, /* u8 if_bDescriptorType; Interface */
|
||||
0x00, /* u8 if_bInterfaceNumber; */
|
||||
0x00, /* u8 if_bAlternateSetting; */
|
||||
0x01, /* u8 if_bNumEndpoints; */
|
||||
0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */
|
||||
0x00, /* u8 if_bInterfaceSubClass; */
|
||||
0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */
|
||||
0x00, /* u8 if_iInterface; */
|
||||
|
||||
/* 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 */
|
||||
0x02, 0x00, /* u16 ep_wMaxPacketSize; 1 + (MAX_ROOT_PORTS / 8) */
|
||||
0xff /* u8 ep_bInterval; (255ms -- usb 2.0 spec) */
|
||||
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_hub_descriptor[] =
|
||||
|
@ -163,37 +149,75 @@ 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 = (USBHubState *)port1->opaque;
|
||||
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];
|
||||
|
||||
/* Let upstream know the device on this port is gone */
|
||||
s->dev.port->ops->child_detach(s->dev.port, port1->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;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_hub_child_detach(USBPort *port1, USBDevice *child)
|
||||
{
|
||||
USBHubState *s = port1->opaque;
|
||||
|
||||
/* Pass along upstream */
|
||||
s->dev.port->ops->child_detach(s->dev.port, child);
|
||||
}
|
||||
|
||||
static void usb_hub_wakeup(USBPort *port1)
|
||||
{
|
||||
USBHubState *s = port1->opaque;
|
||||
USBHubPort *port = &s->ports[port1->index];
|
||||
|
||||
if (port->wPortStatus & PORT_STAT_SUSPEND) {
|
||||
port->wPortChange |= PORT_STAT_C_SUSPEND;
|
||||
usb_wakeup(&s->dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_hub_complete(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
USBHubState *s = port->opaque;
|
||||
|
||||
/*
|
||||
* Just pass it along upstream for now.
|
||||
*
|
||||
* If we ever inplement usb 2.0 split transactions this will
|
||||
* become a little more complicated ...
|
||||
*/
|
||||
usb_packet_complete(&s->dev, packet);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -202,99 +226,24 @@ static void usb_hub_handle_reset(USBDevice *dev)
|
|||
/* XXX: do it */
|
||||
}
|
||||
|
||||
static int usb_hub_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
|
||||
int request, int value, int index, int length, uint8_t *data)
|
||||
{
|
||||
USBHubState *s = (USBHubState *)dev;
|
||||
int ret;
|
||||
|
||||
ret = usb_desc_handle_control(dev, p, 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 v0.12.5");
|
||||
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;
|
||||
|
@ -314,8 +263,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;
|
||||
|
@ -337,8 +287,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) {
|
||||
|
@ -365,11 +316,11 @@ 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) {
|
||||
case PORT_ENABLE:
|
||||
port->wPortStatus &= ~PORT_STAT_ENABLE;
|
||||
|
@ -403,17 +354,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 +393,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,11 +432,11 @@ 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)) {
|
||||
ret = dev->info->handle_packet(dev, p);
|
||||
ret = usb_handle_packet(dev, p);
|
||||
if (ret != USB_RET_NODEV) {
|
||||
return ret;
|
||||
}
|
||||
|
@ -518,44 +469,79 @@ static void usb_hub_handle_destroy(USBDevice *dev)
|
|||
USBHubState *s = (USBHubState *)dev;
|
||||
int i;
|
||||
|
||||
//for (i = 0; i < s->nb_ports; i++) {
|
||||
// usb_unregister_port(usb_bus_from_device(dev),
|
||||
// &s->ports[i].port);
|
||||
//}
|
||||
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,
|
||||
.child_detach = usb_hub_child_detach,
|
||||
.wakeup = usb_hub_wakeup,
|
||||
.complete = usb_hub_complete,
|
||||
};
|
||||
|
||||
static int usb_hub_initfn(USBDevice *dev)
|
||||
{
|
||||
USBHubState *s = (USBHubState*)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, usb_hub_attach);
|
||||
usb_register_port(usb_bus_from_device(dev),
|
||||
&port->port, s, i, &usb_hub_port_ops,
|
||||
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
|
||||
port->wPortStatus = PORT_STAT_POWER;
|
||||
port->wPortChange = 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const VMStateDescription vmstate_usb_hub_port = {
|
||||
.name = "usb-hub-port",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_UINT16(wPortStatus, USBHubPort),
|
||||
VMSTATE_UINT16(wPortChange, USBHubPort),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static const VMStateDescription vmstate_usb_hub = {
|
||||
.name = "usb-hub",
|
||||
.version_id = 1,
|
||||
.minimum_version_id = 1,
|
||||
.fields = (VMStateField []) {
|
||||
VMSTATE_USB_DEVICE(dev, USBHubState),
|
||||
VMSTATE_STRUCT_ARRAY(ports, USBHubState, NUM_PORTS, 0,
|
||||
vmstate_usb_hub_port, USBHubPort),
|
||||
VMSTATE_END_OF_LIST()
|
||||
}
|
||||
};
|
||||
|
||||
static struct USBDeviceInfo hub_info = {
|
||||
usb_hub_initfn,
|
||||
usb_hub_handle_packet,
|
||||
usb_hub_handle_destroy,
|
||||
usb_hub_handle_reset,
|
||||
usb_hub_handle_control,
|
||||
usb_hub_handle_data,
|
||||
|
||||
"QEMU USB Hub",
|
||||
|
||||
.product_desc = "QEMU USB Hub",
|
||||
.qdev.name = "usb-hub",
|
||||
.qdev.fw_name = "hub",
|
||||
.qdev.size = sizeof(USBHubState),
|
||||
.qdev.vmsd = &vmstate_usb_hub,
|
||||
.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,
|
||||
.handle_destroy = usb_hub_handle_destroy,
|
||||
};
|
||||
|
||||
static void usb_hub_register_devices(void)
|
||||
{
|
||||
//usb_qdev_register(&hub_info);
|
||||
usb_qdev_register(&hub_info);
|
||||
}
|
||||
//device_init(usb_hub_register_devices)
|
||||
device_init(usb_hub_register_devices)
|
||||
|
|
|
@ -697,8 +697,8 @@ static void usb_keyboard_handle_reset(USBDevice *dev)
|
|||
USBKeyboardState *s = (USBKeyboardState *)dev;
|
||||
}
|
||||
|
||||
static int usb_keyboard_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_keyboard_handle_control(USBDevice *dev, USBPacket *p, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
{
|
||||
USBKeyboardState *s = (USBKeyboardState *)dev;
|
||||
int ret = 0;
|
||||
|
|
|
@ -4,17 +4,19 @@
|
|||
* Copyright (c) 2006 CodeSourcery.
|
||||
* Written by Paul Brook
|
||||
*
|
||||
* This code is licenced under the LGPL.
|
||||
* This code is licensed under the LGPL.
|
||||
*/
|
||||
|
||||
#include "qemu-common.h"
|
||||
#include "qemu-option.h"
|
||||
#include "qemu-config.h"
|
||||
#include "usb.h"
|
||||
#include "block.h"
|
||||
#include "usb-desc.h"
|
||||
#include "scsi.h"
|
||||
#include "console.h"
|
||||
#include "monitor.h"
|
||||
#include "sysemu.h"
|
||||
#include "blockdev.h"
|
||||
|
||||
//#define DEBUG_MSD
|
||||
|
||||
|
@ -31,7 +33,7 @@ do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
|
|||
|
||||
enum USBMSDMode {
|
||||
USB_MSDM_CBW, /* Command Block. */
|
||||
USB_MSDM_DATAOUT, /* Tranfer data to device. */
|
||||
USB_MSDM_DATAOUT, /* Transfer data to device. */
|
||||
USB_MSDM_DATAIN, /* Transfer data from device. */
|
||||
USB_MSDM_CSW /* Command Status. */
|
||||
};
|
||||
|
@ -46,9 +48,12 @@ typedef struct {
|
|||
uint32_t data_len;
|
||||
uint32_t residue;
|
||||
uint32_t tag;
|
||||
SCSIRequest *req;
|
||||
SCSIBus bus;
|
||||
DriveInfo *dinfo;
|
||||
BlockConf conf;
|
||||
char *serial;
|
||||
SCSIDevice *scsi_dev;
|
||||
uint32_t removable;
|
||||
int result;
|
||||
/* For async completion. */
|
||||
USBPacket *packet;
|
||||
|
@ -71,69 +76,104 @@ 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,
|
||||
.nif = 1,
|
||||
.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,
|
||||
.nif = 1,
|
||||
.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)
|
||||
|
@ -152,77 +192,93 @@ 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->mode == USB_MSDM_DATAIN) {
|
||||
s->scsi_dev->info->read_data(s->scsi_dev, s->tag);
|
||||
} else if (s->mode == USB_MSDM_DATAOUT) {
|
||||
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
|
||||
}
|
||||
if (s->scsi_len == 0 || s->data_len == 0) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
uint32_t arg)
|
||||
static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, bus->qbus.parent);
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
if (tag != s->tag) {
|
||||
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag);
|
||||
}
|
||||
if (reason == SCSI_REASON_DONE) {
|
||||
DPRINTF("Command complete %d\n", arg);
|
||||
s->residue = s->data_len;
|
||||
s->result = arg != 0;
|
||||
if (s->packet) {
|
||||
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);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->mode == USB_MSDM_DATAIN)
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
s->usb_len = 0;
|
||||
}
|
||||
if (s->data_len == 0)
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
return;
|
||||
}
|
||||
s->scsi_len = arg;
|
||||
s->scsi_buf = s->scsi_dev->info->get_buf(s->scsi_dev, tag);
|
||||
assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
|
||||
s->scsi_len = len;
|
||||
s->scsi_buf = scsi_req_get_buf(req);
|
||||
if (p) {
|
||||
usb_msd_copy_data(s);
|
||||
if (s->usb_len == 0) {
|
||||
if (s->packet && s->usb_len == 0) {
|
||||
/* Set s->packet to NULL before calling usb_packet_complete
|
||||
because annother request may be issued before
|
||||
because another request may be issued before
|
||||
usb_packet_complete returns. */
|
||||
DPRINTF("Packet complete %p\n", p);
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(p);
|
||||
usb_packet_complete(&s->dev, p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_msd_command_complete(SCSIRequest *req, uint32_t status)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
USBPacket *p = s->packet;
|
||||
|
||||
DPRINTF("Command complete %d\n", status);
|
||||
s->residue = s->data_len;
|
||||
s->result = status != 0;
|
||||
if (s->packet) {
|
||||
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, p);
|
||||
s->mode = USB_MSDM_CBW;
|
||||
} else {
|
||||
if (s->data_len) {
|
||||
s->data_len -= s->usb_len;
|
||||
if (s->mode == USB_MSDM_DATAIN) {
|
||||
memset(s->usb_buf, 0, s->usb_len);
|
||||
}
|
||||
s->usb_len = 0;
|
||||
}
|
||||
if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
}
|
||||
s->packet = NULL;
|
||||
usb_packet_complete(&s->dev, p);
|
||||
} else if (s->data_len == 0) {
|
||||
s->mode = USB_MSDM_CSW;
|
||||
}
|
||||
scsi_req_unref(req);
|
||||
s->req = NULL;
|
||||
}
|
||||
|
||||
static void usb_msd_request_cancelled(SCSIRequest *req)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev.qdev, req->bus->qbus.parent);
|
||||
|
||||
if (req == s->req) {
|
||||
scsi_req_unref(s->req);
|
||||
s->req = NULL;
|
||||
s->packet = NULL;
|
||||
s->scsi_len = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void usb_msd_handle_reset(USBDevice *dev)
|
||||
{
|
||||
MSDState *s = (MSDState *)dev;
|
||||
|
@ -231,88 +287,19 @@ static void usb_msd_handle_reset(USBDevice *dev)
|
|||
s->mode = USB_MSDM_CBW;
|
||||
}
|
||||
|
||||
static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data)
|
||||
static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
|
||||
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, p, 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;
|
||||
|
@ -321,35 +308,32 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
|
|||
ret = 0;
|
||||
break;
|
||||
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
|
||||
if (value == 0 && index != 0x81) { /* clear ep halt */
|
||||
goto fail;
|
||||
}
|
||||
ret = 0;
|
||||
break;
|
||||
case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
|
||||
ret = 0;
|
||||
break;
|
||||
/* Class specific requests. */
|
||||
case MassStorageReset:
|
||||
case ClassInterfaceOutRequest | MassStorageReset:
|
||||
/* Reset state ready for the next CBW. */
|
||||
s->mode = USB_MSDM_CBW;
|
||||
ret = 0;
|
||||
break;
|
||||
case GetMaxLun:
|
||||
case ClassInterfaceRequest | GetMaxLun:
|
||||
data[0] = 0;
|
||||
ret = 1;
|
||||
break;
|
||||
default:
|
||||
fail:
|
||||
ret = USB_RET_STALL;
|
||||
break;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void usb_msd_cancel_io(USBPacket *p, void *opaque)
|
||||
static void usb_msd_cancel_io(USBDevice *dev, USBPacket *p)
|
||||
{
|
||||
MSDState *s = opaque;
|
||||
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag);
|
||||
s->packet = NULL;
|
||||
s->scsi_len = 0;
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
scsi_req_cancel(s->req);
|
||||
}
|
||||
|
||||
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
||||
|
@ -395,15 +379,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
|
||||
s->tag, cbw.flags, cbw.cmd_len, s->data_len);
|
||||
s->residue = 0;
|
||||
s->scsi_dev->info->send_command(s->scsi_dev, s->tag, cbw.cmd, 0);
|
||||
s->scsi_len = 0;
|
||||
s->req = scsi_req_new(s->scsi_dev, s->tag, 0, NULL);
|
||||
scsi_req_enqueue(s->req, cbw.cmd);
|
||||
/* ??? Should check that USB and SCSI data transfer
|
||||
directions match. */
|
||||
if (s->residue == 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) {
|
||||
s->scsi_dev->info->write_data(s->scsi_dev, s->tag);
|
||||
}
|
||||
if (s->mode != USB_MSDM_CSW && s->residue == 0) {
|
||||
scsi_req_continue(s->req);
|
||||
}
|
||||
ret = len;
|
||||
break;
|
||||
|
@ -426,7 +408,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
}
|
||||
if (s->usb_len) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
|
@ -449,7 +430,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
if (s->data_len != 0 || len < 13)
|
||||
goto fail;
|
||||
/* Waiting for SCSI write to complete. */
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
break;
|
||||
|
@ -460,15 +440,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;
|
||||
|
@ -485,7 +463,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
|
|||
}
|
||||
if (s->usb_len) {
|
||||
DPRINTF("Deferring packet %p\n", p);
|
||||
usb_defer_packet(p, usb_msd_cancel_io, s);
|
||||
s->packet = p;
|
||||
ret = USB_RET_ASYNC;
|
||||
} else {
|
||||
|
@ -514,36 +491,71 @@ static void usb_msd_password_cb(void *opaque, int err)
|
|||
MSDState *s = opaque;
|
||||
|
||||
if (!err)
|
||||
usb_device_attach(&s->dev);
|
||||
else
|
||||
err = usb_device_attach(&s->dev);
|
||||
|
||||
if (err)
|
||||
qdev_unplug(&s->dev.qdev);
|
||||
}
|
||||
|
||||
static const struct SCSIBusOps usb_msd_scsi_ops = {
|
||||
.transfer_data = usb_msd_transfer_data,
|
||||
.complete = usb_msd_command_complete,
|
||||
.cancel = usb_msd_request_cancelled
|
||||
};
|
||||
|
||||
static int usb_msd_initfn(USBDevice *dev)
|
||||
{
|
||||
MSDState *s = DO_UPCAST(MSDState, dev, dev);
|
||||
BlockDriverState *bs = s->conf.bs;
|
||||
DriveInfo *dinfo;
|
||||
|
||||
if (!s->dinfo || !s->dinfo->bdrv) {
|
||||
qemu_error("usb-msd: drive property not set\n");
|
||||
if (!bs) {
|
||||
error_report("usb-msd: drive property not set");
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->dev.speed = USB_SPEED_FULL;
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete);
|
||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, s->dinfo, 0);
|
||||
/*
|
||||
* Hack alert: this pretends to be a block device, but it's really
|
||||
* a SCSI bus that can serve only a single device, which it
|
||||
* creates automatically. But first it needs to detach from its
|
||||
* blockdev, or else scsi_bus_legacy_add_drive() dies when it
|
||||
* attaches again.
|
||||
*
|
||||
* The hack is probably a bad idea.
|
||||
*/
|
||||
bdrv_detach(bs, &s->dev.qdev);
|
||||
s->conf.bs = NULL;
|
||||
|
||||
if (!s->serial) {
|
||||
/* try to fall back to value set with legacy -drive serial=... */
|
||||
dinfo = drive_get_by_blockdev(bs);
|
||||
if (*dinfo->serial) {
|
||||
s->serial = strdup(dinfo->serial);
|
||||
}
|
||||
}
|
||||
if (s->serial) {
|
||||
usb_desc_set_string(dev, STR_SERIALNUMBER, s->serial);
|
||||
}
|
||||
|
||||
usb_desc_init(dev);
|
||||
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, &usb_msd_scsi_ops);
|
||||
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, bs, 0, !!s->removable);
|
||||
if (!s->scsi_dev) {
|
||||
return -1;
|
||||
}
|
||||
s->bus.qbus.allow_hotplug = 0;
|
||||
usb_msd_handle_reset(dev);
|
||||
|
||||
if (bdrv_key_required(s->dinfo->bdrv)) {
|
||||
if (s->dev.qdev.hotplugged) {
|
||||
monitor_read_bdrv_key_start(cur_mon, s->dinfo->bdrv,
|
||||
usb_msd_password_cb, s);
|
||||
if (bdrv_key_required(bs)) {
|
||||
if (cur_mon) {
|
||||
monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
|
||||
s->dev.auto_attach = 0;
|
||||
} else {
|
||||
autostart = 0;
|
||||
}
|
||||
}
|
||||
|
||||
add_boot_device_path(s->conf.bootindex, &dev->qdev, "/disk@0,0");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -554,13 +566,12 @@ static USBDevice *usb_msd_init(const char *filename)
|
|||
QemuOpts *opts;
|
||||
DriveInfo *dinfo;
|
||||
USBDevice *dev;
|
||||
int fatal_error;
|
||||
const char *p1;
|
||||
char fmt[32];
|
||||
|
||||
/* parse -usbdevice disk: syntax into drive opts */
|
||||
snprintf(id, sizeof(id), "usb%d", nr++);
|
||||
opts = qemu_opts_create(&qemu_drive_opts, id, 0);
|
||||
opts = qemu_opts_create(qemu_find_opts("drive"), id, 0);
|
||||
|
||||
p1 = strchr(filename, ':');
|
||||
if (p1++) {
|
||||
|
@ -584,7 +595,7 @@ static USBDevice *usb_msd_init(const char *filename)
|
|||
qemu_opt_set(opts, "if", "none");
|
||||
|
||||
/* create host drive */
|
||||
dinfo = drive_init(opts, NULL, &fatal_error);
|
||||
dinfo = drive_init(opts, 0);
|
||||
if (!dinfo) {
|
||||
qemu_opts_del(opts);
|
||||
return NULL;
|
||||
|
@ -595,7 +606,10 @@ static USBDevice *usb_msd_init(const char *filename)
|
|||
if (!dev) {
|
||||
return NULL;
|
||||
}
|
||||
qdev_prop_set_drive(&dev->qdev, "drive", dinfo);
|
||||
if (qdev_prop_set_drive(&dev->qdev, "drive", dinfo->bdrv) < 0) {
|
||||
qdev_free(&dev->qdev);
|
||||
return NULL;
|
||||
}
|
||||
if (qdev_init(&dev->qdev) < 0)
|
||||
return NULL;
|
||||
|
||||
|
@ -605,16 +619,22 @@ static USBDevice *usb_msd_init(const char *filename)
|
|||
static struct USBDeviceInfo msd_info = {
|
||||
.product_desc = "QEMU USB MSD",
|
||||
.qdev.name = "usb-storage",
|
||||
.qdev.fw_name = "storage",
|
||||
.qdev.size = sizeof(MSDState),
|
||||
.usb_desc = &desc,
|
||||
.init = usb_msd_initfn,
|
||||
.handle_packet = usb_generic_handle_packet,
|
||||
.cancel_packet = usb_msd_cancel_io,
|
||||
.handle_attach = usb_desc_attach,
|
||||
.handle_reset = usb_msd_handle_reset,
|
||||
.handle_control = usb_msd_handle_control,
|
||||
.handle_data = usb_msd_handle_data,
|
||||
.usbdevice_name = "disk",
|
||||
.usbdevice_init = usb_msd_init,
|
||||
.qdev.props = (Property[]) {
|
||||
DEFINE_PROP_DRIVE("drive", MSDState, dinfo),
|
||||
DEFINE_BLOCK_PROPERTIES(MSDState, conf),
|
||||
DEFINE_PROP_STRING("serial", MSDState, serial),
|
||||
DEFINE_PROP_BIT("removable", MSDState, removable, 0, false),
|
||||
DEFINE_PROP_END_OF_LIST(),
|
||||
},
|
||||
};
|
|
@ -58,38 +58,23 @@ int dprintf(const char *fmt,...)
|
|||
#endif
|
||||
}
|
||||
|
||||
static int last_level = 0;
|
||||
|
||||
/* Update IRQ levels */
|
||||
static inline void ohci_intr_update(OHCIState *ohci)
|
||||
{
|
||||
uint32_t bits = (ohci->intr_status & ohci->intr) & 0x7fffffff;
|
||||
int level = 0;
|
||||
|
||||
if ((ohci->intr & OHCI_INTR_MIE) && (bits!=0)) // && (ohci->ctl & OHCI_CTL_HCFS))
|
||||
if ((ohci->intr & OHCI_INTR_MIE) &&
|
||||
(ohci->intr_status & ohci->intr))
|
||||
level = 1;
|
||||
|
||||
if(level && !last_level)
|
||||
{
|
||||
/*
|
||||
static char reasons[1024];
|
||||
int first=1;
|
||||
|
||||
reasons[0]=0;
|
||||
|
||||
#define reason_add(p,t) if(bits&(p)) { if(!first) strcat_s(reasons,1024,", "); first=0; strcat_s(reasons,1024,t); }
|
||||
reason_add(OHCI_INTR_SO,"Scheduling overrun");
|
||||
reason_add(OHCI_INTR_WD,"HcDoneHead writeback");
|
||||
reason_add(OHCI_INTR_SF,"Start of frame");
|
||||
reason_add(OHCI_INTR_RD,"Resume detect");
|
||||
reason_add(OHCI_INTR_UE,"Unrecoverable error");
|
||||
reason_add(OHCI_INTR_FNO,"Frame number overflow");
|
||||
reason_add(OHCI_INTR_RHSC,"Root hub status change");
|
||||
reason_add(OHCI_INTR_OC,"Ownership change");
|
||||
*/
|
||||
if((ohci->ctl & OHCI_CTL_HCFS)==OHCI_USB_OPERATIONAL)
|
||||
if( (get_clock() - last_cycle) > MIN_IRQ_INTERVAL)
|
||||
{
|
||||
if( (get_clock() - last_cycle) > MIN_IRQ_INTERVAL)
|
||||
{
|
||||
USBirq(1);
|
||||
last_cycle = get_clock();
|
||||
}
|
||||
//dprintf("usb-ohci: Interrupt Called. Reason(s): %s\n",reasons);
|
||||
USBirq(1);
|
||||
last_cycle = get_clock();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,55 +87,97 @@ 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 = (OHCIState *)port1->opaque;
|
||||
OHCIState *s = (OHCIState*)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;
|
||||
/* 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);
|
||||
/* update speed */
|
||||
if (port->port.dev->speed == USB_SPEED_LOW) {
|
||||
port->ctrl |= OHCI_PORT_LSDA;
|
||||
} 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);
|
||||
port->ctrl &= ~OHCI_PORT_LSDA;
|
||||
}
|
||||
|
||||
if (old_state != port->ctrl)
|
||||
/* 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);
|
||||
|
||||
if (old_state != port->ctrl) {
|
||||
ohci_set_interrupt(s, OHCI_INTR_RHSC);
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev)
|
||||
{
|
||||
if (ohci->async_td && ohci->usb_packet.owner == dev) {
|
||||
usb_cancel_packet(&ohci->usb_packet);
|
||||
ohci->async_td = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_detach(USBPort *port1)
|
||||
{
|
||||
OHCIState *s = (OHCIState*)port1->opaque;
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t old_state = port->ctrl;
|
||||
|
||||
ohci_async_cancel_device(s, port1->dev);
|
||||
|
||||
/* 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);
|
||||
}
|
||||
}
|
||||
|
||||
static void ohci_wakeup(USBPort *port1)
|
||||
{
|
||||
OHCIState *s = (OHCIState*)port1->opaque;
|
||||
OHCIPort *port = &s->rhport[port1->index];
|
||||
uint32_t intr = 0;
|
||||
if (port->ctrl & OHCI_PORT_PSS) {
|
||||
dprintf("usb-ohci: port %d: wakeup\n", port1->index);
|
||||
port->ctrl |= OHCI_PORT_PSSC;
|
||||
port->ctrl &= ~OHCI_PORT_PSS;
|
||||
intr = OHCI_INTR_RHSC;
|
||||
}
|
||||
/* Note that the controller can be suspended even if this port is not */
|
||||
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND) {
|
||||
dprintf("usb-ohci: remote-wakeup: SUSPEND->RESUME\n");
|
||||
/* This is the one state transition the controller can do by itself */
|
||||
s->ctl &= ~OHCI_CTL_HCFS;
|
||||
s->ctl |= OHCI_USB_RESUME;
|
||||
/* In suspend mode only ResumeDetected is possible, not RHSC:
|
||||
* see the OHCI spec 5.1.2.3.
|
||||
*/
|
||||
intr = OHCI_INTR_RD;
|
||||
}
|
||||
ohci_set_interrupt(s, intr);
|
||||
}
|
||||
|
||||
static void ohci_child_detach(USBPort *port1, USBDevice *child)
|
||||
{
|
||||
OHCIState *s = (OHCIState*)port1->opaque;
|
||||
|
||||
ohci_async_cancel_device(s, child);
|
||||
}
|
||||
|
||||
/* Reset the controller */
|
||||
|
@ -193,8 +220,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);
|
||||
|
@ -212,7 +240,7 @@ static inline int get_dwords(OHCIState *ohci,
|
|||
addr += ohci->localmem_base;
|
||||
|
||||
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
|
||||
cpu_physical_memory_read(addr, (uint8_t*)buf, sizeof(*buf));
|
||||
*buf = (*buf);
|
||||
}
|
||||
|
||||
|
@ -229,7 +257,7 @@ static inline int put_dwords(OHCIState *ohci,
|
|||
|
||||
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
uint32_t tmp = (*buf);
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
|
||||
cpu_physical_memory_write(addr, (uint8_t*)&tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -244,7 +272,7 @@ static inline int get_words(OHCIState *ohci,
|
|||
addr += ohci->localmem_base;
|
||||
|
||||
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, sizeof(*buf), 0);
|
||||
cpu_physical_memory_read(addr, (uint8_t*)buf, sizeof(*buf));
|
||||
*buf = (*buf);
|
||||
}
|
||||
|
||||
|
@ -261,7 +289,7 @@ static inline int put_words(OHCIState *ohci,
|
|||
|
||||
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
|
||||
uint16_t tmp = (*buf);
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)&tmp, sizeof(tmp), 1);
|
||||
cpu_physical_memory_write(addr, (uint8_t*)&tmp, sizeof(tmp));
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
@ -289,8 +317,7 @@ static inline int ohci_read_iso_td(OHCIState *ohci,
|
|||
static inline int ohci_read_hcca(OHCIState *ohci,
|
||||
uint32_t addr, struct ohci_hcca *hcca)
|
||||
{
|
||||
cpu_physical_memory_rw(addr + ohci->localmem_base,
|
||||
(uint8_t *)hcca, sizeof(*hcca), 0);
|
||||
cpu_physical_memory_read(addr + ohci->localmem_base, (uint8_t*)hcca, sizeof(*hcca));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -316,8 +343,7 @@ static inline int ohci_put_iso_td(OHCIState *ohci,
|
|||
static inline int ohci_put_hcca(OHCIState *ohci,
|
||||
uint32_t addr, struct ohci_hcca *hcca)
|
||||
{
|
||||
cpu_physical_memory_rw(addr + ohci->localmem_base,
|
||||
(uint8_t *)hcca, sizeof(*hcca), 1);
|
||||
cpu_physical_memory_write(addr + ohci->localmem_base, (uint8_t*)hcca, sizeof(*hcca));
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -362,9 +388,9 @@ static void ohci_copy_iso_td(OHCIState *ohci,
|
|||
|
||||
static void ohci_process_lists(OHCIState *ohci, int completion);
|
||||
|
||||
static void ohci_async_complete_packet(USBPacket *packet, void *opaque)
|
||||
static void ohci_async_complete_packet(USBPort *port, USBPacket *packet)
|
||||
{
|
||||
OHCIState *ohci = (OHCIState *)opaque;
|
||||
OHCIState *ohci = (OHCIState*)packet->owner->opaque;
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf("Async packet complete\n");
|
||||
#endif
|
||||
|
@ -379,7 +405,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
{
|
||||
int dir;
|
||||
size_t len = 0;
|
||||
#ifdef DEBUG_ISOCH
|
||||
const char *str = NULL;
|
||||
#endif
|
||||
int pid;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -443,15 +471,21 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
dir = OHCI_BM(ed->flags, ED_D);
|
||||
switch (dir) {
|
||||
case OHCI_TD_DIR_IN:
|
||||
#ifdef DEBUG_ISOCH
|
||||
str = "in";
|
||||
#endif
|
||||
pid = USB_TOKEN_IN;
|
||||
break;
|
||||
case OHCI_TD_DIR_OUT:
|
||||
#ifdef DEBUG_ISOCH
|
||||
str = "out";
|
||||
#endif
|
||||
pid = USB_TOKEN_OUT;
|
||||
break;
|
||||
case OHCI_TD_DIR_SETUP:
|
||||
#ifdef DEBUG_ISOCH
|
||||
str = "setup";
|
||||
#endif
|
||||
pid = USB_TOKEN_SETUP;
|
||||
break;
|
||||
default:
|
||||
|
@ -527,9 +561,7 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
|
|||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ohci->usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
ohci->usb_packet.complete_opaque = ohci;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
|
@ -614,7 +646,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
{
|
||||
int dir;
|
||||
size_t len = 0;
|
||||
#ifdef DEBUG_PACKET
|
||||
const char *str = NULL;
|
||||
#endif
|
||||
int pid;
|
||||
int ret;
|
||||
int i;
|
||||
|
@ -651,15 +685,21 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
|
||||
switch (dir) {
|
||||
case OHCI_TD_DIR_IN:
|
||||
#ifdef DEBUG_PACKET
|
||||
str = "in";
|
||||
#endif
|
||||
pid = USB_TOKEN_IN;
|
||||
break;
|
||||
case OHCI_TD_DIR_OUT:
|
||||
#ifdef DEBUG_PACKET
|
||||
str = "out";
|
||||
#endif
|
||||
pid = USB_TOKEN_OUT;
|
||||
break;
|
||||
case OHCI_TD_DIR_SETUP:
|
||||
#ifdef DEBUG_PACKET
|
||||
str = "setup";
|
||||
#endif
|
||||
pid = USB_TOKEN_SETUP;
|
||||
break;
|
||||
default:
|
||||
|
@ -681,7 +721,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
flag_r = (td.flags & OHCI_TD_R) != 0;
|
||||
#ifdef DEBUG_PACKET
|
||||
dprintf(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n",
|
||||
addr, len, str, flag_r, td.cbp, td.be);
|
||||
addr, (int64_t)len, str, flag_r, td.cbp, td.be);
|
||||
|
||||
if (len > 0 && dir != OHCI_TD_DIR_IN) {
|
||||
dprintf(" data:");
|
||||
|
@ -717,9 +757,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
ohci->usb_packet.devep = OHCI_BM(ed->flags, ED_EN);
|
||||
ohci->usb_packet.data = ohci->usb_buf;
|
||||
ohci->usb_packet.len = len;
|
||||
ohci->usb_packet.complete_cb = ohci_async_complete_packet;
|
||||
ohci->usb_packet.complete_opaque = ohci;
|
||||
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
|
||||
ret = usb_handle_packet(dev, &ohci->usb_packet);
|
||||
if (ret != USB_RET_NODEV)
|
||||
break;
|
||||
}
|
||||
|
@ -773,7 +811,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
|
|||
switch (ret) {
|
||||
case USB_RET_NODEV:
|
||||
OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
|
||||
return 1;
|
||||
case USB_RET_NAK:
|
||||
dprintf("usb-ohci: got NAK\n");
|
||||
return 1;
|
||||
|
@ -873,6 +910,7 @@ static void ohci_sof(OHCIState *ohci)
|
|||
{
|
||||
ohci->sof_time = get_clock();
|
||||
ohci->eof_timer += usb_frame_time;
|
||||
//ohci->sof_time = qemu_get_clock_ns(vm_clock);
|
||||
//qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
|
||||
ohci_set_interrupt(ohci, OHCI_INTR_SF);
|
||||
}
|
||||
|
@ -881,9 +919,10 @@ static void ohci_sof(OHCIState *ohci)
|
|||
static void ohci_process_lists(OHCIState *ohci, int completion)
|
||||
{
|
||||
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
|
||||
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head)
|
||||
dprintf("usb-ohci: head %x, cur %x\n",
|
||||
ohci->ctrl_head, ohci->ctrl_cur);
|
||||
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
|
||||
dprintf("usb-ohci: head %x, cur %x\n",
|
||||
ohci->ctrl_head, ohci->ctrl_cur);
|
||||
}
|
||||
if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
|
||||
ohci->ctrl_cur = 0;
|
||||
ohci->status &= ~OHCI_STATUS_CLF;
|
||||
|
@ -901,7 +940,7 @@ static void ohci_process_lists(OHCIState *ohci, int completion)
|
|||
/* Do frame processing on frame boundary */
|
||||
void ohci_frame_boundary(void *opaque)
|
||||
{
|
||||
OHCIState *ohci = (OHCIState *)opaque;
|
||||
OHCIState *ohci = (OHCIState*)opaque;
|
||||
struct ohci_hcca hcca;
|
||||
|
||||
ohci_read_hcca(ohci, ohci->hcca, &hcca);
|
||||
|
@ -954,7 +993,7 @@ void ohci_frame_boundary(void *opaque)
|
|||
/* Start sending SOF tokens across the USB bus, lists are processed in
|
||||
* next frame
|
||||
*/
|
||||
int ohci_bus_start(OHCIState *ohci)
|
||||
static int ohci_bus_start(OHCIState *ohci)
|
||||
{
|
||||
ohci->eof_timer = 0;
|
||||
|
||||
|
@ -966,7 +1005,7 @@ int ohci_bus_start(OHCIState *ohci)
|
|||
}
|
||||
|
||||
/* Stop sending SOF tokens on the bus */
|
||||
void ohci_bus_stop(OHCIState *ohci)
|
||||
static void ohci_bus_stop(OHCIState *ohci)
|
||||
{
|
||||
if (ohci->eof_timer)
|
||||
ohci->eof_timer=0;
|
||||
|
@ -1072,7 +1111,7 @@ static uint32_t ohci_get_frame_remaining(OHCIState *ohci)
|
|||
/* Being in USB operational state guarnatees sof_time was
|
||||
* set already.
|
||||
*/
|
||||
tks = get_clock() - ohci->sof_time;
|
||||
tks = get_clock() - ohci->sof_time;
|
||||
|
||||
/* avoid muldiv if possible */
|
||||
if (tks >= usb_frame_time)
|
||||
|
@ -1140,8 +1179,9 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
|
|||
|
||||
ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PES);
|
||||
|
||||
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS))
|
||||
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PSS)) {
|
||||
dprintf("usb-ohci: port %d: SUSPEND\n", portnum);
|
||||
}
|
||||
|
||||
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
|
||||
dprintf("usb-ohci: port %d: RESET\n", portnum);
|
||||
|
@ -1167,9 +1207,11 @@ static void ohci_port_set_status(OHCIState *ohci, int portnum, uint32_t val)
|
|||
|
||||
uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
|
||||
{
|
||||
OHCIState *ohci = (OHCIState *)ptr;
|
||||
OHCIState *ohci = (OHCIState*)ptr;
|
||||
uint32_t retval;
|
||||
|
||||
addr &= 0xff;
|
||||
|
||||
/* Only aligned reads are allowed on OHCI */
|
||||
if (addr & 3) {
|
||||
fprintf(stderr, "usb-ohci: Mis-aligned read\n");
|
||||
|
@ -1283,9 +1325,6 @@ uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
|
|||
}
|
||||
}
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
retval = bswap32(retval);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
|
||||
|
@ -1293,9 +1332,7 @@ void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|||
{
|
||||
OHCIState *ohci = (OHCIState *)ptr;
|
||||
|
||||
#ifdef TARGET_WORDS_BIGENDIAN
|
||||
val = bswap32(val);
|
||||
#endif
|
||||
addr &= 0xff;
|
||||
|
||||
/* Only aligned reads are allowed on OHCI */
|
||||
if (addr & 3) {
|
||||
|
@ -1344,6 +1381,10 @@ void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|||
ohci->hcca = val & OHCI_HCCA_MASK;
|
||||
break;
|
||||
|
||||
case 7: /* HcPeriodCurrentED */
|
||||
/* Ignore writes to this read-only register, Linux does them */
|
||||
break;
|
||||
|
||||
case 8: /* HcControlHeadED */
|
||||
ohci->ctrl_head = val & OHCI_EDPTR_MASK;
|
||||
break;
|
||||
|
@ -1413,6 +1454,17 @@ void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
|
|||
}
|
||||
}
|
||||
|
||||
static USBPortOps ohci_port_ops = {
|
||||
ohci_attach,
|
||||
ohci_detach,
|
||||
ohci_child_detach,
|
||||
ohci_wakeup,
|
||||
ohci_async_complete_packet,
|
||||
};
|
||||
|
||||
static USBBusOps ohci_bus_ops = {
|
||||
};
|
||||
|
||||
OHCIState *ohci_create(uint32_t base, int ports)
|
||||
{
|
||||
OHCIState *ohci=(OHCIState*)malloc(sizeof(OHCIState));
|
||||
|
@ -1443,15 +1495,15 @@ OHCIState *ohci_create(uint32_t base, int ports)
|
|||
usb_bit_time = 1;
|
||||
}
|
||||
#endif
|
||||
dprintf("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
|
||||
usb_frame_time, usb_bit_time);
|
||||
dprintf("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
|
||||
usb_frame_time, usb_bit_time);
|
||||
}
|
||||
|
||||
ohci->num_ports = ports;
|
||||
for (i = 0; i < ports; i++) {
|
||||
ohci->rhport[i].port.opaque = ohci;
|
||||
ohci->rhport[i].port.index = i;
|
||||
ohci->rhport[i].port.attach = ohci_attach;
|
||||
ohci->rhport[i].port.ops = &ohci_port_ops;
|
||||
}
|
||||
|
||||
ohci->async_td = 0;
|
||||
|
|
|
@ -1,259 +1,382 @@
|
|||
/*
|
||||
* QEMU USB API
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu-queue.h"
|
||||
|
||||
#define USB_TOKEN_SETUP 0x2d
|
||||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
|
||||
/* specific usb messages, also sent in the 'pid' parameter */
|
||||
#define USB_MSG_ATTACH 0x100
|
||||
#define USB_MSG_DETACH 0x101
|
||||
#define USB_MSG_RESET 0x102
|
||||
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
#define USB_RET_ASYNC (-5)
|
||||
|
||||
#define USB_SPEED_LOW 0
|
||||
#define USB_SPEED_FULL 1
|
||||
#define USB_SPEED_HIGH 2
|
||||
|
||||
#define USB_STATE_NOTATTACHED 0
|
||||
#define USB_STATE_ATTACHED 1
|
||||
//#define USB_STATE_POWERED 2
|
||||
#define USB_STATE_DEFAULT 3
|
||||
//#define USB_STATE_ADDRESS 4
|
||||
//#define USB_STATE_CONFIGURED 5
|
||||
#define USB_STATE_SUSPENDED 6
|
||||
|
||||
#define USB_CLASS_AUDIO 1
|
||||
#define USB_CLASS_COMM 2
|
||||
#define USB_CLASS_HID 3
|
||||
#define USB_CLASS_PHYSICAL 5
|
||||
#define USB_CLASS_STILL_IMAGE 6
|
||||
#define USB_CLASS_PRINTER 7
|
||||
#define USB_CLASS_MASS_STORAGE 8
|
||||
#define USB_CLASS_HUB 9
|
||||
#define USB_CLASS_CDC_DATA 0x0a
|
||||
#define USB_CLASS_CSCID 0x0b
|
||||
#define USB_CLASS_CONTENT_SEC 0x0d
|
||||
#define USB_CLASS_APP_SPEC 0xfe
|
||||
#define USB_CLASS_VENDOR_SPEC 0xff
|
||||
|
||||
#define USB_DIR_OUT 0
|
||||
#define USB_DIR_IN 0x80
|
||||
|
||||
#define USB_TYPE_MASK (0x03 << 5)
|
||||
#define USB_TYPE_STANDARD (0x00 << 5)
|
||||
#define USB_TYPE_CLASS (0x01 << 5)
|
||||
#define USB_TYPE_VENDOR (0x02 << 5)
|
||||
#define USB_TYPE_RESERVED (0x03 << 5)
|
||||
|
||||
#define USB_RECIP_MASK 0x1f
|
||||
#define USB_RECIP_DEVICE 0x00
|
||||
#define USB_RECIP_INTERFACE 0x01
|
||||
#define USB_RECIP_ENDPOINT 0x02
|
||||
#define USB_RECIP_OTHER 0x03
|
||||
|
||||
#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define InterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||
#define USB_REQ_SET_FEATURE 0x03
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
|
||||
#define USB_DEVICE_SELF_POWERED 0
|
||||
#define USB_DEVICE_REMOTE_WAKEUP 1
|
||||
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
#define USB_DT_STRING 0x03
|
||||
#define USB_DT_INTERFACE 0x04
|
||||
#define USB_DT_ENDPOINT 0x05
|
||||
|
||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||
#define USB_ENDPOINT_XFER_ISOC 1
|
||||
#define USB_ENDPOINT_XFER_BULK 2
|
||||
#define USB_ENDPOINT_XFER_INT 3
|
||||
|
||||
typedef struct USBBus USBBus;
|
||||
typedef struct USBPort USBPort;
|
||||
typedef struct USBDevice USBDevice;
|
||||
typedef struct USBDeviceInfo USBDeviceInfo;
|
||||
typedef struct USBPacket USBPacket;
|
||||
|
||||
/* definition of a USB device */
|
||||
struct USBDevice {
|
||||
//DeviceState qdev;
|
||||
USBDeviceInfo *info;
|
||||
void *opaque;
|
||||
|
||||
int speed;
|
||||
uint8_t addr;
|
||||
char product_desc[32];
|
||||
int auto_attach;
|
||||
int attached;
|
||||
|
||||
int state;
|
||||
uint8_t setup_buf[8];
|
||||
uint8_t data_buf[1024];
|
||||
int remote_wakeup;
|
||||
int setup_state;
|
||||
int setup_len;
|
||||
int setup_index;
|
||||
};
|
||||
|
||||
struct USBDeviceInfo {
|
||||
//DeviceInfo qdev;
|
||||
int (*init)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Process USB packet.
|
||||
* Called by the HC (Host Controller).
|
||||
*
|
||||
* Returns length of the transaction
|
||||
* or one of the USB_RET_XXX codes.
|
||||
*/
|
||||
int (*handle_packet)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
/*
|
||||
* Called when device is destroyed.
|
||||
*/
|
||||
void (*handle_destroy)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Reset the device
|
||||
*/
|
||||
void (*handle_reset)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Process control request.
|
||||
* Called from handle_packet().
|
||||
*
|
||||
* Returns length or one of the USB_RET_ codes.
|
||||
*/
|
||||
int (*handle_control)(USBDevice *dev, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
|
||||
/*
|
||||
* Process data transfers (both BULK and ISOC).
|
||||
* Called from handle_packet().
|
||||
*
|
||||
* Returns length or one of the USB_RET_ codes.
|
||||
*/
|
||||
int (*handle_data)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
const char *product_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);
|
||||
|
||||
/* USB port on which a device can be connected */
|
||||
struct USBPort {
|
||||
USBDevice *dev;
|
||||
usb_attachfn attach;
|
||||
void *opaque;
|
||||
int index; /* internal port index, may be used with the opaque */
|
||||
QTAILQ_ENTRY(USBPort) next;
|
||||
};
|
||||
|
||||
typedef void USBCallback(USBPacket * packet, void *opaque);
|
||||
|
||||
/* Structure used to hold information about an active USB packet. */
|
||||
struct USBPacket {
|
||||
/* Data fields for use by the driver. */
|
||||
int pid;
|
||||
uint8_t devaddr;
|
||||
uint8_t devep;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
/* Internal use by the USB layer. */
|
||||
USBCallback *complete_cb;
|
||||
void *complete_opaque;
|
||||
USBCallback *cancel_cb;
|
||||
void *cancel_opaque;
|
||||
};
|
||||
|
||||
/* Defer completion of a USB packet. The hadle_packet routine should then
|
||||
return USB_RET_ASYNC. Packets that complete immediately (before
|
||||
handle_packet returns) should not call this method. */
|
||||
static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
|
||||
void * opaque)
|
||||
{
|
||||
p->cancel_cb = cancel;
|
||||
p->cancel_opaque = opaque;
|
||||
}
|
||||
|
||||
/* Notify the controller that an async packet is complete. This should only
|
||||
be called for packets previously deferred with usb_defer_packet, and
|
||||
should never be called from within handle_packet. */
|
||||
static inline void usb_packet_complete(USBPacket *p)
|
||||
{
|
||||
p->complete_cb(p, p->complete_opaque);
|
||||
}
|
||||
|
||||
/* Cancel an active packet. The packed must have been deferred with
|
||||
usb_defer_packet, and not yet completed. */
|
||||
static inline void usb_cancel_packet(USBPacket * p)
|
||||
{
|
||||
p->cancel_cb(p, p->cancel_opaque);
|
||||
}
|
||||
|
||||
void usb_attach(USBPort *port, 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);
|
||||
|
||||
/* usb-hid.c */
|
||||
void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *));
|
||||
|
||||
/* usb ports of the VM */
|
||||
|
||||
#define VM_USB_HUB_SIZE 2
|
||||
|
||||
/* usb-kbd.cpp */
|
||||
USBDevice *usb_keyboard_init(void);
|
||||
/*
|
||||
* QEMU USB API
|
||||
*
|
||||
* Copyright (c) 2005 Fabrice Bellard
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
#include "qemu-queue.h"
|
||||
|
||||
/* Constants related to the USB / PCI interaction */
|
||||
#define USB_SBRN 0x60 /* Serial Bus Release Number Register */
|
||||
#define USB_RELEASE_1 0x10 /* USB 1.0 */
|
||||
#define USB_RELEASE_2 0x20 /* USB 2.0 */
|
||||
#define USB_RELEASE_3 0x30 /* USB 3.0 */
|
||||
|
||||
#define USB_TOKEN_SETUP 0x2d
|
||||
#define USB_TOKEN_IN 0x69 /* device -> host */
|
||||
#define USB_TOKEN_OUT 0xe1 /* host -> device */
|
||||
|
||||
/* specific usb messages, also sent in the 'pid' parameter */
|
||||
#define USB_MSG_ATTACH 0x100
|
||||
#define USB_MSG_DETACH 0x101
|
||||
#define USB_MSG_RESET 0x102
|
||||
|
||||
#define USB_RET_NODEV (-1)
|
||||
#define USB_RET_NAK (-2)
|
||||
#define USB_RET_STALL (-3)
|
||||
#define USB_RET_BABBLE (-4)
|
||||
#define USB_RET_ASYNC (-5)
|
||||
|
||||
#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
|
||||
//#define USB_STATE_POWERED 2
|
||||
#define USB_STATE_DEFAULT 3
|
||||
//#define USB_STATE_ADDRESS 4
|
||||
//#define USB_STATE_CONFIGURED 5
|
||||
#define USB_STATE_SUSPENDED 6
|
||||
|
||||
#define USB_CLASS_AUDIO 1
|
||||
#define USB_CLASS_COMM 2
|
||||
#define USB_CLASS_HID 3
|
||||
#define USB_CLASS_PHYSICAL 5
|
||||
#define USB_CLASS_STILL_IMAGE 6
|
||||
#define USB_CLASS_PRINTER 7
|
||||
#define USB_CLASS_MASS_STORAGE 8
|
||||
#define USB_CLASS_HUB 9
|
||||
#define USB_CLASS_CDC_DATA 0x0a
|
||||
#define USB_CLASS_CSCID 0x0b
|
||||
#define USB_CLASS_CONTENT_SEC 0x0d
|
||||
#define USB_CLASS_APP_SPEC 0xfe
|
||||
#define USB_CLASS_VENDOR_SPEC 0xff
|
||||
|
||||
#define USB_DIR_OUT 0
|
||||
#define USB_DIR_IN 0x80
|
||||
|
||||
#define USB_TYPE_MASK (0x03 << 5)
|
||||
#define USB_TYPE_STANDARD (0x00 << 5)
|
||||
#define USB_TYPE_CLASS (0x01 << 5)
|
||||
#define USB_TYPE_VENDOR (0x02 << 5)
|
||||
#define USB_TYPE_RESERVED (0x03 << 5)
|
||||
|
||||
#define USB_RECIP_MASK 0x1f
|
||||
#define USB_RECIP_DEVICE 0x00
|
||||
#define USB_RECIP_INTERFACE 0x01
|
||||
#define USB_RECIP_ENDPOINT 0x02
|
||||
#define USB_RECIP_OTHER 0x03
|
||||
|
||||
#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
|
||||
#define InterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define InterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
|
||||
#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
#define EndpointOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
|
||||
#define ClassInterfaceRequest \
|
||||
((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
|
||||
#define ClassInterfaceOutRequest \
|
||||
((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
|
||||
|
||||
#define USB_REQ_GET_STATUS 0x00
|
||||
#define USB_REQ_CLEAR_FEATURE 0x01
|
||||
#define USB_REQ_SET_FEATURE 0x03
|
||||
#define USB_REQ_SET_ADDRESS 0x05
|
||||
#define USB_REQ_GET_DESCRIPTOR 0x06
|
||||
#define USB_REQ_SET_DESCRIPTOR 0x07
|
||||
#define USB_REQ_GET_CONFIGURATION 0x08
|
||||
#define USB_REQ_SET_CONFIGURATION 0x09
|
||||
#define USB_REQ_GET_INTERFACE 0x0A
|
||||
#define USB_REQ_SET_INTERFACE 0x0B
|
||||
#define USB_REQ_SYNCH_FRAME 0x0C
|
||||
|
||||
#define USB_DEVICE_SELF_POWERED 0
|
||||
#define USB_DEVICE_REMOTE_WAKEUP 1
|
||||
|
||||
#define USB_DT_DEVICE 0x01
|
||||
#define USB_DT_CONFIG 0x02
|
||||
#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_DT_DEBUG 0x0A
|
||||
#define USB_DT_INTERFACE_ASSOC 0x0B
|
||||
|
||||
#define USB_ENDPOINT_XFER_CONTROL 0
|
||||
#define USB_ENDPOINT_XFER_ISOC 1
|
||||
#define USB_ENDPOINT_XFER_BULK 2
|
||||
#define USB_ENDPOINT_XFER_INT 3
|
||||
|
||||
typedef struct USBBus USBBus;
|
||||
typedef struct USBBusOps USBBusOps;
|
||||
typedef struct USBPort USBPort;
|
||||
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 USBDescIfaceAssoc USBDescIfaceAssoc;
|
||||
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;
|
||||
|
||||
/* Actual connected speed */
|
||||
int speed;
|
||||
/* Supported speeds, not in info because it may be variable (hostdevs) */
|
||||
int speedmask;
|
||||
uint8_t addr;
|
||||
char product_desc[32];
|
||||
int auto_attach;
|
||||
int attached;
|
||||
|
||||
int32_t state;
|
||||
uint8_t setup_buf[8];
|
||||
uint8_t data_buf[4096];
|
||||
int32_t remote_wakeup;
|
||||
int32_t setup_state;
|
||||
int32_t setup_len;
|
||||
int32_t setup_index;
|
||||
|
||||
QLIST_HEAD(, USBDescString) strings;
|
||||
const USBDescDevice *device;
|
||||
const USBDescConfig *config;
|
||||
};
|
||||
|
||||
struct USBDeviceInfo {
|
||||
//DeviceInfo qdev;
|
||||
int (*init)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Process USB packet.
|
||||
* Called by the HC (Host Controller).
|
||||
*
|
||||
* Returns length of the transaction
|
||||
* or one of the USB_RET_XXX codes.
|
||||
*/
|
||||
int (*handle_packet)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
/*
|
||||
* Called when a packet is canceled.
|
||||
*/
|
||||
void (*cancel_packet)(USBDevice *dev, USBPacket *p);
|
||||
|
||||
/*
|
||||
* Called when device is destroyed.
|
||||
*/
|
||||
void (*handle_destroy)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Attach the device
|
||||
*/
|
||||
void (*handle_attach)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Reset the device
|
||||
*/
|
||||
void (*handle_reset)(USBDevice *dev);
|
||||
|
||||
/*
|
||||
* Process control request.
|
||||
* Called from handle_packet().
|
||||
*
|
||||
* Returns length or one of the USB_RET_ codes.
|
||||
*/
|
||||
int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
|
||||
int index, int length, uint8_t *data);
|
||||
|
||||
/*
|
||||
* Process data transfers (both BULK and ISOC).
|
||||
* Called from handle_packet().
|
||||
*
|
||||
* Returns length or one of the USB_RET_ codes.
|
||||
*/
|
||||
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 struct USBPortOps {
|
||||
void (*attach)(USBPort *port);
|
||||
void (*detach)(USBPort *port);
|
||||
/*
|
||||
* This gets called when a device downstream from the device attached to
|
||||
* the port (iow attached through a hub) gets detached.
|
||||
*/
|
||||
void (*child_detach)(USBPort *port, USBDevice *child);
|
||||
void (*wakeup)(USBPort *port);
|
||||
/*
|
||||
* Note that port->dev will be different then the device from which
|
||||
* the packet originated when a hub is involved, if you want the orginating
|
||||
* device use p->owner
|
||||
*/
|
||||
void (*complete)(USBPort *port, USBPacket *p);
|
||||
} USBPortOps;
|
||||
|
||||
/* USB port on which a device can be connected */
|
||||
struct USBPort {
|
||||
USBDevice *dev;
|
||||
int speedmask;
|
||||
char path[16];
|
||||
USBPortOps *ops;
|
||||
void *opaque;
|
||||
int index; /* internal port index, may be used with the opaque */
|
||||
QTAILQ_ENTRY(USBPort) next;
|
||||
};
|
||||
|
||||
typedef void USBCallback(USBPacket * packet, void *opaque);
|
||||
|
||||
/* Structure used to hold information about an active USB packet. */
|
||||
struct USBPacket {
|
||||
/* Data fields for use by the driver. */
|
||||
int pid;
|
||||
uint8_t devaddr;
|
||||
uint8_t devep;
|
||||
uint8_t *data;
|
||||
int len;
|
||||
/* Internal use by the USB layer. */
|
||||
USBDevice *owner;
|
||||
};
|
||||
|
||||
int usb_handle_packet(USBDevice *dev, USBPacket *p);
|
||||
void usb_packet_complete(USBDevice *dev, USBPacket *p);
|
||||
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);
|
||||
void usb_generic_async_ctrl_complete(USBDevice *s, USBPacket *p);
|
||||
int set_usb_string(uint8_t *buf, const char *str);
|
||||
void usb_send_msg(USBDevice *dev, int msg);
|
||||
|
||||
/* usb-linux.c */
|
||||
//USBDevice *usb_host_device_open(const char *devname);
|
||||
//int usb_host_device_close(const char *devname);
|
||||
//void usb_host_info(Monitor *mon);
|
||||
|
||||
/* usb-hid.c */
|
||||
void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *));
|
||||
|
||||
/* usb-bt.c */
|
||||
//USBDevice *usb_bt_init(HCIInfo *hci);
|
||||
|
||||
/* usb ports of the VM */
|
||||
|
||||
#define VM_USB_HUB_SIZE 8
|
||||
|
||||
/* usb-musb.c */
|
||||
enum musb_irq_source_e {
|
||||
musb_irq_suspend = 0,
|
||||
musb_irq_resume,
|
||||
musb_irq_rst_babble,
|
||||
musb_irq_sof,
|
||||
musb_irq_connect,
|
||||
musb_irq_disconnect,
|
||||
musb_irq_vbus_request,
|
||||
musb_irq_vbus_error,
|
||||
musb_irq_rx,
|
||||
musb_irq_tx,
|
||||
musb_set_vbus,
|
||||
musb_set_session,
|
||||
__musb_irq_max,
|
||||
};
|
||||
|
||||
typedef struct MUSBState MUSBState;
|
||||
//MUSBState *musb_init(qemu_irq *irqs);
|
||||
uint32_t musb_core_intr_get(MUSBState *s);
|
||||
void musb_core_intr_clear(MUSBState *s, uint32_t mask);
|
||||
void musb_set_size(MUSBState *s, int epnum, int size, int is_tx);
|
||||
|
||||
/* usb-bus.c */
|
||||
|
||||
struct USBBus {
|
||||
//BusState qbus;
|
||||
USBBusOps *ops;
|
||||
int busnr;
|
||||
int nfree;
|
||||
int nused;
|
||||
QTAILQ_HEAD(, USBPort) free;
|
||||
QTAILQ_HEAD(, USBPort) used;
|
||||
QTAILQ_ENTRY(USBBus) next;
|
||||
};
|
||||
|
||||
struct USBBusOps {
|
||||
int (*register_companion)(USBBus *bus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport);
|
||||
};
|
||||
|
||||
//void usb_bus_new(USBBus *bus, USBBusOps *ops, DeviceState *host);
|
||||
USBBus *usb_bus_find(int busnr);
|
||||
void usb_qdev_register(USBDeviceInfo *info);
|
||||
void usb_qdev_register_many(USBDeviceInfo *info);
|
||||
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,
|
||||
USBPortOps *ops, int speedmask);
|
||||
int usb_register_companion(const char *masterbus, USBPort *ports[],
|
||||
uint32_t portcount, uint32_t firstport,
|
||||
void *opaque, 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);
|
||||
int usb_device_delete_addr(int busnr, int addr);
|
||||
//
|
||||
//static inline USBBus *usb_bus_from_device(USBDevice *d)
|
||||
//{
|
||||
// return DO_UPCAST(USBBus, qbus, d->qdev.parent_bus);
|
||||
//}
|
||||
|
||||
USBDevice *usb_keyboard_init(void);
|
||||
|
|
|
@ -6,17 +6,6 @@
|
|||
void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf,
|
||||
int len, int is_write);
|
||||
|
||||
static inline void cpu_physical_memory_read(uint32_t addr,
|
||||
uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, buf, len, 0);
|
||||
}
|
||||
static inline void cpu_physical_memory_write(uint32_t addr,
|
||||
const uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
|
||||
}
|
||||
|
||||
/* compute with 96 bit intermediate result: (a*b)/c */
|
||||
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
|
||||
{
|
||||
|
|
|
@ -95,8 +95,16 @@ static inline char *realpath(const char *path, char *resolved_path)
|
|||
/* vl.c */
|
||||
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c);
|
||||
void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf, int len, int is_write);
|
||||
static inline void cpu_physical_memory_read(uint32_t addr, uint8_t *buf, int len);
|
||||
static inline void cpu_physical_memory_write(uint32_t addr, const uint8_t *buf, int len);
|
||||
static inline void cpu_physical_memory_read(uint32_t addr, uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, buf, len, 0);
|
||||
}
|
||||
|
||||
static inline void cpu_physical_memory_write(uint32_t addr, const uint8_t *buf, int len)
|
||||
{
|
||||
cpu_physical_memory_rw(addr, (uint8_t *)buf, len, 1);
|
||||
}
|
||||
|
||||
void *qemu_mallocz(uint32_t size);
|
||||
|
||||
#endif /* VL_H */
|
||||
|
|
Loading…
Reference in New Issue