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:
gigaherz 2011-09-04 15:24:23 +00:00
parent 34edc05ab8
commit ef50bc898d
15 changed files with 1655 additions and 1241 deletions

View File

@ -142,14 +142,18 @@ s32 CALLBACK USBinit() {
} }
qemu_ohci = ohci_create(0x1f801600,2); 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; return 0;
} }
void CALLBACK USBshutdown() { 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); free(qemu_ohci);
@ -237,27 +241,66 @@ void CALLBACK USBsetRAM(void *mem) {
// extended funcs // extended funcs
char USBfreezeID[] = "USB STv0"; char USBfreezeID[] = "USBqemu01";
typedef struct { typedef struct {
char freezeID[10];
OHCIState t; OHCIState t;
int extraData; // for future expansion with the device state
} USBfreezeData; } USBfreezeData;
s32 CALLBACK USBfreeze(int mode, freezeData *data) { s32 CALLBACK USBfreeze(int mode, freezeData *data) {
USBfreezeData *usbd; USBfreezeData usbd;
if (mode == FREEZE_LOAD) { if (mode == FREEZE_LOAD)
usbd = (USBfreezeData*)data->data; {
if (data->size != sizeof(USBfreezeData)) return -1; 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)); for(int i=0; i< qemu_ohci->num_ports; i++)
} else {
if (mode == FREEZE_SAVE) { 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->size = sizeof(USBfreezeData);
data->data = (s8*)malloc(data->size); data->data = (s8*)malloc(data->size);
if (data->data == NULL) return -1; if (data->data == NULL)
usbd = (USBfreezeData*)data->data; return -1;
memcpy(usbd, qemu_ohci, sizeof(OHCIState));
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; return 0;

View File

@ -57,7 +57,8 @@ void LoadConfig();
extern FILE *usbLog; extern FILE *usbLog;
void __Log(char *fmt, ...); void __Log(char *fmt, ...);
void SysMessage(char *fmt, ...); extern void SysMessage(const char *fmt, ...);
extern void SysMessage(const wchar_t *fmt, ...);
extern HWND gsWindowHandle; extern HWND gsWindowHandle;

View File

@ -64,7 +64,7 @@ void CfgSetSettingsDir( const char* dir )
} }
/*| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\ /*¯| Config File Format: |¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯¯*\
+--+---------------------+------------------------+ +--+---------------------+------------------------+
| | | |
| Option=Value | | Option=Value |
@ -76,7 +76,7 @@ void CfgSetSettingsDir( const char* dir )
| All Values are limited to 255 chars. | | All Values are limited to 255 chars. |
| | | |
+-------------------------------------------------+ +-------------------------------------------------+
\*_____________________________________________*/ \*______________________________________________*/
void CfgWriteBool(const TCHAR* Section, const TCHAR* Name, bool Value) 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) 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); int chars = GetPrivateProfileString(Section,Name,L"",Data,DataSize,CfgFile);
if(!chars) if(!chars)

View File

@ -3,7 +3,6 @@
#include <tchar.h> #include <tchar.h>
extern void CfgSetSettingsDir( const char* dir ); extern void CfgSetSettingsDir( const char* dir );
extern void CfgSetLogDir( const char* dir );
extern bool CfgFindName( const TCHAR *Section, const TCHAR* Name); extern bool CfgFindName( const TCHAR *Section, const TCHAR* Name);

View File

@ -126,7 +126,9 @@
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">true</ExcludedFromBuild>
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Devel|Win32'">true</ExcludedFromBuild>
</ClCompile> </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="..\qemu-usb\usb-kbd.cpp" />
<ClCompile Include="..\usb-mic\usb-mic-dummy.cpp"> <ClCompile Include="..\usb-mic\usb-mic-dummy.cpp">
<ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild> <ExcludedFromBuild Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</ExcludedFromBuild>

View File

@ -28,7 +28,6 @@ typedef uint32_t target_phys_addr_t;
typedef struct { typedef struct {
//USBBus bus; //USBBus bus;
//qemu_irq irq; //qemu_irq irq;
enum ohci_type type;
int mem; int mem;
int num_ports; int num_ports;
const char *name; const char *name;
@ -91,19 +90,8 @@ struct ohci_hcca {
uint32_t done; 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_bus_stop(OHCIState *ohci);
static void ohci_async_cancel_device(OHCIState *ohci, USBDevice *dev);
/* Bitfields for the first word of an Endpoint Desciptor. */ /* Bitfields for the first word of an Endpoint Desciptor. */
#define OHCI_ED_FA_SHIFT 0 #define OHCI_ED_FA_SHIFT 0
@ -282,6 +270,7 @@ struct ohci_iso_td {
#define OHCI_HRESET_FSBIR (1 << 0) #define OHCI_HRESET_FSBIR (1 << 0)
int64_t get_ticks_per_sec();
int64_t get_clock(); int64_t get_clock();

View File

@ -26,9 +26,35 @@
#include "USBinternal.h" #include "USBinternal.h"
//#include "usb.h" //#include "usb.h"
#include <assert.h>
void usb_attach(USBPort *port, USBDevice *dev) void usb_attach(USBPort *port, USBDevice *dev)
{ {
port->attach(port, dev); if (dev != NULL) {
/* attach */
if (port->dev) {
usb_attach(port, NULL);
}
dev->port = port;
port->dev = dev;
port->ops->attach(port);
usb_send_msg(dev, USB_MSG_ATTACH);
} else {
/* detach */
dev = port->dev;
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) protocol)
*/ */
#define SETUP_STATE_IDLE 0 #define SETUP_STATE_IDLE 0
#define SETUP_STATE_DATA 1 #define SETUP_STATE_SETUP 1
#define SETUP_STATE_ACK 2 #define SETUP_STATE_DATA 2
#define SETUP_STATE_ACK 3
static int do_token_setup(USBDevice *s, USBPacket *p) 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]; request = (s->setup_buf[0] << 8) | s->setup_buf[1];
value = (s->setup_buf[3] << 8) | s->setup_buf[2]; value = (s->setup_buf[3] << 8) | s->setup_buf[2];
index = (s->setup_buf[5] << 8) | s->setup_buf[4]; index = (s->setup_buf[5] << 8) | s->setup_buf[4];
if (s->setup_buf[0] & USB_DIR_IN) { 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); s->setup_len, s->data_buf);
if (ret == USB_RET_ASYNC) {
s->setup_state = SETUP_STATE_SETUP;
return USB_RET_ASYNC;
}
if (ret < 0) if (ret < 0)
return ret; return ret;
@ -68,6 +99,12 @@ static int do_token_setup(USBDevice *s, USBPacket *p)
s->setup_len = ret; s->setup_len = ret;
s->setup_state = SETUP_STATE_DATA; s->setup_state = SETUP_STATE_DATA;
} else { } 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) if (s->setup_len == 0)
s->setup_state = SETUP_STATE_ACK; s->setup_state = SETUP_STATE_ACK;
else else
@ -92,9 +129,12 @@ static int do_token_in(USBDevice *s, USBPacket *p)
switch(s->setup_state) { switch(s->setup_state) {
case SETUP_STATE_ACK: case SETUP_STATE_ACK:
if (!(s->setup_buf[0] & USB_DIR_IN)) { if (!(s->setup_buf[0] & USB_DIR_IN)) {
s->setup_state = SETUP_STATE_IDLE; ret = s->info->handle_control(s, p, request, value, index,
ret = s->info->handle_control(s, request, value, index,
s->setup_len, s->data_buf); s->setup_len, s->data_buf);
if (ret == USB_RET_ASYNC) {
return USB_RET_ASYNC;
}
s->setup_state = SETUP_STATE_IDLE;
if (ret > 0) if (ret > 0)
return 0; return 0;
return ret; return ret;
@ -169,6 +209,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
switch(p->pid) { switch(p->pid) {
case USB_MSG_ATTACH: case USB_MSG_ATTACH:
s->state = USB_STATE_ATTACHED; s->state = USB_STATE_ATTACHED;
if (s->info->handle_attach) {
s->info->handle_attach(s);
}
return 0; return 0;
case USB_MSG_DETACH: case USB_MSG_DETACH:
@ -179,7 +222,9 @@ int usb_generic_handle_packet(USBDevice *s, USBPacket *p)
s->remote_wakeup = 0; s->remote_wakeup = 0;
s->addr = 0; s->addr = 0;
s->state = USB_STATE_DEFAULT; s->state = USB_STATE_DEFAULT;
s->info->handle_reset(s); if (s->info->handle_reset) {
s->info->handle_reset(s);
}
return 0; return 0;
} }
@ -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 */ /* XXX: fix overflow */
int set_usb_string(uint8_t *buf, const char *str) 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) void usb_send_msg(USBDevice *dev, int msg)
{ {
USBPacket p; USBPacket p;
int ret;
memset(&p, 0, sizeof(p)); memset(&p, 0, sizeof(p));
p.pid = msg; p.pid = msg;
dev->info->handle_packet(dev, &p); ret = usb_handle_packet(dev, &p);
/* This _must_ be synchronous */ /* 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

View File

@ -25,7 +25,7 @@
//#define DEBUG //#define DEBUG
#define MAX_PORTS 8 #define NUM_PORTS 8
typedef struct USBHubPort { typedef struct USBHubPort {
USBPort port; USBPort port;
@ -35,8 +35,7 @@ typedef struct USBHubPort {
typedef struct USBHubState { typedef struct USBHubState {
USBDevice dev; USBDevice dev;
int nb_ports; USBHubPort ports[NUM_PORTS];
USBHubPort ports[MAX_PORTS];
} USBHubState; } USBHubState;
#define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE) #define ClearHubFeature (0x2000 | USB_REQ_CLEAR_FEATURE)
@ -82,72 +81,59 @@ typedef struct USBHubState {
/* same as Linux kernel root hubs */ /* same as Linux kernel root hubs */
static const uint8_t qemu_hub_dev_descriptor[] = { enum {
0x12, /* u8 bLength; */ STR_MANUFACTURER = 1,
0x01, /* u8 bDescriptorType; Device */ STR_PRODUCT,
0x10, 0x01, /* u16 bcdUSB; v1.1 */ STR_SERIALNUMBER,
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; */
}; };
/* XXX: patch interrupt size */ static const USBDescStrings desc_strings = {
static const uint8_t qemu_hub_config_descriptor[] = { [STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
[STR_PRODUCT] = "QEMU USB Hub",
[STR_SERIALNUMBER] = "314159",
};
/* one configuration */ static const USBDescIface desc_iface_hub = {
0x09, /* u8 bLength; */ .bInterfaceNumber = 0,
0x02, /* u8 bDescriptorType; Configuration */ .bNumEndpoints = 1,
0x19, 0x00, /* u16 wTotalLength; */ .bInterfaceClass = USB_CLASS_HUB,
0x01, /* u8 bNumInterfaces; (1) */ .eps = (USBDescEndpoint[]) {
0x01, /* u8 bConfigurationValue; */ {
0x00, /* u8 iConfiguration; */ .bEndpointAddress = USB_DIR_IN | 0x01,
0xc0, /* u8 bmAttributes; .bmAttributes = USB_ENDPOINT_XFER_INT,
Bit 7: must be set, .wMaxPacketSize = 1 + (NUM_PORTS + 7) / 8,
6: Self-powered, .bInterval = 0xff,
5: Remote wakeup, },
4..0: resvd */ }
0x00, /* u8 MaxPower; */ };
/* USB 1.1: static const USBDescDevice desc_device_hub = {
* USB 2.0, single TT organization (mandatory): .bcdUSB = 0x0110,
* one interface, protocol 0 .bDeviceClass = USB_CLASS_HUB,
* .bMaxPacketSize0 = 8,
* USB 2.0, multiple TT organization (optional): .bNumConfigurations = 1,
* two interfaces, protocols 1 (like single TT) .confs = (USBDescConfig[]) {
* and 2 (multiple TT mode) ... config is {
* sometimes settable .bNumInterfaces = 1,
* NOT IMPLEMENTED .bConfigurationValue = 1,
*/ .bmAttributes = 0xe0,
.nif = 1,
.ifs = &desc_iface_hub,
},
},
};
/* one interface */ static const USBDesc desc_hub = {
0x09, /* u8 if_bLength; */ .id = {
0x04, /* u8 if_bDescriptorType; Interface */ .idVendor = 0,
0x00, /* u8 if_bInterfaceNumber; */ .idProduct = 0,
0x00, /* u8 if_bAlternateSetting; */ .bcdDevice = 0x0101,
0x01, /* u8 if_bNumEndpoints; */ .iManufacturer = STR_MANUFACTURER,
0x09, /* u8 if_bInterfaceClass; HUB_CLASSCODE */ .iProduct = STR_PRODUCT,
0x00, /* u8 if_bInterfaceSubClass; */ .iSerialNumber = STR_SERIALNUMBER,
0x00, /* u8 if_bInterfaceProtocol; [usb1.1 or single tt] */ },
0x00, /* u8 if_iInterface; */ .full = &desc_device_hub,
.str = desc_strings,
/* 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 uint8_t qemu_hub_hub_descriptor[] = 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 */ /* 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]; USBHubPort *port = &s->ports[port1->index];
if (dev) { port->wPortStatus |= PORT_STAT_CONNECTION;
if (port->port.dev) port->wPortChange |= PORT_STAT_C_CONNECTION;
usb_attach(port1, NULL); if (port->port.dev->speed == USB_SPEED_LOW) {
port->wPortStatus |= PORT_STAT_LOW_SPEED;
port->wPortStatus |= PORT_STAT_CONNECTION;
port->wPortChange |= PORT_STAT_C_CONNECTION;
if (dev->speed == USB_SPEED_LOW)
port->wPortStatus |= PORT_STAT_LOW_SPEED;
else
port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
port->port.dev = dev;
/* send the attach message */
usb_send_msg(dev, USB_MSG_ATTACH);
} else { } else {
dev = port->port.dev; port->wPortStatus &= ~PORT_STAT_LOW_SPEED;
if (dev) { }
port->wPortStatus &= ~PORT_STAT_CONNECTION; }
port->wPortChange |= PORT_STAT_C_CONNECTION;
if (port->wPortStatus & PORT_STAT_ENABLE) { static void usb_hub_detach(USBPort *port1)
port->wPortStatus &= ~PORT_STAT_ENABLE; {
port->wPortChange |= PORT_STAT_C_ENABLE; USBHubState *s = port1->opaque;
} USBHubPort *port = &s->ports[port1->index];
/* send the detach message */
usb_send_msg(dev, USB_MSG_DETACH); /* Let upstream know the device on this port is gone */
port->port.dev = NULL; 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 */ /* XXX: do it */
} }
static int usb_hub_handle_control(USBDevice *dev, int request, int value, static int usb_hub_handle_control(USBDevice *dev, USBPacket *p,
int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
USBHubState *s = (USBHubState *)dev; USBHubState *s = (USBHubState *)dev;
int ret; int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
switch(request) { switch(request) {
case DeviceRequest | USB_REQ_GET_STATUS:
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
data[1] = 0x00;
ret = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
} else {
goto fail;
}
ret = 0;
break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == 0 && index != 0x81) { /* clear ep halt */ if (value == 0 && index != 0x81) { /* clear ep halt */
goto fail; goto fail;
} }
ret = 0; ret = 0;
break; break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 1;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
dev->addr = value;
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
memcpy(data, qemu_hub_dev_descriptor,
sizeof(qemu_hub_dev_descriptor));
ret = sizeof(qemu_hub_dev_descriptor);
break;
case USB_DT_CONFIG:
memcpy(data, qemu_hub_config_descriptor,
sizeof(qemu_hub_config_descriptor));
/* status change endpoint size based on number
* of ports */
data[22] = (s->nb_ports + 1 + 7) / 8;
ret = sizeof(qemu_hub_config_descriptor);
break;
case USB_DT_STRING:
switch(value & 0xff) {
case 0:
/* language ids */
data[0] = 4;
data[1] = 3;
data[2] = 0x09;
data[3] = 0x04;
ret = 4;
break;
case 1:
/* serial number */
ret = set_usb_string(data, "314159");
break;
case 2:
/* product description */
ret = set_usb_string(data, "QEMU USB Hub");
break;
case 3:
/* vendor description */
ret = set_usb_string(data, "QEMU 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: case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0; data[0] = 0;
ret = 1; ret = 1;
@ -314,8 +263,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
{ {
unsigned int n = index - 1; unsigned int n = index - 1;
USBHubPort *port; USBHubPort *port;
if (n >= s->nb_ports) if (n >= NUM_PORTS) {
goto fail; goto fail;
}
port = &s->ports[n]; port = &s->ports[n];
data[0] = port->wPortStatus; data[0] = port->wPortStatus;
data[1] = port->wPortStatus >> 8; data[1] = port->wPortStatus >> 8;
@ -337,8 +287,9 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
unsigned int n = index - 1; unsigned int n = index - 1;
USBHubPort *port; USBHubPort *port;
USBDevice *dev; USBDevice *dev;
if (n >= s->nb_ports) if (n >= NUM_PORTS) {
goto fail; goto fail;
}
port = &s->ports[n]; port = &s->ports[n];
dev = port->port.dev; dev = port->port.dev;
switch(value) { switch(value) {
@ -365,11 +316,11 @@ static int usb_hub_handle_control(USBDevice *dev, int request, int value,
{ {
unsigned int n = index - 1; unsigned int n = index - 1;
USBHubPort *port; USBHubPort *port;
USBDevice *dev;
if (n >= s->nb_ports) if (n >= NUM_PORTS) {
goto fail; goto fail;
}
port = &s->ports[n]; port = &s->ports[n];
dev = port->port.dev;
switch(value) { switch(value) {
case PORT_ENABLE: case PORT_ENABLE:
port->wPortStatus &= ~PORT_STAT_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; unsigned int n, limit, var_hub_size = 0;
memcpy(data, qemu_hub_hub_descriptor, memcpy(data, qemu_hub_hub_descriptor,
sizeof(qemu_hub_hub_descriptor)); sizeof(qemu_hub_hub_descriptor));
data[2] = s->nb_ports; data[2] = NUM_PORTS;
/* fill DeviceRemovable bits */ /* fill DeviceRemovable bits */
limit = ((s->nb_ports + 1 + 7) / 8) + 7; limit = ((NUM_PORTS + 1 + 7) / 8) + 7;
for (n = 7; n < limit; n++) { for (n = 7; n < limit; n++) {
data[n] = 0x00; data[n] = 0x00;
var_hub_size++; var_hub_size++;
} }
/* fill PortPwrCtrlMask bits */ /* fill PortPwrCtrlMask bits */
limit = limit + ((s->nb_ports + 7) / 8); limit = limit + ((NUM_PORTS + 7) / 8);
for (;n < limit; n++) { for (;n < limit; n++) {
data[n] = 0xff; data[n] = 0xff;
var_hub_size++; var_hub_size++;
@ -442,14 +393,14 @@ static int usb_hub_handle_data(USBDevice *dev, USBPacket *p)
USBHubPort *port; USBHubPort *port;
unsigned int status; unsigned int status;
int i, n; int i, n;
n = (s->nb_ports + 1 + 7) / 8; n = (NUM_PORTS + 1 + 7) / 8;
if (p->len == 1) { /* FreeBSD workaround */ if (p->len == 1) { /* FreeBSD workaround */
n = 1; n = 1;
} else if (n > p->len) { } else if (n > p->len) {
return USB_RET_BABBLE; return USB_RET_BABBLE;
} }
status = 0; status = 0;
for(i = 0; i < s->nb_ports; i++) { for(i = 0; i < NUM_PORTS; i++) {
port = &s->ports[i]; port = &s->ports[i];
if (port->wPortChange) if (port->wPortChange)
status |= (1 << (i + 1)); status |= (1 << (i + 1));
@ -481,11 +432,11 @@ static int usb_hub_broadcast_packet(USBHubState *s, USBPacket *p)
USBDevice *dev; USBDevice *dev;
int i, ret; int i, ret;
for(i = 0; i < s->nb_ports; i++) { for(i = 0; i < NUM_PORTS; i++) {
port = &s->ports[i]; port = &s->ports[i];
dev = port->port.dev; dev = port->port.dev;
if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) { if (dev && (port->wPortStatus & PORT_STAT_ENABLE)) {
ret = dev->info->handle_packet(dev, p); ret = usb_handle_packet(dev, p);
if (ret != USB_RET_NODEV) { if (ret != USB_RET_NODEV) {
return ret; return ret;
} }
@ -518,44 +469,79 @@ static void usb_hub_handle_destroy(USBDevice *dev)
USBHubState *s = (USBHubState *)dev; USBHubState *s = (USBHubState *)dev;
int i; int i;
//for (i = 0; i < s->nb_ports; i++) { for (i = 0; i < NUM_PORTS; i++) {
// usb_unregister_port(usb_bus_from_device(dev), usb_unregister_port(usb_bus_from_device(dev),
// &s->ports[i].port); &s->ports[i].port);
//} }
} }
static USBPortOps usb_hub_port_ops = {
.attach = usb_hub_attach,
.detach = usb_hub_detach,
.child_detach = usb_hub_child_detach,
.wakeup = usb_hub_wakeup,
.complete = usb_hub_complete,
};
static int usb_hub_initfn(USBDevice *dev) static int usb_hub_initfn(USBDevice *dev)
{ {
USBHubState *s = (USBHubState*)dev; USBHubState *s = DO_UPCAST(USBHubState, dev, dev);
USBHubPort *port; USBHubPort *port;
int i; int i;
s->dev.speed = USB_SPEED_FULL, usb_desc_init(dev);
s->nb_ports = MAX_PORTS; /* FIXME: make configurable */ for (i = 0; i < NUM_PORTS; i++) {
for (i = 0; i < s->nb_ports; i++) {
port = &s->ports[i]; port = &s->ports[i];
//usb_register_port(usb_bus_from_device(dev), usb_register_port(usb_bus_from_device(dev),
// &port->port, s, i, usb_hub_attach); &port->port, s, i, &usb_hub_port_ops,
USB_SPEED_MASK_LOW | USB_SPEED_MASK_FULL);
port->wPortStatus = PORT_STAT_POWER; port->wPortStatus = PORT_STAT_POWER;
port->wPortChange = 0; port->wPortChange = 0;
} }
return 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 = { static struct USBDeviceInfo hub_info = {
usb_hub_initfn, .product_desc = "QEMU USB Hub",
usb_hub_handle_packet, .qdev.name = "usb-hub",
usb_hub_handle_destroy, .qdev.fw_name = "hub",
usb_hub_handle_reset, .qdev.size = sizeof(USBHubState),
usb_hub_handle_control, .qdev.vmsd = &vmstate_usb_hub,
usb_hub_handle_data, .usb_desc = &desc_hub,
.init = usb_hub_initfn,
"QEMU USB Hub", .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) 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)

View File

@ -697,8 +697,8 @@ static void usb_keyboard_handle_reset(USBDevice *dev)
USBKeyboardState *s = (USBKeyboardState *)dev; USBKeyboardState *s = (USBKeyboardState *)dev;
} }
static int usb_keyboard_handle_control(USBDevice *dev, int request, int value, static int usb_keyboard_handle_control(USBDevice *dev, USBPacket *p, int request, int value,
int index, int length, uint8_t *data) int index, int length, uint8_t *data)
{ {
USBKeyboardState *s = (USBKeyboardState *)dev; USBKeyboardState *s = (USBKeyboardState *)dev;
int ret = 0; int ret = 0;

View File

@ -4,17 +4,19 @@
* Copyright (c) 2006 CodeSourcery. * Copyright (c) 2006 CodeSourcery.
* Written by Paul Brook * Written by Paul Brook
* *
* This code is licenced under the LGPL. * This code is licensed under the LGPL.
*/ */
#include "qemu-common.h" #include "qemu-common.h"
#include "qemu-option.h" #include "qemu-option.h"
#include "qemu-config.h" #include "qemu-config.h"
#include "usb.h" #include "usb.h"
#include "block.h" #include "usb-desc.h"
#include "scsi.h" #include "scsi.h"
#include "console.h" #include "console.h"
#include "monitor.h" #include "monitor.h"
#include "sysemu.h"
#include "blockdev.h"
//#define DEBUG_MSD //#define DEBUG_MSD
@ -31,7 +33,7 @@ do { printf("usb-msd: " fmt , ## __VA_ARGS__); } while (0)
enum USBMSDMode { enum USBMSDMode {
USB_MSDM_CBW, /* Command Block. */ 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_DATAIN, /* Transfer data from device. */
USB_MSDM_CSW /* Command Status. */ USB_MSDM_CSW /* Command Status. */
}; };
@ -46,9 +48,12 @@ typedef struct {
uint32_t data_len; uint32_t data_len;
uint32_t residue; uint32_t residue;
uint32_t tag; uint32_t tag;
SCSIRequest *req;
SCSIBus bus; SCSIBus bus;
DriveInfo *dinfo; BlockConf conf;
char *serial;
SCSIDevice *scsi_dev; SCSIDevice *scsi_dev;
uint32_t removable;
int result; int result;
/* For async completion. */ /* For async completion. */
USBPacket *packet; USBPacket *packet;
@ -71,69 +76,104 @@ struct usb_msd_csw {
uint8_t status; uint8_t status;
}; };
static const uint8_t qemu_msd_dev_descriptor[] = { enum {
0x12, /* u8 bLength; */ STR_MANUFACTURER = 1,
0x01, /* u8 bDescriptorType; Device */ STR_PRODUCT,
0x00, 0x01, /* u16 bcdUSB; v1.0 */ STR_SERIALNUMBER,
STR_CONFIG_FULL,
0x00, /* u8 bDeviceClass; */ STR_CONFIG_HIGH,
0x00, /* u8 bDeviceSubClass; */
0x00, /* u8 bDeviceProtocol; [ low/full speeds only ] */
0x08, /* u8 bMaxPacketSize0; 8 Bytes */
/* Vendor and product id are arbitrary. */
0x00, 0x00, /* u16 idVendor; */
0x00, 0x00, /* u16 idProduct; */
0x00, 0x00, /* u16 bcdDevice */
0x01, /* u8 iManufacturer; */
0x02, /* u8 iProduct; */
0x03, /* u8 iSerialNumber; */
0x01 /* u8 bNumConfigurations; */
}; };
static const uint8_t qemu_msd_config_descriptor[] = { static const USBDescStrings desc_strings = {
[STR_MANUFACTURER] = "QEMU " QEMU_VERSION,
[STR_PRODUCT] = "QEMU USB HARDDRIVE",
[STR_SERIALNUMBER] = "1",
[STR_CONFIG_FULL] = "Full speed config (usb 1.1)",
[STR_CONFIG_HIGH] = "High speed config (usb 2.0)",
};
/* one configuration */ static const USBDescIface desc_iface_full = {
0x09, /* u8 bLength; */ .bInterfaceNumber = 0,
0x02, /* u8 bDescriptorType; Configuration */ .bNumEndpoints = 2,
0x20, 0x00, /* u16 wTotalLength; */ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
0x01, /* u8 bNumInterfaces; (1) */ .bInterfaceSubClass = 0x06, /* SCSI */
0x01, /* u8 bConfigurationValue; */ .bInterfaceProtocol = 0x50, /* Bulk */
0x00, /* u8 iConfiguration; */ .eps = (USBDescEndpoint[]) {
0xc0, /* u8 bmAttributes; {
Bit 7: must be set, .bEndpointAddress = USB_DIR_IN | 0x01,
6: Self-powered, .bmAttributes = USB_ENDPOINT_XFER_BULK,
5: Remote wakeup, .wMaxPacketSize = 64,
4..0: resvd */ },{
0x00, /* u8 MaxPower; */ .bEndpointAddress = USB_DIR_OUT | 0x02,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 64,
},
}
};
/* one interface */ static const USBDescDevice desc_device_full = {
0x09, /* u8 if_bLength; */ .bcdUSB = 0x0200,
0x04, /* u8 if_bDescriptorType; Interface */ .bMaxPacketSize0 = 8,
0x00, /* u8 if_bInterfaceNumber; */ .bNumConfigurations = 1,
0x00, /* u8 if_bAlternateSetting; */ .confs = (USBDescConfig[]) {
0x02, /* u8 if_bNumEndpoints; */ {
0x08, /* u8 if_bInterfaceClass; MASS STORAGE */ .bNumInterfaces = 1,
0x06, /* u8 if_bInterfaceSubClass; SCSI */ .bConfigurationValue = 1,
0x50, /* u8 if_bInterfaceProtocol; Bulk Only */ .iConfiguration = STR_CONFIG_FULL,
0x00, /* u8 if_iInterface; */ .bmAttributes = 0xc0,
.nif = 1,
.ifs = &desc_iface_full,
},
},
};
/* Bulk-In endpoint */ static const USBDescIface desc_iface_high = {
0x07, /* u8 ep_bLength; */ .bInterfaceNumber = 0,
0x05, /* u8 ep_bDescriptorType; Endpoint */ .bNumEndpoints = 2,
0x81, /* u8 ep_bEndpointAddress; IN Endpoint 1 */ .bInterfaceClass = USB_CLASS_MASS_STORAGE,
0x02, /* u8 ep_bmAttributes; Bulk */ .bInterfaceSubClass = 0x06, /* SCSI */
0x40, 0x00, /* u16 ep_wMaxPacketSize; */ .bInterfaceProtocol = 0x50, /* Bulk */
0x00, /* u8 ep_bInterval; */ .eps = (USBDescEndpoint[]) {
{
.bEndpointAddress = USB_DIR_IN | 0x01,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
},{
.bEndpointAddress = USB_DIR_OUT | 0x02,
.bmAttributes = USB_ENDPOINT_XFER_BULK,
.wMaxPacketSize = 512,
},
}
};
/* Bulk-Out endpoint */ static const USBDescDevice desc_device_high = {
0x07, /* u8 ep_bLength; */ .bcdUSB = 0x0200,
0x05, /* u8 ep_bDescriptorType; Endpoint */ .bMaxPacketSize0 = 64,
0x02, /* u8 ep_bEndpointAddress; OUT Endpoint 2 */ .bNumConfigurations = 1,
0x02, /* u8 ep_bmAttributes; Bulk */ .confs = (USBDescConfig[]) {
0x40, 0x00, /* u16 ep_wMaxPacketSize; */ {
0x00 /* u8 ep_bInterval; */ .bNumInterfaces = 1,
.bConfigurationValue = 1,
.iConfiguration = STR_CONFIG_HIGH,
.bmAttributes = 0xc0,
.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) 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->usb_buf += len;
s->scsi_buf += len; s->scsi_buf += len;
s->data_len -= len; s->data_len -= len;
if (s->scsi_len == 0) { if (s->scsi_len == 0 || s->data_len == 0) {
if (s->mode == USB_MSDM_DATAIN) { scsi_req_continue(s->req);
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);
}
} }
} }
static void usb_msd_send_status(MSDState *s) static void usb_msd_send_status(MSDState *s, USBPacket *p)
{ {
struct usb_msd_csw csw; struct usb_msd_csw csw;
int len;
csw.sig = cpu_to_le32(0x53425355); csw.sig = cpu_to_le32(0x53425355);
csw.tag = cpu_to_le32(s->tag); csw.tag = cpu_to_le32(s->tag);
csw.residue = s->residue; csw.residue = s->residue;
csw.status = s->result; csw.status = s->result;
memcpy(s->usb_buf, &csw, 13);
len = MIN(sizeof(csw), p->len);
memcpy(p->data, &csw, len);
} }
static void usb_msd_command_complete(SCSIBus *bus, int reason, uint32_t tag, static void usb_msd_transfer_data(SCSIRequest *req, uint32_t len)
uint32_t arg)
{ {
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; USBPacket *p = s->packet;
if (tag != s->tag) { assert((s->mode == USB_MSDM_DATAOUT) == (req->cmd.mode == SCSI_XFER_TO_DEV));
fprintf(stderr, "usb-msd: Unexpected SCSI Tag 0x%x\n", tag); s->scsi_len = len;
} s->scsi_buf = scsi_req_get_buf(req);
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);
if (p) { if (p) {
usb_msd_copy_data(s); 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 /* 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. */ usb_packet_complete returns. */
DPRINTF("Packet complete %p\n", p); DPRINTF("Packet complete %p\n", p);
s->packet = NULL; 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) static void usb_msd_handle_reset(USBDevice *dev)
{ {
MSDState *s = (MSDState *)dev; MSDState *s = (MSDState *)dev;
@ -231,88 +287,19 @@ static void usb_msd_handle_reset(USBDevice *dev)
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
} }
static int usb_msd_handle_control(USBDevice *dev, int request, int value, static int usb_msd_handle_control(USBDevice *dev, USBPacket *p,
int index, int length, uint8_t *data) int request, int value, int index, int length, uint8_t *data)
{ {
MSDState *s = (MSDState *)dev; MSDState *s = (MSDState *)dev;
int ret = 0; int ret;
ret = usb_desc_handle_control(dev, p, request, value, index, length, data);
if (ret >= 0) {
return ret;
}
ret = 0;
switch (request) { switch (request) {
case DeviceRequest | USB_REQ_GET_STATUS:
data[0] = (1 << USB_DEVICE_SELF_POWERED) |
(dev->remote_wakeup << USB_DEVICE_REMOTE_WAKEUP);
data[1] = 0x00;
ret = 2;
break;
case DeviceOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 0;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_FEATURE:
if (value == USB_DEVICE_REMOTE_WAKEUP) {
dev->remote_wakeup = 1;
} else {
goto fail;
}
ret = 0;
break;
case DeviceOutRequest | USB_REQ_SET_ADDRESS:
dev->addr = value;
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_DESCRIPTOR:
switch(value >> 8) {
case USB_DT_DEVICE:
memcpy(data, qemu_msd_dev_descriptor,
sizeof(qemu_msd_dev_descriptor));
ret = sizeof(qemu_msd_dev_descriptor);
break;
case USB_DT_CONFIG:
memcpy(data, qemu_msd_config_descriptor,
sizeof(qemu_msd_config_descriptor));
ret = sizeof(qemu_msd_config_descriptor);
break;
case USB_DT_STRING:
switch(value & 0xff) {
case 0:
/* language ids */
data[0] = 4;
data[1] = 3;
data[2] = 0x09;
data[3] = 0x04;
ret = 4;
break;
case 1:
/* vendor description */
ret = set_usb_string(data, "QEMU " QEMU_VERSION);
break;
case 2:
/* product description */
ret = set_usb_string(data, "QEMU USB HARDDRIVE");
break;
case 3:
/* serial number */
ret = set_usb_string(data, "1");
break;
default:
goto fail;
}
break;
default:
goto fail;
}
break;
case DeviceRequest | USB_REQ_GET_CONFIGURATION:
data[0] = 1;
ret = 1;
break;
case DeviceOutRequest | USB_REQ_SET_CONFIGURATION:
ret = 0;
break;
case DeviceRequest | USB_REQ_GET_INTERFACE: case DeviceRequest | USB_REQ_GET_INTERFACE:
data[0] = 0; data[0] = 0;
ret = 1; ret = 1;
@ -321,35 +308,32 @@ static int usb_msd_handle_control(USBDevice *dev, int request, int value,
ret = 0; ret = 0;
break; break;
case EndpointOutRequest | USB_REQ_CLEAR_FEATURE: case EndpointOutRequest | USB_REQ_CLEAR_FEATURE:
if (value == 0 && index != 0x81) { /* clear ep halt */ ret = 0;
goto fail; break;
} case InterfaceOutRequest | USB_REQ_SET_INTERFACE:
ret = 0; ret = 0;
break; break;
/* Class specific requests. */ /* Class specific requests. */
case MassStorageReset: case ClassInterfaceOutRequest | MassStorageReset:
/* Reset state ready for the next CBW. */ /* Reset state ready for the next CBW. */
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
ret = 0; ret = 0;
break; break;
case GetMaxLun: case ClassInterfaceRequest | GetMaxLun:
data[0] = 0; data[0] = 0;
ret = 1; ret = 1;
break; break;
default: default:
fail:
ret = USB_RET_STALL; ret = USB_RET_STALL;
break; break;
} }
return ret; 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; MSDState *s = DO_UPCAST(MSDState, dev, dev);
s->scsi_dev->info->cancel_io(s->scsi_dev, s->tag); scsi_req_cancel(s->req);
s->packet = NULL;
s->scsi_len = 0;
} }
static int usb_msd_handle_data(USBDevice *dev, USBPacket *p) 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", DPRINTF("Command tag 0x%x flags %08x len %d data %d\n",
s->tag, cbw.flags, cbw.cmd_len, s->data_len); s->tag, cbw.flags, cbw.cmd_len, s->data_len);
s->residue = 0; 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 /* ??? Should check that USB and SCSI data transfer
directions match. */ directions match. */
if (s->residue == 0) { if (s->mode != USB_MSDM_CSW && s->residue == 0) {
if (s->mode == USB_MSDM_DATAIN) { scsi_req_continue(s->req);
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);
}
} }
ret = len; ret = len;
break; break;
@ -426,7 +408,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
} }
if (s->usb_len) { if (s->usb_len) {
DPRINTF("Deferring packet %p\n", p); DPRINTF("Deferring packet %p\n", p);
usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; ret = USB_RET_ASYNC;
} else { } else {
@ -449,7 +430,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (s->data_len != 0 || len < 13) if (s->data_len != 0 || len < 13)
goto fail; goto fail;
/* Waiting for SCSI write to complete. */ /* Waiting for SCSI write to complete. */
usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; ret = USB_RET_ASYNC;
break; break;
@ -460,15 +440,13 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
if (len < 13) if (len < 13)
goto fail; goto fail;
s->usb_len = len; usb_msd_send_status(s, p);
s->usb_buf = data;
usb_msd_send_status(s);
s->mode = USB_MSDM_CBW; s->mode = USB_MSDM_CBW;
ret = 13; ret = 13;
break; break;
case USB_MSDM_DATAIN: case USB_MSDM_DATAIN:
DPRINTF("Data in %d/%d\n", len, s->data_len); DPRINTF("Data in %d/%d, scsi_len %d\n", len, s->data_len, s->scsi_len);
if (len > s->data_len) if (len > s->data_len)
len = s->data_len; len = s->data_len;
s->usb_buf = data; s->usb_buf = data;
@ -485,7 +463,6 @@ static int usb_msd_handle_data(USBDevice *dev, USBPacket *p)
} }
if (s->usb_len) { if (s->usb_len) {
DPRINTF("Deferring packet %p\n", p); DPRINTF("Deferring packet %p\n", p);
usb_defer_packet(p, usb_msd_cancel_io, s);
s->packet = p; s->packet = p;
ret = USB_RET_ASYNC; ret = USB_RET_ASYNC;
} else { } else {
@ -514,36 +491,71 @@ static void usb_msd_password_cb(void *opaque, int err)
MSDState *s = opaque; MSDState *s = opaque;
if (!err) if (!err)
usb_device_attach(&s->dev); err = usb_device_attach(&s->dev);
else
if (err)
qdev_unplug(&s->dev.qdev); 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) static int usb_msd_initfn(USBDevice *dev)
{ {
MSDState *s = DO_UPCAST(MSDState, dev, dev); MSDState *s = DO_UPCAST(MSDState, dev, dev);
BlockDriverState *bs = s->conf.bs;
DriveInfo *dinfo;
if (!s->dinfo || !s->dinfo->bdrv) { if (!bs) {
qemu_error("usb-msd: drive property not set\n"); error_report("usb-msd: drive property not set");
return -1; return -1;
} }
s->dev.speed = USB_SPEED_FULL; /*
scsi_bus_new(&s->bus, &s->dev.qdev, 0, 1, usb_msd_command_complete); * Hack alert: this pretends to be a block device, but it's really
s->scsi_dev = scsi_bus_legacy_add_drive(&s->bus, s->dinfo, 0); * 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; s->bus.qbus.allow_hotplug = 0;
usb_msd_handle_reset(dev); usb_msd_handle_reset(dev);
if (bdrv_key_required(s->dinfo->bdrv)) { if (bdrv_key_required(bs)) {
if (s->dev.qdev.hotplugged) { if (cur_mon) {
monitor_read_bdrv_key_start(cur_mon, s->dinfo->bdrv, monitor_read_bdrv_key_start(cur_mon, bs, usb_msd_password_cb, s);
usb_msd_password_cb, s);
s->dev.auto_attach = 0; s->dev.auto_attach = 0;
} else { } else {
autostart = 0; autostart = 0;
} }
} }
add_boot_device_path(s->conf.bootindex, &dev->qdev, "/disk@0,0");
return 0; return 0;
} }
@ -554,13 +566,12 @@ static USBDevice *usb_msd_init(const char *filename)
QemuOpts *opts; QemuOpts *opts;
DriveInfo *dinfo; DriveInfo *dinfo;
USBDevice *dev; USBDevice *dev;
int fatal_error;
const char *p1; const char *p1;
char fmt[32]; char fmt[32];
/* parse -usbdevice disk: syntax into drive opts */ /* parse -usbdevice disk: syntax into drive opts */
snprintf(id, sizeof(id), "usb%d", nr++); 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, ':'); p1 = strchr(filename, ':');
if (p1++) { if (p1++) {
@ -584,7 +595,7 @@ static USBDevice *usb_msd_init(const char *filename)
qemu_opt_set(opts, "if", "none"); qemu_opt_set(opts, "if", "none");
/* create host drive */ /* create host drive */
dinfo = drive_init(opts, NULL, &fatal_error); dinfo = drive_init(opts, 0);
if (!dinfo) { if (!dinfo) {
qemu_opts_del(opts); qemu_opts_del(opts);
return NULL; return NULL;
@ -595,7 +606,10 @@ static USBDevice *usb_msd_init(const char *filename)
if (!dev) { if (!dev) {
return NULL; 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) if (qdev_init(&dev->qdev) < 0)
return NULL; return NULL;
@ -605,16 +619,22 @@ static USBDevice *usb_msd_init(const char *filename)
static struct USBDeviceInfo msd_info = { static struct USBDeviceInfo msd_info = {
.product_desc = "QEMU USB MSD", .product_desc = "QEMU USB MSD",
.qdev.name = "usb-storage", .qdev.name = "usb-storage",
.qdev.fw_name = "storage",
.qdev.size = sizeof(MSDState), .qdev.size = sizeof(MSDState),
.usb_desc = &desc,
.init = usb_msd_initfn, .init = usb_msd_initfn,
.handle_packet = usb_generic_handle_packet, .handle_packet = usb_generic_handle_packet,
.cancel_packet = usb_msd_cancel_io,
.handle_attach = usb_desc_attach,
.handle_reset = usb_msd_handle_reset, .handle_reset = usb_msd_handle_reset,
.handle_control = usb_msd_handle_control, .handle_control = usb_msd_handle_control,
.handle_data = usb_msd_handle_data, .handle_data = usb_msd_handle_data,
.usbdevice_name = "disk", .usbdevice_name = "disk",
.usbdevice_init = usb_msd_init, .usbdevice_init = usb_msd_init,
.qdev.props = (Property[]) { .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(), DEFINE_PROP_END_OF_LIST(),
}, },
}; };

View File

@ -58,38 +58,23 @@ int dprintf(const char *fmt,...)
#endif #endif
} }
static int last_level = 0;
/* Update IRQ levels */ /* Update IRQ levels */
static inline void ohci_intr_update(OHCIState *ohci) 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)
{ {
/* if( (get_clock() - last_cycle) > MIN_IRQ_INTERVAL)
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) USBirq(1);
{ last_cycle = get_clock();
USBirq(1);
last_cycle = get_clock();
}
//dprintf("usb-ohci: Interrupt Called. Reason(s): %s\n",reasons);
} }
} }
} }
@ -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. */ /* 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]; OHCIPort *port = &s->rhport[port1->index];
uint32_t old_state = port->ctrl; uint32_t old_state = port->ctrl;
if (dev) { /* set connect status */
if (port->port.dev) { port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
usb_attach(port1, NULL);
}
/* set connect status */
port->ctrl |= OHCI_PORT_CCS | OHCI_PORT_CSC;
/* update speed */ /* update speed */
if (dev->speed == USB_SPEED_LOW) if (port->port.dev->speed == USB_SPEED_LOW) {
port->ctrl |= OHCI_PORT_LSDA; port->ctrl |= OHCI_PORT_LSDA;
else
port->ctrl &= ~OHCI_PORT_LSDA;
port->port.dev = dev;
/* notify of remote-wakeup */
if ((s->ctl & OHCI_CTL_HCFS) == OHCI_USB_SUSPEND)
ohci_set_interrupt(s, OHCI_INTR_RD);
/* send the attach message */
usb_send_msg(dev, USB_MSG_ATTACH);
dprintf("usb-ohci: Attached port %d\n", port1->index);
} else { } else {
/* set connect status */ port->ctrl &= ~OHCI_PORT_LSDA;
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);
} }
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); 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 */ /* Reset the controller */
@ -193,8 +220,9 @@ static void ohci_reset(void *opaque)
{ {
port = &ohci->rhport[i]; port = &ohci->rhport[i];
port->ctrl = 0; port->ctrl = 0;
if (port->port.dev) if (port->port.dev) {
ohci_attach(&port->port, port->port.dev); usb_attach(&port->port, port->port.dev);
}
} }
if (ohci->async_td) { if (ohci->async_td) {
usb_cancel_packet(&ohci->usb_packet); usb_cancel_packet(&ohci->usb_packet);
@ -212,7 +240,7 @@ static inline int get_dwords(OHCIState *ohci,
addr += ohci->localmem_base; addr += ohci->localmem_base;
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { 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); *buf = (*buf);
} }
@ -229,7 +257,7 @@ static inline int put_dwords(OHCIState *ohci,
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
uint32_t tmp = (*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; return 1;
@ -244,7 +272,7 @@ static inline int get_words(OHCIState *ohci,
addr += ohci->localmem_base; addr += ohci->localmem_base;
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { 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); *buf = (*buf);
} }
@ -261,7 +289,7 @@ static inline int put_words(OHCIState *ohci,
for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) { for (i = 0; i < num; i++, buf++, addr += sizeof(*buf)) {
uint16_t tmp = (*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; return 1;
@ -289,8 +317,7 @@ static inline int ohci_read_iso_td(OHCIState *ohci,
static inline int ohci_read_hcca(OHCIState *ohci, static inline int ohci_read_hcca(OHCIState *ohci,
uint32_t addr, struct ohci_hcca *hcca) uint32_t addr, struct ohci_hcca *hcca)
{ {
cpu_physical_memory_rw(addr + ohci->localmem_base, cpu_physical_memory_read(addr + ohci->localmem_base, (uint8_t*)hcca, sizeof(*hcca));
(uint8_t *)hcca, sizeof(*hcca), 0);
return 1; return 1;
} }
@ -316,8 +343,7 @@ static inline int ohci_put_iso_td(OHCIState *ohci,
static inline int ohci_put_hcca(OHCIState *ohci, static inline int ohci_put_hcca(OHCIState *ohci,
uint32_t addr, struct ohci_hcca *hcca) uint32_t addr, struct ohci_hcca *hcca)
{ {
cpu_physical_memory_rw(addr + ohci->localmem_base, cpu_physical_memory_write(addr + ohci->localmem_base, (uint8_t*)hcca, sizeof(*hcca));
(uint8_t *)hcca, sizeof(*hcca), 1);
return 1; 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_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 #ifdef DEBUG_PACKET
dprintf("Async packet complete\n"); dprintf("Async packet complete\n");
#endif #endif
@ -379,7 +405,9 @@ static int ohci_service_iso_td(OHCIState *ohci, struct ohci_ed *ed,
{ {
int dir; int dir;
size_t len = 0; size_t len = 0;
#ifdef DEBUG_ISOCH
const char *str = NULL; const char *str = NULL;
#endif
int pid; int pid;
int ret; int ret;
int i; 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); dir = OHCI_BM(ed->flags, ED_D);
switch (dir) { switch (dir) {
case OHCI_TD_DIR_IN: case OHCI_TD_DIR_IN:
#ifdef DEBUG_ISOCH
str = "in"; str = "in";
#endif
pid = USB_TOKEN_IN; pid = USB_TOKEN_IN;
break; break;
case OHCI_TD_DIR_OUT: case OHCI_TD_DIR_OUT:
#ifdef DEBUG_ISOCH
str = "out"; str = "out";
#endif
pid = USB_TOKEN_OUT; pid = USB_TOKEN_OUT;
break; break;
case OHCI_TD_DIR_SETUP: case OHCI_TD_DIR_SETUP:
#ifdef DEBUG_ISOCH
str = "setup"; str = "setup";
#endif
pid = USB_TOKEN_SETUP; pid = USB_TOKEN_SETUP;
break; break;
default: 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.devep = OHCI_BM(ed->flags, ED_EN);
ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.data = ohci->usb_buf;
ohci->usb_packet.len = len; ohci->usb_packet.len = len;
ohci->usb_packet.complete_cb = ohci_async_complete_packet; ret = usb_handle_packet(dev, &ohci->usb_packet);
ohci->usb_packet.complete_opaque = ohci;
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV) if (ret != USB_RET_NODEV)
break; break;
} }
@ -614,7 +646,9 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
{ {
int dir; int dir;
size_t len = 0; size_t len = 0;
#ifdef DEBUG_PACKET
const char *str = NULL; const char *str = NULL;
#endif
int pid; int pid;
int ret; int ret;
int i; int i;
@ -651,15 +685,21 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
switch (dir) { switch (dir) {
case OHCI_TD_DIR_IN: case OHCI_TD_DIR_IN:
#ifdef DEBUG_PACKET
str = "in"; str = "in";
#endif
pid = USB_TOKEN_IN; pid = USB_TOKEN_IN;
break; break;
case OHCI_TD_DIR_OUT: case OHCI_TD_DIR_OUT:
#ifdef DEBUG_PACKET
str = "out"; str = "out";
#endif
pid = USB_TOKEN_OUT; pid = USB_TOKEN_OUT;
break; break;
case OHCI_TD_DIR_SETUP: case OHCI_TD_DIR_SETUP:
#ifdef DEBUG_PACKET
str = "setup"; str = "setup";
#endif
pid = USB_TOKEN_SETUP; pid = USB_TOKEN_SETUP;
break; break;
default: default:
@ -681,7 +721,7 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
flag_r = (td.flags & OHCI_TD_R) != 0; flag_r = (td.flags & OHCI_TD_R) != 0;
#ifdef DEBUG_PACKET #ifdef DEBUG_PACKET
dprintf(" TD @ 0x%.8x %" PRId64 " bytes %s r=%d cbp=0x%.8x be=0x%.8x\n", 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) { if (len > 0 && dir != OHCI_TD_DIR_IN) {
dprintf(" data:"); 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.devep = OHCI_BM(ed->flags, ED_EN);
ohci->usb_packet.data = ohci->usb_buf; ohci->usb_packet.data = ohci->usb_buf;
ohci->usb_packet.len = len; ohci->usb_packet.len = len;
ohci->usb_packet.complete_cb = ohci_async_complete_packet; ret = usb_handle_packet(dev, &ohci->usb_packet);
ohci->usb_packet.complete_opaque = ohci;
ret = dev->info->handle_packet(dev, &ohci->usb_packet);
if (ret != USB_RET_NODEV) if (ret != USB_RET_NODEV)
break; break;
} }
@ -773,7 +811,6 @@ static int ohci_service_td(OHCIState *ohci, struct ohci_ed *ed)
switch (ret) { switch (ret) {
case USB_RET_NODEV: case USB_RET_NODEV:
OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING); OHCI_SET_BM(td.flags, TD_CC, OHCI_CC_DEVICENOTRESPONDING);
return 1;
case USB_RET_NAK: case USB_RET_NAK:
dprintf("usb-ohci: got NAK\n"); dprintf("usb-ohci: got NAK\n");
return 1; return 1;
@ -873,6 +910,7 @@ static void ohci_sof(OHCIState *ohci)
{ {
ohci->sof_time = get_clock(); ohci->sof_time = get_clock();
ohci->eof_timer += usb_frame_time; 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); //qemu_mod_timer(ohci->eof_timer, ohci->sof_time + usb_frame_time);
ohci_set_interrupt(ohci, OHCI_INTR_SF); 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) static void ohci_process_lists(OHCIState *ohci, int completion)
{ {
if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) { if ((ohci->ctl & OHCI_CTL_CLE) && (ohci->status & OHCI_STATUS_CLF)) {
if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) if (ohci->ctrl_cur && ohci->ctrl_cur != ohci->ctrl_head) {
dprintf("usb-ohci: head %x, cur %x\n", dprintf("usb-ohci: head %x, cur %x\n",
ohci->ctrl_head, ohci->ctrl_cur); ohci->ctrl_head, ohci->ctrl_cur);
}
if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) { if (!ohci_service_ed_list(ohci, ohci->ctrl_head, completion)) {
ohci->ctrl_cur = 0; ohci->ctrl_cur = 0;
ohci->status &= ~OHCI_STATUS_CLF; ohci->status &= ~OHCI_STATUS_CLF;
@ -901,7 +940,7 @@ static void ohci_process_lists(OHCIState *ohci, int completion)
/* Do frame processing on frame boundary */ /* Do frame processing on frame boundary */
void ohci_frame_boundary(void *opaque) void ohci_frame_boundary(void *opaque)
{ {
OHCIState *ohci = (OHCIState *)opaque; OHCIState *ohci = (OHCIState*)opaque;
struct ohci_hcca hcca; struct ohci_hcca hcca;
ohci_read_hcca(ohci, 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 /* Start sending SOF tokens across the USB bus, lists are processed in
* next frame * next frame
*/ */
int ohci_bus_start(OHCIState *ohci) static int ohci_bus_start(OHCIState *ohci)
{ {
ohci->eof_timer = 0; ohci->eof_timer = 0;
@ -966,7 +1005,7 @@ int ohci_bus_start(OHCIState *ohci)
} }
/* Stop sending SOF tokens on the bus */ /* Stop sending SOF tokens on the bus */
void ohci_bus_stop(OHCIState *ohci) static void ohci_bus_stop(OHCIState *ohci)
{ {
if (ohci->eof_timer) if (ohci->eof_timer)
ohci->eof_timer=0; 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 /* Being in USB operational state guarnatees sof_time was
* set already. * set already.
*/ */
tks = get_clock() - ohci->sof_time; tks = get_clock() - ohci->sof_time;
/* avoid muldiv if possible */ /* avoid muldiv if possible */
if (tks >= usb_frame_time) 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); 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); dprintf("usb-ohci: port %d: SUSPEND\n", portnum);
}
if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) { if (ohci_port_set_if_connected(ohci, portnum, val & OHCI_PORT_PRS)) {
dprintf("usb-ohci: port %d: RESET\n", portnum); 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) uint32_t ohci_mem_read(void *ptr, target_phys_addr_t addr)
{ {
OHCIState *ohci = (OHCIState *)ptr; OHCIState *ohci = (OHCIState*)ptr;
uint32_t retval; uint32_t retval;
addr &= 0xff;
/* Only aligned reads are allowed on OHCI */ /* Only aligned reads are allowed on OHCI */
if (addr & 3) { if (addr & 3) {
fprintf(stderr, "usb-ohci: Mis-aligned read\n"); 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; return retval;
} }
@ -1293,9 +1332,7 @@ void ohci_mem_write(void *ptr, target_phys_addr_t addr, uint32_t val)
{ {
OHCIState *ohci = (OHCIState *)ptr; OHCIState *ohci = (OHCIState *)ptr;
#ifdef TARGET_WORDS_BIGENDIAN addr &= 0xff;
val = bswap32(val);
#endif
/* Only aligned reads are allowed on OHCI */ /* Only aligned reads are allowed on OHCI */
if (addr & 3) { 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; ohci->hcca = val & OHCI_HCCA_MASK;
break; break;
case 7: /* HcPeriodCurrentED */
/* Ignore writes to this read-only register, Linux does them */
break;
case 8: /* HcControlHeadED */ case 8: /* HcControlHeadED */
ohci->ctrl_head = val & OHCI_EDPTR_MASK; ohci->ctrl_head = val & OHCI_EDPTR_MASK;
break; 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_create(uint32_t base, int ports)
{ {
OHCIState *ohci=(OHCIState*)malloc(sizeof(OHCIState)); OHCIState *ohci=(OHCIState*)malloc(sizeof(OHCIState));
@ -1443,15 +1495,15 @@ OHCIState *ohci_create(uint32_t base, int ports)
usb_bit_time = 1; usb_bit_time = 1;
} }
#endif #endif
dprintf("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n", dprintf("usb-ohci: usb_bit_time=%" PRId64 " usb_frame_time=%" PRId64 "\n",
usb_frame_time, usb_bit_time); usb_frame_time, usb_bit_time);
} }
ohci->num_ports = ports; ohci->num_ports = ports;
for (i = 0; i < ports; i++) { for (i = 0; i < ports; i++) {
ohci->rhport[i].port.opaque = ohci; ohci->rhport[i].port.opaque = ohci;
ohci->rhport[i].port.index = i; 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; ohci->async_td = 0;

View File

@ -1,259 +1,382 @@
/* /*
* QEMU USB API * QEMU USB API
* *
* Copyright (c) 2005 Fabrice Bellard * Copyright (c) 2005 Fabrice Bellard
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights * in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is * copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions: * furnished to do so, subject to the following conditions:
* *
* The above copyright notice and this permission notice shall be included in * The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software. * all copies or substantial portions of the Software.
* *
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * 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, * 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 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE. * THE SOFTWARE.
*/ */
#include "qemu-queue.h" #include "qemu-queue.h"
#define USB_TOKEN_SETUP 0x2d /* Constants related to the USB / PCI interaction */
#define USB_TOKEN_IN 0x69 /* device -> host */ #define USB_SBRN 0x60 /* Serial Bus Release Number Register */
#define USB_TOKEN_OUT 0xe1 /* host -> device */ #define USB_RELEASE_1 0x10 /* USB 1.0 */
#define USB_RELEASE_2 0x20 /* USB 2.0 */
/* specific usb messages, also sent in the 'pid' parameter */ #define USB_RELEASE_3 0x30 /* USB 3.0 */
#define USB_MSG_ATTACH 0x100
#define USB_MSG_DETACH 0x101 #define USB_TOKEN_SETUP 0x2d
#define USB_MSG_RESET 0x102 #define USB_TOKEN_IN 0x69 /* device -> host */
#define USB_TOKEN_OUT 0xe1 /* host -> device */
#define USB_RET_NODEV (-1)
#define USB_RET_NAK (-2) /* specific usb messages, also sent in the 'pid' parameter */
#define USB_RET_STALL (-3) #define USB_MSG_ATTACH 0x100
#define USB_RET_BABBLE (-4) #define USB_MSG_DETACH 0x101
#define USB_RET_ASYNC (-5) #define USB_MSG_RESET 0x102
#define USB_SPEED_LOW 0 #define USB_RET_NODEV (-1)
#define USB_SPEED_FULL 1 #define USB_RET_NAK (-2)
#define USB_SPEED_HIGH 2 #define USB_RET_STALL (-3)
#define USB_RET_BABBLE (-4)
#define USB_STATE_NOTATTACHED 0 #define USB_RET_ASYNC (-5)
#define USB_STATE_ATTACHED 1
//#define USB_STATE_POWERED 2 #define USB_SPEED_LOW 0
#define USB_STATE_DEFAULT 3 #define USB_SPEED_FULL 1
//#define USB_STATE_ADDRESS 4 #define USB_SPEED_HIGH 2
//#define USB_STATE_CONFIGURED 5 #define USB_SPEED_SUPER 3
#define USB_STATE_SUSPENDED 6
#define USB_SPEED_MASK_LOW (1 << USB_SPEED_LOW)
#define USB_CLASS_AUDIO 1 #define USB_SPEED_MASK_FULL (1 << USB_SPEED_FULL)
#define USB_CLASS_COMM 2 #define USB_SPEED_MASK_HIGH (1 << USB_SPEED_HIGH)
#define USB_CLASS_HID 3 #define USB_SPEED_MASK_SUPER (1 << USB_SPEED_SUPER)
#define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6 #define USB_STATE_NOTATTACHED 0
#define USB_CLASS_PRINTER 7 #define USB_STATE_ATTACHED 1
#define USB_CLASS_MASS_STORAGE 8 //#define USB_STATE_POWERED 2
#define USB_CLASS_HUB 9 #define USB_STATE_DEFAULT 3
#define USB_CLASS_CDC_DATA 0x0a //#define USB_STATE_ADDRESS 4
#define USB_CLASS_CSCID 0x0b //#define USB_STATE_CONFIGURED 5
#define USB_CLASS_CONTENT_SEC 0x0d #define USB_STATE_SUSPENDED 6
#define USB_CLASS_APP_SPEC 0xfe
#define USB_CLASS_VENDOR_SPEC 0xff #define USB_CLASS_AUDIO 1
#define USB_CLASS_COMM 2
#define USB_DIR_OUT 0 #define USB_CLASS_HID 3
#define USB_DIR_IN 0x80 #define USB_CLASS_PHYSICAL 5
#define USB_CLASS_STILL_IMAGE 6
#define USB_TYPE_MASK (0x03 << 5) #define USB_CLASS_PRINTER 7
#define USB_TYPE_STANDARD (0x00 << 5) #define USB_CLASS_MASS_STORAGE 8
#define USB_TYPE_CLASS (0x01 << 5) #define USB_CLASS_HUB 9
#define USB_TYPE_VENDOR (0x02 << 5) #define USB_CLASS_CDC_DATA 0x0a
#define USB_TYPE_RESERVED (0x03 << 5) #define USB_CLASS_CSCID 0x0b
#define USB_CLASS_CONTENT_SEC 0x0d
#define USB_RECIP_MASK 0x1f #define USB_CLASS_APP_SPEC 0xfe
#define USB_RECIP_DEVICE 0x00 #define USB_CLASS_VENDOR_SPEC 0xff
#define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02 #define USB_DIR_OUT 0
#define USB_RECIP_OTHER 0x03 #define USB_DIR_IN 0x80
#define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) #define USB_TYPE_MASK (0x03 << 5)
#define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8) #define USB_TYPE_STANDARD (0x00 << 5)
#define InterfaceRequest \ #define USB_TYPE_CLASS (0x01 << 5)
((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8) #define USB_TYPE_VENDOR (0x02 << 5)
#define InterfaceOutRequest \ #define USB_TYPE_RESERVED (0x03 << 5)
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define USB_RECIP_MASK 0x1f
#define EndpointOutRequest \ #define USB_RECIP_DEVICE 0x00
((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8) #define USB_RECIP_INTERFACE 0x01
#define USB_RECIP_ENDPOINT 0x02
#define USB_REQ_GET_STATUS 0x00 #define USB_RECIP_OTHER 0x03
#define USB_REQ_CLEAR_FEATURE 0x01
#define USB_REQ_SET_FEATURE 0x03 #define DeviceRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define USB_REQ_SET_ADDRESS 0x05 #define DeviceOutRequest ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_DEVICE)<<8)
#define USB_REQ_GET_DESCRIPTOR 0x06 #define InterfaceRequest \
#define USB_REQ_SET_DESCRIPTOR 0x07 ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define USB_REQ_GET_CONFIGURATION 0x08 #define InterfaceOutRequest \
#define USB_REQ_SET_CONFIGURATION 0x09 ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_INTERFACE)<<8)
#define USB_REQ_GET_INTERFACE 0x0A #define EndpointRequest ((USB_DIR_IN|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
#define USB_REQ_SET_INTERFACE 0x0B #define EndpointOutRequest \
#define USB_REQ_SYNCH_FRAME 0x0C ((USB_DIR_OUT|USB_TYPE_STANDARD|USB_RECIP_ENDPOINT)<<8)
#define ClassInterfaceRequest \
#define USB_DEVICE_SELF_POWERED 0 ((USB_DIR_IN|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
#define USB_DEVICE_REMOTE_WAKEUP 1 #define ClassInterfaceOutRequest \
((USB_DIR_OUT|USB_TYPE_CLASS|USB_RECIP_INTERFACE)<<8)
#define USB_DT_DEVICE 0x01
#define USB_DT_CONFIG 0x02 #define USB_REQ_GET_STATUS 0x00
#define USB_DT_STRING 0x03 #define USB_REQ_CLEAR_FEATURE 0x01
#define USB_DT_INTERFACE 0x04 #define USB_REQ_SET_FEATURE 0x03
#define USB_DT_ENDPOINT 0x05 #define USB_REQ_SET_ADDRESS 0x05
#define USB_REQ_GET_DESCRIPTOR 0x06
#define USB_ENDPOINT_XFER_CONTROL 0 #define USB_REQ_SET_DESCRIPTOR 0x07
#define USB_ENDPOINT_XFER_ISOC 1 #define USB_REQ_GET_CONFIGURATION 0x08
#define USB_ENDPOINT_XFER_BULK 2 #define USB_REQ_SET_CONFIGURATION 0x09
#define USB_ENDPOINT_XFER_INT 3 #define USB_REQ_GET_INTERFACE 0x0A
#define USB_REQ_SET_INTERFACE 0x0B
typedef struct USBBus USBBus; #define USB_REQ_SYNCH_FRAME 0x0C
typedef struct USBPort USBPort;
typedef struct USBDevice USBDevice; #define USB_DEVICE_SELF_POWERED 0
typedef struct USBDeviceInfo USBDeviceInfo; #define USB_DEVICE_REMOTE_WAKEUP 1
typedef struct USBPacket USBPacket;
#define USB_DT_DEVICE 0x01
/* definition of a USB device */ #define USB_DT_CONFIG 0x02
struct USBDevice { #define USB_DT_STRING 0x03
//DeviceState qdev; #define USB_DT_INTERFACE 0x04
USBDeviceInfo *info; #define USB_DT_ENDPOINT 0x05
void *opaque; #define USB_DT_DEVICE_QUALIFIER 0x06
#define USB_DT_OTHER_SPEED_CONFIG 0x07
int speed; #define USB_DT_DEBUG 0x0A
uint8_t addr; #define USB_DT_INTERFACE_ASSOC 0x0B
char product_desc[32];
int auto_attach; #define USB_ENDPOINT_XFER_CONTROL 0
int attached; #define USB_ENDPOINT_XFER_ISOC 1
#define USB_ENDPOINT_XFER_BULK 2
int state; #define USB_ENDPOINT_XFER_INT 3
uint8_t setup_buf[8];
uint8_t data_buf[1024]; typedef struct USBBus USBBus;
int remote_wakeup; typedef struct USBBusOps USBBusOps;
int setup_state; typedef struct USBPort USBPort;
int setup_len; typedef struct USBDevice USBDevice;
int setup_index; typedef struct USBDeviceInfo USBDeviceInfo;
}; typedef struct USBPacket USBPacket;
struct USBDeviceInfo { typedef struct USBDesc USBDesc;
//DeviceInfo qdev; typedef struct USBDescID USBDescID;
int (*init)(USBDevice *dev); typedef struct USBDescDevice USBDescDevice;
typedef struct USBDescConfig USBDescConfig;
/* typedef struct USBDescIfaceAssoc USBDescIfaceAssoc;
* Process USB packet. typedef struct USBDescIface USBDescIface;
* Called by the HC (Host Controller). typedef struct USBDescEndpoint USBDescEndpoint;
* typedef struct USBDescOther USBDescOther;
* Returns length of the transaction typedef struct USBDescString USBDescString;
* or one of the USB_RET_XXX codes.
*/ struct USBDescString {
int (*handle_packet)(USBDevice *dev, USBPacket *p); uint8_t index;
char *str;
/* QLIST_ENTRY(USBDescString) next;
* Called when device is destroyed. };
*/
void (*handle_destroy)(USBDevice *dev); /* definition of a USB device */
struct USBDevice {
/* //DeviceState qdev;
* Reset the device USBDeviceInfo *info;
*/ USBPort *port;
void (*handle_reset)(USBDevice *dev); char *port_path;
void *opaque;
/*
* Process control request. /* Actual connected speed */
* Called from handle_packet(). int speed;
* /* Supported speeds, not in info because it may be variable (hostdevs) */
* Returns length or one of the USB_RET_ codes. int speedmask;
*/ uint8_t addr;
int (*handle_control)(USBDevice *dev, int request, int value, char product_desc[32];
int index, int length, uint8_t *data); int auto_attach;
int attached;
/*
* Process data transfers (both BULK and ISOC). int32_t state;
* Called from handle_packet(). uint8_t setup_buf[8];
* uint8_t data_buf[4096];
* Returns length or one of the USB_RET_ codes. int32_t remote_wakeup;
*/ int32_t setup_state;
int (*handle_data)(USBDevice *dev, USBPacket *p); int32_t setup_len;
int32_t setup_index;
const char *product_desc;
QLIST_HEAD(, USBDescString) strings;
/* handle legacy -usbdevice command line options */ const USBDescDevice *device;
const char *usbdevice_name; const USBDescConfig *config;
USBDevice *(*usbdevice_init)(const char *params); };
};
struct USBDeviceInfo {
typedef void (*usb_attachfn)(USBPort *port, USBDevice *dev); //DeviceInfo qdev;
int (*init)(USBDevice *dev);
/* USB port on which a device can be connected */
struct USBPort { /*
USBDevice *dev; * Process USB packet.
usb_attachfn attach; * Called by the HC (Host Controller).
void *opaque; *
int index; /* internal port index, may be used with the opaque */ * Returns length of the transaction
QTAILQ_ENTRY(USBPort) next; * or one of the USB_RET_XXX codes.
}; */
int (*handle_packet)(USBDevice *dev, USBPacket *p);
typedef void USBCallback(USBPacket * packet, void *opaque);
/*
/* Structure used to hold information about an active USB packet. */ * Called when a packet is canceled.
struct USBPacket { */
/* Data fields for use by the driver. */ void (*cancel_packet)(USBDevice *dev, USBPacket *p);
int pid;
uint8_t devaddr; /*
uint8_t devep; * Called when device is destroyed.
uint8_t *data; */
int len; void (*handle_destroy)(USBDevice *dev);
/* Internal use by the USB layer. */
USBCallback *complete_cb; /*
void *complete_opaque; * Attach the device
USBCallback *cancel_cb; */
void *cancel_opaque; void (*handle_attach)(USBDevice *dev);
};
/*
/* Defer completion of a USB packet. The hadle_packet routine should then * Reset the device
return USB_RET_ASYNC. Packets that complete immediately (before */
handle_packet returns) should not call this method. */ void (*handle_reset)(USBDevice *dev);
static inline void usb_defer_packet(USBPacket *p, USBCallback *cancel,
void * opaque) /*
{ * Process control request.
p->cancel_cb = cancel; * Called from handle_packet().
p->cancel_opaque = opaque; *
} * Returns length or one of the USB_RET_ codes.
*/
/* Notify the controller that an async packet is complete. This should only int (*handle_control)(USBDevice *dev, USBPacket *p, int request, int value,
be called for packets previously deferred with usb_defer_packet, and int index, int length, uint8_t *data);
should never be called from within handle_packet. */
static inline void usb_packet_complete(USBPacket *p) /*
{ * Process data transfers (both BULK and ISOC).
p->complete_cb(p, p->complete_opaque); * Called from handle_packet().
} *
* Returns length or one of the USB_RET_ codes.
/* Cancel an active packet. The packed must have been deferred with */
usb_defer_packet, and not yet completed. */ int (*handle_data)(USBDevice *dev, USBPacket *p);
static inline void usb_cancel_packet(USBPacket * p)
{ const char *product_desc;
p->cancel_cb(p, p->cancel_opaque); const USBDesc *usb_desc;
}
/* handle legacy -usbdevice command line options */
void usb_attach(USBPort *port, USBDevice *dev); const char *usbdevice_name;
int usb_generic_handle_packet(USBDevice *s, USBPacket *p); USBDevice *(*usbdevice_init)(const char *params);
int set_usb_string(uint8_t *buf, const char *str); };
void usb_send_msg(USBDevice *dev, int msg);
typedef struct USBPortOps {
/* usb-hid.c */ void (*attach)(USBPort *port);
void usb_hid_datain_cb(USBDevice *dev, void *opaque, void (*datain)(void *)); void (*detach)(USBPort *port);
/*
/* usb ports of the VM */ * This gets called when a device downstream from the device attached to
* the port (iow attached through a hub) gets detached.
#define VM_USB_HUB_SIZE 2 */
void (*child_detach)(USBPort *port, USBDevice *child);
/* usb-kbd.cpp */ void (*wakeup)(USBPort *port);
USBDevice *usb_keyboard_init(void); /*
* 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);

View File

@ -6,17 +6,6 @@
void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf, void cpu_physical_memory_rw(uint32_t addr, uint8_t *buf,
int len, int is_write); 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 */ /* compute with 96 bit intermediate result: (a*b)/c */
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c) uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t c)
{ {

View File

@ -95,8 +95,16 @@ static inline char *realpath(const char *path, char *resolved_path)
/* vl.c */ /* vl.c */
uint64_t muldiv64(uint64_t a, uint32_t b, uint32_t 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); 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_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); {
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); void *qemu_mallocz(uint32_t size);
#endif /* VL_H */ #endif /* VL_H */