From c162af659e1dccaf07257b82e51c8d9f6bf654c5 Mon Sep 17 00:00:00 2001 From: dborth Date: Tue, 6 Jul 2010 06:39:15 +0000 Subject: [PATCH] updated usb2 support (now supports both ports). requires new cIOS 202. freetypegx optimization. --- source/snes9xgx.cpp | 57 +++++++++++- source/utils/FreeTypeGX.cpp | 73 +++++----------- source/utils/ehcmodule.elf.o | Bin 20668 -> 22376 bytes source/utils/mload.c | 165 +++++++++++++++++------------------ source/utils/mload.h | 7 +- source/utils/usb2storage.c | 110 ++++++++++------------- source/utils/usb2storage.h | 7 ++ 7 files changed, 209 insertions(+), 210 deletions(-) diff --git a/source/snes9xgx.cpp b/source/snes9xgx.cpp index 48803a3..6fd2a6d 100644 --- a/source/snes9xgx.cpp +++ b/source/snes9xgx.cpp @@ -252,6 +252,31 @@ static bool FindIOS(u32 ios) free(titles); return false; } + +#define ROUNDDOWN32(v) (((u32)(v)-0x1f)&~0x1f) + +static bool USBLANDetected() +{ + u8 dummy; + u8 i; + u16 vid, pid; + + USB_Initialize(); + u8 *buffer = (u8*)ROUNDDOWN32(((u32)SYS_GetArena2Hi() - (32*1024))); + memset(buffer, 0, 8 << 3); + + if(USB_GetDeviceList("/dev/usb/oh0", buffer, 8, 0, &dummy) < 0) + return false; + + for(i = 0; i < 8; i++) + { + memcpy(&vid, (buffer + (i << 3) + 4), 2); + memcpy(&pid, (buffer + (i << 3) + 6), 2); + if( (vid == 0x0b95) && (pid == 0x7720)) + return true; + } + return false; +} #endif /**************************************************************************** * USB Gecko Debugging @@ -318,6 +343,8 @@ main(int argc, char *argv[]) #endif #ifdef HW_RVL + bool usblan = USBLANDetected(); + // try to load IOS 202 if(FindIOS(202)) IOS_ReloadIOS(202); @@ -326,12 +353,34 @@ main(int argc, char *argv[]) if(IOS_GetVersion() == 202) { - DI_LoadDVDX(false); - DI_Init(); - - // load usb2 driver + // enable DVD and USB2 if(mload_init() >= 0 && load_ehci_module()) + { + int mode = 3; + + if(usblan) + { + int usblanport = GetUSB2LanPort(); + + if(usblanport >= 0) + { + if(usblanport == 1) + mode = 1; + else + mode = 2; + + USB2Storage_Close(); + mload_close(); + IOS_ReloadIOS(202); + mload_init(); + load_ehci_module(); + } + } + SetUSB2Mode(mode); USB2Enable(true); + DI_LoadDVDX(false); + DI_Init(); + } } #endif diff --git a/source/utils/FreeTypeGX.cpp b/source/utils/FreeTypeGX.cpp index d982292..8c2f936 100644 --- a/source/utils/FreeTypeGX.cpp +++ b/source/utils/FreeTypeGX.cpp @@ -22,8 +22,6 @@ #include "FreeTypeGX.h" -#define EXPLODE_UINT8_TO_UINT32(x) (x << 24) | (x << 16) | (x << 8) | x - static FT_Library ftLibrary; /**< FreeType FT_Library instance. */ static FT_Face ftFace; /**< FreeType reusable FT_Face typographic object. */ static FT_GlyphSlot ftSlot; /**< FreeType reusable FT_GlyphSlot glyph container object. */ @@ -91,47 +89,6 @@ wchar_t* charToWideChar(const char* strChar) return strWChar; } -static uint32_t* convertBufferToRGBA8(uint32_t* rgbaBuffer, uint16_t bufferWidth, uint16_t bufferHeight) { - uint32_t bufferSize = (bufferWidth * bufferHeight) << 2; - uint32_t* dataBufferRGBA8 = (uint32_t *)memalign(32, bufferSize); - memset(dataBufferRGBA8, 0x00, bufferSize); - - uint8_t *src = (uint8_t *)rgbaBuffer; - uint8_t *dst = (uint8_t *)dataBufferRGBA8; - - for(uint32_t block = 0; block < bufferHeight; block += 4) { - for(uint32_t i = 0; i < bufferWidth; i += 4) { - for (uint32_t c = 0; c < 4; c++) { - uint32_t blockWid = (((block + c) * bufferWidth)+i)<<2 ; - - *dst++ = src[blockWid+ 3]; // ar = 0 - *dst++ = src[blockWid+ 0]; - *dst++ = src[blockWid+ 7]; // ar = 1 - *dst++ = src[blockWid+ 4]; - *dst++ = src[blockWid+ 11]; // ar = 2 - *dst++ = src[blockWid+ 8]; - *dst++ = src[blockWid+ 15]; // ar = 3 - *dst++ = src[blockWid+ 12]; - } - for (uint32_t c = 0; c < 4; c++) { - uint32_t blockWid = (((block + c) * bufferWidth)+i)<<2 ; - - *dst++ = src[blockWid+ 1]; // gb = 0 - *dst++ = src[blockWid+ 2]; - *dst++ = src[blockWid+ 5]; // gb = 1 - *dst++ = src[blockWid+ 6]; - *dst++ = src[blockWid+ 9]; // gb = 2 - *dst++ = src[blockWid+ 10]; - *dst++ = src[blockWid+ 13]; // gb = 3 - *dst++ = src[blockWid+ 14]; - } - } - } - DCFlushRange(dataBufferRGBA8, bufferSize); - - return dataBufferRGBA8; -} - /** * Default constructor for the FreeTypeGX class. * @@ -333,26 +290,36 @@ uint16_t FreeTypeGX::cacheGlyphDataComplete() * * @param bmp A pointer to the most recently rendered glyph's bitmap. * @param charData A pointer to an allocated ftgxCharData structure whose data represent that of the last rendered glyph. + * + * Optimized for RGBA8 use by Dimok. */ void FreeTypeGX::loadGlyphData(FT_Bitmap *bmp, ftgxCharData *charData) { - uint32_t *glyphData = (uint32_t *)memalign(32, charData->textureWidth * charData->textureHeight * 4); - memset(glyphData, 0x00, charData->textureWidth * charData->textureHeight * 4); + int length = charData->textureWidth * charData->textureHeight * 4; + + uint8_t * glyphData = (uint8_t *) memalign(32, length); + if(!glyphData) + return; + + memset(glyphData, 0x00, length); uint8_t *src = (uint8_t *)bmp->buffer; - uint32_t *dest = glyphData, *ptr = dest; + uint32_t offset; - for (int imagePosY = 0; imagePosY < bmp->rows; imagePosY++) + for (int imagePosY = 0; imagePosY < bmp->rows; ++imagePosY) { - for (int imagePosX = 0; imagePosX < bmp->width; imagePosX++) + for (int imagePosX = 0; imagePosX < bmp->width; ++imagePosX) { - *ptr++ = EXPLODE_UINT8_TO_UINT32(*src); - src++; + offset = ((((imagePosY >> 2) * (charData->textureWidth >> 2) + (imagePosX >> 2)) << 5) + ((imagePosY & 3) << 2) + (imagePosX & 3)) << 1; + glyphData[offset] = *src; + glyphData[offset+1] = *src; + glyphData[offset+32] = *src; + glyphData[offset+33] = *src; + ++src; } - ptr = dest += charData->textureWidth; } - charData->glyphDataTexture = convertBufferToRGBA8(glyphData, charData->textureWidth, charData->textureHeight); - free(glyphData); + DCFlushRange(glyphData, length); + charData->glyphDataTexture = (uint32_t *) glyphData; } /** diff --git a/source/utils/ehcmodule.elf.o b/source/utils/ehcmodule.elf.o index f88d988c66d541c4888d3edf932e7b6dd2bf2cdd..1a67ac279a849e830232f742672ab4a70e7fff01 100644 GIT binary patch literal 22376 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 literal 20668 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 F{{WkqWKjSB diff --git a/source/utils/mload.c b/source/utils/mload.c index 2c6f534..24bb65b 100644 --- a/source/utils/mload.c +++ b/source/utils/mload.c @@ -14,7 +14,7 @@ along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ - + #ifdef HW_RVL #include @@ -22,25 +22,15 @@ #include #include #include -#include #include "unistd.h" #include "ehcmodule_elf.h" -#define MLOAD_MLOAD_THREAD_ID 0x4D4C4400 -#define MLOAD_LOAD_MODULE 0x4D4C4480 -#define MLOAD_RUN_MODULE 0x4D4C4481 +#define MLOAD_GET_IOS_BASE 0x4D4C4401 +#define MLOAD_GET_MLOAD_VERSION 0x4D4C4402 #define MLOAD_RUN_THREAD 0x4D4C4482 - -#define MLOAD_STOP_THREAD 0x4D4C4484 -#define MLOAD_CONTINUE_THREAD 0x4D4C4485 - #define MLOAD_GET_LOAD_BASE 0x4D4C4490 #define MLOAD_MEMSET 0x4D4C4491 -#define MLOAD_GET_EHCI_DATA 0x4D4C44A0 - -#define MLOAD_SET_ES_IOCTLV 0x4D4C44B0 - #define getbe32(x) ((adr[x]<<24) | (adr[x+1]<<16) | (adr[x+2]<<8) | (adr[x+3])) typedef struct @@ -84,17 +74,53 @@ typedef struct } data_elf; static const char mload_fs[] ATTRIBUTE_ALIGN(32) = "/dev/mload"; + static s32 mload_fd = -1; +static s32 hid = -1; + +int mloadVersion = -1; +int iosBase = -1; + +// to close the device (remember call it when rebooting the IOS!) +int mload_close() +{ + int ret; + + if (hid >= 0) + { + iosDestroyHeap(hid); + hid = -1; + } + + if (mload_fd < 0) + return -1; + + ret = IOS_Close(mload_fd); + mload_fd = -1; + return ret; +} // to init/test if the device is running int mload_init() { int n; + if (hid < 0) + hid = iosCreateHeap(0x800); + + if (hid < 0) + { + if (mload_fd >= 0) + IOS_Close(mload_fd); + + mload_fd = -1; + return hid; + } + if (mload_fd >= 0) return 0; - for (n = 0; n < 10; n++) // try 2.5 seconds + for (n = 0; n < 20; n++) // try 5 seconds { mload_fd = IOS_Open(mload_fs, 0); @@ -104,22 +130,16 @@ int mload_init() usleep(250 * 1000); } - return mload_fd; -} - -// to close the device (remember call it when rebooting the IOS!) -int mload_close() -{ - int ret; - if (mload_fd < 0) - return -1; + return mload_close(); - ret = IOS_Close(mload_fd); + mloadVersion = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_MLOAD_VERSION, ":"); + iosBase = IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_IOS_BASE, ":"); - mload_fd = -1; + if(mloadVersion < 82) // unsupported IOS202 + return mload_close(); - return ret; + return mload_fd; } // fix starlet address to read/write (uses SEEK_SET, etc as mode) @@ -127,6 +147,7 @@ static int mload_seek(int offset, int mode) { if (mload_init() < 0) return -1; + return IOS_Seek(mload_fd, offset, mode); } @@ -135,29 +156,17 @@ static int mload_write(const void * buf, u32 size) { if (mload_init() < 0) return -1; + return IOS_Write(mload_fd, buf, size); } // fill a block (similar to memset) static int mload_memset(void *starlet_addr, int set, int len) { - int ret; - s32 hid = -1; - if (mload_init() < 0) return -1; - hid = iosCreateHeap(0x800); - - if (hid < 0) - return hid; - - ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, - set, len); - - iosDestroyHeap(hid); - - return ret; + return IOS_IoctlvFormat(hid, mload_fd, MLOAD_MEMSET, "iii:", starlet_addr, set, len); } // load a module from the PPC @@ -202,25 +211,23 @@ static int mload_elf(void *my_elf, data_elf *data_elf) { switch (getbe32(m)) { - case 0x9: - data_elf->start = (void *) getbe32(m+4); - break; - case 0x7D: - data_elf->prio = getbe32(m+4); - break; - case 0x7E: - data_elf->size_stack = getbe32(m+4); - break; - case 0x7F: - data_elf->stack = (void *) (getbe32(m+4)); - break; - + case 0x9: + data_elf->start = (void *) getbe32(m+4); + break; + case 0x7D: + data_elf->prio = getbe32(m+4); + break; + case 0x7E: + data_elf->size_stack = getbe32(m+4); + break; + case 0x7F: + data_elf->stack = (void *) (getbe32(m+4)); + break; } } } else if (entries->type == 1 && entries->memsz != 0 && entries->vaddr != 0) { - if (mload_memset((void *) entries->vaddr, 0, entries->memsz) < 0) return -1; if (mload_seek(entries->vaddr, SEEK_SET) < 0) @@ -229,52 +236,44 @@ static int mload_elf(void *my_elf, data_elf *data_elf) return -1; } } - return 0; } // run one thread (you can use to load modules or binary files) -static int mload_run_thread(void *starlet_addr, void *starlet_top_stack, - int stack_size, int priority) +static int mload_run_thread(void *starlet_addr, void *starlet_top_stack, int stack_size, int priority) { - int ret; - s32 hid = -1; - if (mload_init() < 0) return -1; - hid = iosCreateHeap(0x800); + return IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", starlet_addr, starlet_top_stack, stack_size, priority); +} - if (hid < 0) - return hid; +// get the base and the size of the memory readable/writable to load modules +static int mload_get_load_base(u32 *starlet_base, int *size) +{ + if (mload_init() < 0) + return -1; - ret = IOS_IoctlvFormat(hid, mload_fd, MLOAD_RUN_THREAD, "iiii:", - starlet_addr, starlet_top_stack, stack_size, priority); - - iosDestroyHeap(hid); - - return ret; + return IOS_IoctlvFormat(hid, mload_fd, MLOAD_GET_LOAD_BASE, ":ii", starlet_base, size); } bool load_ehci_module() { - data_elf my_data_elf; - my_data_elf.start = NULL; - my_data_elf.prio = 0; - my_data_elf.stack = NULL; - my_data_elf.size_stack = 0; + data_elf elf; + memset(&elf, 0, sizeof(data_elf)); - if(mload_elf((void *) ehcmodule_elf, &my_data_elf) != 0) + u32 addr; + int len; + + mload_get_load_base(&addr, &len); + + if(mload_elf((void *) ehcmodule_elf, &elf) != 0) return false; - if (mload_run_thread(my_data_elf.start, my_data_elf.stack, - my_data_elf.size_stack, my_data_elf.prio) < 0) - { - usleep(1000); - if (mload_run_thread(my_data_elf.start, my_data_elf.stack, - my_data_elf.size_stack, 0x47) < 0) - return false; - } + if(mload_run_thread(elf.start, elf.stack, elf.size_stack, 0x47) < 0) + return false; + + usleep(5000); return true; } diff --git a/source/utils/mload.h b/source/utils/mload.h index db6bb5b..aa01daf 100644 --- a/source/utils/mload.h +++ b/source/utils/mload.h @@ -18,16 +18,15 @@ #ifndef __MLOAD_H__ #define __MLOAD_H__ -#ifdef __cplusplus extern "C" { -#endif + +extern int mloadVersion; +extern int iosBase; int mload_init(); bool load_ehci_module(); int mload_close(); -#ifdef __cplusplus } -#endif #endif diff --git a/source/utils/usb2storage.c b/source/utils/usb2storage.c index 3b7fc68..4f3c374 100644 --- a/source/utils/usb2storage.c +++ b/source/utils/usb2storage.c @@ -50,22 +50,24 @@ #define debug_printf(fmt, args...) #endif // DEBUG_USB2 -#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) -#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) -#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) -#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) +#define UMS_BASE (('U'<<24)|('M'<<16)|('S'<<8)) +#define USB_IOCTL_UMS_INIT (UMS_BASE+0x1) +#define USB_IOCTL_UMS_GET_CAPACITY (UMS_BASE+0x2) +#define USB_IOCTL_UMS_READ_SECTORS (UMS_BASE+0x3) #define USB_IOCTL_UMS_WRITE_SECTORS (UMS_BASE+0x4) #define USB_IOCTL_UMS_READ_STRESS (UMS_BASE+0x5) #define USB_IOCTL_UMS_SET_VERBOSE (UMS_BASE+0x6) #define USB_IOCTL_UMS_IS_INSERTED (UMS_BASE+0x7) +#define USB_IOCTL_UMS_USB_MODE (UMS_BASE+0x8) +#define USB_IOCTL_UMS_GET_USBLAN_PORT (UMS_BASE+0x9) #define USB_IOCTL_UMS_UMOUNT (UMS_BASE+0x10) -#define USB_IOCTL_UMS_START (UMS_BASE+0x11) -#define USB_IOCTL_UMS_STOP (UMS_BASE+0x12) -#define USB_IOCTL_UMS_EXIT (UMS_BASE+0x16) +#define USB_IOCTL_UMS_START (UMS_BASE+0x11) +#define USB_IOCTL_UMS_STOP (UMS_BASE+0x12) +#define USB_IOCTL_UMS_EXIT (UMS_BASE+0x16) #define UMS_HEAPSIZE 2*1024 -#define UMS_MAXPATH 16 +#define UMS_MAXPATH 16 static s32 hId = -1; static s32 __usb2fd = -1; @@ -81,7 +83,6 @@ static u8 __usb2_heap_created = 0; static DISC_INTERFACE __io_usb1storage; static int usb1disc_inited = 0; extern const DISC_INTERFACE __io_usb2storage; -static int currentMode = 2; // 1 = use USB1 interface, 2 = use USB2 interface static s32 USB2CreateHeap() { @@ -108,11 +109,11 @@ static s32 USB2CreateHeap() static s32 USB2Storage_Initialize() { - static bool inited = false; - - if(inited) + if(__usb2fd > 0) return 0; + char *devicepath = NULL; + if (usb2_mutex == LWP_MUTEX_NULL) LWP_MutexInit(&usb2_mutex, false); @@ -134,26 +135,9 @@ static s32 USB2Storage_Initialize() if (fixed_buffer == NULL) fixed_buffer = __lwp_heap_allocate(&usb2_heap, USB2_BUFFER); - inited = true; - return 0; -} - -static s32 USB2Storage_Open(int verbose) -{ - char *devicepath = NULL; - s32 ret = USB_OK; - u32 size = 0; - - if(__usb2fd > 0) - return 0; - - if(USB2Storage_Initialize() < 0) - return -1; - - LWP_MutexLock(usb2_mutex); - if (__usb2fd <= 0) { + LWP_MutexLock(usb2_mutex); devicepath = iosAlloc(hId, UMS_MAXPATH); if (devicepath == NULL) { @@ -164,16 +148,26 @@ static s32 USB2Storage_Open(int verbose) snprintf(devicepath, USB_MAXPATH, "/dev/usb2"); __usb2fd = IOS_Open(devicepath, 0); iosFree(hId, devicepath); + LWP_MutexUnlock(usb2_mutex); } if(__usb2fd <= 0) return -1; - if (verbose) - ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_SET_VERBOSE, ":"); + return 0; +} + +static s32 USB2Storage_Open() +{ + s32 ret = USB_OK; + u32 size = 0; + + if(USB2Storage_Initialize() < 0) + return -1; + ret = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_INIT, ":"); debug_printf("usb2 init value: %i\n", ret); - + if (ret < 0) debug_printf("usb2 error init\n"); else @@ -189,7 +183,7 @@ static s32 USB2Storage_Open(int verbose) return ret; } -static void USB2Storage_Close() +void USB2Storage_Close() { if(__usb2fd <= 0) return; @@ -225,28 +219,18 @@ void USB2Enable(bool enable) static bool __usb2storage_Startup(void) { - if(__usb2fd > 0) - return true; - - USB2Storage_Open(0); + if(USB2Storage_Open() < 0) + return false; if(__usb2fd > 0) - { - currentMode = 2; return true; - } - if(__io_usb1storage.startup()) - { - currentMode = 1; - return true; - } return false; } static bool __usb2storage_IsInserted(void) { - if (!__usb2storage_Startup()) + if (USB2Storage_Open() < 0) return false; if(usb2_mutex == LWP_MUTEX_NULL) @@ -257,17 +241,11 @@ static bool __usb2storage_IsInserted(void) LWP_MutexLock(usb2_mutex); int retval = IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_IS_INSERTED, ":"); LWP_MutexUnlock(usb2_mutex); - debug_printf("isinserted usb2 retval: %d ret: %d\n",retval,ret); + debug_printf("isinserted usb2 retval: %d \n",retval); if (retval > 0) return true; } - - if (__io_usb1storage.isInserted() > 0) - { - debug_printf("isinserted usb1 retval: %d\n",retval); - return true; - } return false; } @@ -277,9 +255,6 @@ static bool __usb2storage_ReadSectors(u32 sector, u32 numSectors, void *buffer) u32 sectors = 0; uint8_t *dest = buffer; - if (currentMode == 1) - return __io_usb1storage.readSectors(sector, numSectors, buffer); - if (__usb2fd < 1 || usb2_mutex == LWP_MUTEX_NULL) return false; @@ -320,9 +295,6 @@ static bool __usb2storage_WriteSectors(u32 sector, u32 numSectors, const void *b u32 sectors = 0; uint8_t *dest = (uint8_t *) buffer; - if (currentMode == 1) - return __io_usb1storage.writeSectors(sector, numSectors, buffer); - if (__usb2fd < 1 || usb2_mutex == LWP_MUTEX_NULL) return false; @@ -358,20 +330,26 @@ static bool __usb2storage_WriteSectors(u32 sector, u32 numSectors, const void *b static bool __usb2storage_ClearStatus(void) { - if (currentMode == 1) - return __io_usb1storage.clearStatus(); - return true; } static bool __usb2storage_Shutdown(void) { - if (currentMode == 1) - return __io_usb1storage.shutdown(); - return true; } +void SetUSB2Mode(int mode) +{ + USB2Storage_Initialize(); + IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_USB_MODE, "b:",(u8)mode); +} + +int GetUSB2LanPort() +{ + USB2Storage_Initialize(); + return IOS_IoctlvFormat(hId, __usb2fd, USB_IOCTL_UMS_GET_USBLAN_PORT, ":"); +} + const DISC_INTERFACE __io_usb2storage = { DEVICE_TYPE_WII_USB, FEATURE_MEDIUM_CANREAD | FEATURE_MEDIUM_CANWRITE | FEATURE_WII_USB, diff --git a/source/utils/usb2storage.h b/source/utils/usb2storage.h index b6b9eb9..d7bed91 100644 --- a/source/utils/usb2storage.h +++ b/source/utils/usb2storage.h @@ -15,6 +15,13 @@ #endif void USB2Enable(bool e); +void USB2Storage_Close(); +int GetUSB2LanPort(); +void SetUSB2Mode(int mode); +//mode 0: port0 disabled & port1 disabled +//mode 1: port0 enabled & port1 disabled +//mode 2: port0 disabled & port1 enabled +//mode 3: port0 enabled & port1 enabled #ifdef __cplusplus }