2015-08-04 21:52:48 +00:00
|
|
|
/* USBqemu
|
|
|
|
* Copyright (C) 2002-2011 PCSX2 Team
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "qemu-usb/USBinternal.h"
|
|
|
|
|
|
|
|
#ifdef _MSC_VER
|
|
|
|
# include "svnrev.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const unsigned char version = PS2E_USB_VERSION;
|
|
|
|
const unsigned char revision = 0;
|
|
|
|
const unsigned char build = 1; // increase that with each version
|
|
|
|
|
|
|
|
// PCSX2 expects ASNI, not unicode, so this MUST always be char...
|
|
|
|
static char libraryName[256];
|
|
|
|
|
|
|
|
OHCIState *qemu_ohci;
|
|
|
|
|
|
|
|
Config conf;
|
|
|
|
|
|
|
|
HWND gsWindowHandle=NULL;
|
|
|
|
|
|
|
|
u8 *ram;
|
|
|
|
USBcallback _USBirq;
|
|
|
|
FILE *usbLog;
|
|
|
|
int64_t usb_frame_time=0;
|
|
|
|
int64_t usb_bit_time=0;
|
|
|
|
|
|
|
|
s64 clocks=0;
|
|
|
|
s64 remaining=0;
|
|
|
|
|
|
|
|
int64_t get_ticks_per_sec()
|
|
|
|
{
|
|
|
|
return PSXCLK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void __Log(char *fmt, ...) {
|
|
|
|
va_list list;
|
|
|
|
|
|
|
|
if (!conf.Log) return;
|
|
|
|
|
|
|
|
va_start(list, fmt);
|
|
|
|
vfprintf(usbLog, fmt, list);
|
|
|
|
va_end(list);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void InitLibraryName()
|
|
|
|
{
|
|
|
|
#ifdef USBQEMU_PUBLIC_RELEASE
|
|
|
|
|
|
|
|
// Public Release!
|
|
|
|
// Output a simplified string that's just our name:
|
|
|
|
|
|
|
|
strcpy( libraryName, "USBqemu" );
|
|
|
|
|
|
|
|
#else
|
|
|
|
#ifdef SVN_REV_UNKNOWN
|
|
|
|
|
|
|
|
// Unknown revision.
|
|
|
|
// Output a name that includes devbuild status but not
|
|
|
|
// subversion revision tags:
|
|
|
|
|
|
|
|
strcpy( libraryName, "USBqemu"
|
|
|
|
#ifdef DEBUG_FAST
|
|
|
|
"-Debug"
|
|
|
|
#elif defined( PCSX2_DEBUG )
|
|
|
|
"-Debug/Strict" // strict debugging is slow!
|
|
|
|
#elif defined( PCSX2_DEVBUILD )
|
|
|
|
"-Dev"
|
|
|
|
#else
|
|
|
|
""
|
|
|
|
#endif
|
|
|
|
);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// Use TortoiseSVN's SubWCRev utility's output
|
|
|
|
// to label the specific revision:
|
|
|
|
|
|
|
|
sprintf_s( libraryName, "USBqemu %lld%s"
|
|
|
|
#ifdef DEBUG_FAST
|
|
|
|
"-Debug"
|
|
|
|
#elif defined( PCSX2_DEBUG )
|
|
|
|
"-Debug/Strict" // strict debugging is slow!
|
|
|
|
#elif defined( PCSX2_DEVBUILD )
|
|
|
|
"-Dev"
|
|
|
|
#else
|
|
|
|
""
|
|
|
|
#endif
|
|
|
|
,SVN_REV,SVN_MODS ? "m" : ""
|
|
|
|
);
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CALLBACK PS2EgetLibType() {
|
|
|
|
return PS2E_LT_USB;
|
|
|
|
}
|
|
|
|
|
|
|
|
char* CALLBACK PS2EgetLibName()
|
|
|
|
{
|
|
|
|
InitLibraryName();
|
|
|
|
return libraryName;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CALLBACK PS2EgetLibVersion2(u32 type) {
|
|
|
|
return (version<<16) | (revision<<8) | build;
|
|
|
|
}
|
|
|
|
|
|
|
|
void USBirq(int cycles)
|
|
|
|
{
|
|
|
|
USB_LOG("USBirq.\n");
|
|
|
|
|
|
|
|
_USBirq(cycles);
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 CALLBACK USBinit() {
|
|
|
|
LoadConfig();
|
|
|
|
if (conf.Log)
|
|
|
|
{
|
|
|
|
usbLog = fopen("logs/usbLog.txt", "w");
|
|
|
|
setvbuf(usbLog, NULL, _IONBF, 0);
|
|
|
|
USB_LOG("USBqemu plugin version %d,%d\n",revision,build);
|
|
|
|
USB_LOG("USBinit\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
qemu_ohci = ohci_create(0x1f801600,2);
|
|
|
|
qemu_ohci->rhport[0].port.dev = usb_keyboard_init();
|
|
|
|
qemu_ohci->rhport[0].port.ops->attach(&(qemu_ohci->rhport[0].port));
|
|
|
|
|
|
|
|
clocks = 0;
|
|
|
|
remaining = 0;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBshutdown() {
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
#ifdef _DEBUG
|
|
|
|
if(usbLog)
|
|
|
|
fclose(usbLog);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 CALLBACK USBopen(void *pDsp) {
|
|
|
|
USB_LOG("USBopen\n");
|
|
|
|
|
|
|
|
HWND hWnd=(HWND)pDsp;
|
|
|
|
|
|
|
|
if (!IsWindow (hWnd) && !IsBadReadPtr ((u32*)hWnd, 4))
|
|
|
|
hWnd = *(HWND*)hWnd;
|
|
|
|
if (!IsWindow (hWnd))
|
|
|
|
hWnd = NULL;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
while (GetWindowLong (hWnd, GWL_STYLE) & WS_CHILD)
|
|
|
|
hWnd = GetParent (hWnd);
|
|
|
|
}
|
|
|
|
gsWindowHandle = hWnd;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBclose() {
|
|
|
|
}
|
|
|
|
|
|
|
|
u8 CALLBACK USBread8(u32 addr) {
|
|
|
|
USB_LOG("* Invalid 8bit read at address %lx\n", addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u16 CALLBACK USBread16(u32 addr) {
|
|
|
|
USB_LOG("* Invalid 16bit read at address %lx\n", addr);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
u32 CALLBACK USBread32(u32 addr) {
|
|
|
|
u32 hard;
|
|
|
|
|
|
|
|
hard=ohci_mem_read(qemu_ohci,addr - qemu_ohci->mem);
|
|
|
|
|
|
|
|
USB_LOG("* Known 32bit read at address %lx: %lx\n", addr, hard);
|
|
|
|
|
|
|
|
return hard;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBwrite8(u32 addr, u8 value) {
|
|
|
|
USB_LOG("* Invalid 8bit write at address %lx value %x\n", addr, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBwrite16(u32 addr, u16 value) {
|
|
|
|
USB_LOG("* Invalid 16bit write at address %lx value %x\n", addr, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBwrite32(u32 addr, u32 value) {
|
|
|
|
USB_LOG("* Known 32bit write at address %lx value %lx\n", addr, value);
|
|
|
|
ohci_mem_write(qemu_ohci,addr - qemu_ohci->mem, value);
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBirqCallback(USBcallback callback) {
|
|
|
|
_USBirq = callback;
|
|
|
|
}
|
|
|
|
|
|
|
|
extern u32 bits;
|
|
|
|
|
|
|
|
int CALLBACK _USBirqHandler(void)
|
|
|
|
{
|
|
|
|
//fprintf(stderr," * USB: IRQ Acknowledged.\n");
|
|
|
|
//qemu_ohci->intr_status&=~bits;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
USBhandler CALLBACK USBirqHandler(void) {
|
|
|
|
return (USBhandler)_USBirqHandler;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBsetRAM(void *mem) {
|
|
|
|
ram = (u8*)mem;
|
|
|
|
}
|
|
|
|
|
|
|
|
// extended funcs
|
|
|
|
|
|
|
|
char USBfreezeID[] = "USBqemu01";
|
|
|
|
|
|
|
|
typedef struct {
|
|
|
|
char freezeID[10];
|
|
|
|
int cycles;
|
|
|
|
int remaining;
|
|
|
|
OHCIState t;
|
|
|
|
int extraData; // for future expansion with the device state
|
|
|
|
} USBfreezeData;
|
|
|
|
|
|
|
|
s32 CALLBACK USBfreeze(int mode, freezeData *data) {
|
|
|
|
USBfreezeData usbd;
|
|
|
|
|
|
|
|
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;
|
2016-06-29 17:58:14 +00:00
|
|
|
usbd.cycles = static_cast<int>(clocks);
|
|
|
|
usbd.remaining = static_cast<int>(remaining);
|
2015-08-04 21:52:48 +00:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
}
|
|
|
|
|
|
|
|
clocks = usbd.cycles;
|
|
|
|
remaining = usbd.remaining;
|
|
|
|
|
|
|
|
// WARNING: TODO: Save the state of the attached devices!
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CALLBACK USBasync(u32 _cycles)
|
|
|
|
{
|
|
|
|
remaining += _cycles;
|
|
|
|
clocks += remaining;
|
|
|
|
if(qemu_ohci->eof_timer>0)
|
|
|
|
{
|
|
|
|
while(remaining>=qemu_ohci->eof_timer)
|
|
|
|
{
|
|
|
|
remaining-=qemu_ohci->eof_timer;
|
|
|
|
qemu_ohci->eof_timer=0;
|
|
|
|
ohci_frame_boundary(qemu_ohci);
|
|
|
|
}
|
|
|
|
if((remaining>0)&&(qemu_ohci->eof_timer>0))
|
|
|
|
{
|
|
|
|
s64 m = qemu_ohci->eof_timer;
|
|
|
|
if(remaining < m)
|
|
|
|
m = remaining;
|
|
|
|
qemu_ohci->eof_timer -= m;
|
|
|
|
remaining -= m;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
//if(qemu_ohci->eof_timer <= 0)
|
|
|
|
//{
|
|
|
|
// ohci_frame_boundary(qemu_ohci);
|
|
|
|
//}
|
|
|
|
}
|
|
|
|
|
|
|
|
s32 CALLBACK USBtest() {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
void cpu_physical_memory_rw(u32 addr, u8 *buf,
|
|
|
|
int len, int is_write)
|
|
|
|
{
|
|
|
|
if(is_write)
|
|
|
|
memcpy(&(ram[addr]),buf,len);
|
|
|
|
else
|
|
|
|
memcpy(buf,&(ram[addr]),len);
|
|
|
|
}
|
|
|
|
|
|
|
|
s64 get_clock()
|
|
|
|
{
|
|
|
|
return clocks;
|
|
|
|
}
|
|
|
|
|
|
|
|
void *qemu_mallocz(uint32_t size)
|
|
|
|
{
|
|
|
|
void *m=malloc(size);
|
|
|
|
if(!m) return NULL;
|
|
|
|
memset(m,0,size);
|
|
|
|
return m;
|
|
|
|
}
|