GaryOderNichts / recovery_menu

Recovery Menu for the Nintendo Wii U
GNU General Public License v2.0
139 stars 17 forks source link

Pairing gamepad with no display #19

Open nelgin opened 1 year ago

nelgin commented 1 year ago

It was a challenge trying to pair the gamepad when you get no screen output. You cannot see which numbers to type. It seems the pin is generated from the MAC address so as long as you don't change your wifi module in the console, it should stay the same so make a note when you have a working Wii U.

The following snippet of code added in the option_pairDRC function will turn off the led, and then wait 3 seconds Then for each of the 4 numbers in the pincode, the red led will flash for 1/2 second with a 3 second pause between each number If you get a 6 second gap between numbers, then you know that number is a 0.

Example

BLANK....FLASH FLASH / PAUSE / FLASH / PAUSE / PAUSE / FLASH FLASH FLASH 

Would be read as 2, 1, 0, 3.

0 = spade 1 = heart 2 = diamond 3 = club

So on your gamepad you'd press diamond, heart, spade, club.

Insert the following code after the "static const char symbol_names" code block.

    setNotificationLED(NOTIF_LED_OFF);

    for (int i = 0; i < 4; i++) {
        usleep(3*1000*1000);
        for (int j = 1; j < pincode[i]+1; j++ ) {
            setNotificationLED(NOTIF_LED_RED);
            usleep(1000*500);
            setNotificationLED(NOTIF_LED_OFF);
            usleep(1000*500);
        }
    }
    setNotificationLED(NOTIF_LED_BLUE|NOTIF_LED_RED);
vgmoose commented 1 year ago

Another potentially useful way to pair a gamepad with no display would be to write the pin to the SD card, so it can be checked on a computer (maybe alongside other hw information?)

nelgin commented 1 year ago

That's actually what I did first. If you want to use that method, you can add this section in the same place:

    int pairHandle;
    int pairres = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/pairing.txt", "w", &pairHandle);
    if (pairres < 0) {
        FSA_CloseFile(fsaHandle, pairHandle);
    }
    void* dataBuffer = IOS_HeapAllocAligned(CROSS_PROCESS_HEAP_ID, 0x100, 0x40);
    snprintf(dataBuffer, 0x40, "%d %d %d %d\n", pincode[0], pincode[1], pincode[2], pincode[3]);
    pairres = FSA_WriteFile(fsaHandle, dataBuffer, 1, strnlen(dataBuffer, 0x40), pairHandle, 0);
    FSA_CloseFile(fsaHandle, pairHandle);

This will write a numbers to the sdcard in a file called pairing. The numbers shouldn't ever chance for that WiiU as long as you don't change the network module. You could modify it to write out the words instead. I actually now have both included. It's obviously going to be in a file pairing.txt

As for adding other information, I tried to dump the manufacturer of the emmc chip to a file but I only got NULL characters so not sure what's going on there. I may need Gary's assistance to figure that out.

filipe-maia commented 1 year ago

Hi. I'm facing the same problem. I have a Wii U that doesn't show any image and I don't have any gamepad connected. I can brute force the code on the gamepad, but it would take a long time. I know the Wii U works, because I was able to blindly use udpih and recovery_menu to dump OTP+SEEPROM. A new option that could log into a file on the SD card the code to connect a gamepad to the console would be great. The sugestion from @Sierraffinity in issue 13 is also great. Please implement one or the other or both. Thanks.

EDIT: Using recovery_menu_dc_init did not solve my problem, because I believe the problem of this Wii U is in the HDMI port.

filipe-maia commented 1 year ago

Hi.

Thank you @nelgin .

I've added your code, the end result is:

(...)
#include "utils.h"
#include "fsa.h"
#include <string.h>
(...)
void option_PairDRC(void)
{
    gfx_clear(COLOR_BACKGROUND);
    drawTopBar("Pairing Gamepad...");

    uint32_t index = 16 + 8 + 2 + 8;

    int res = CCRCDCSetup();
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "CCRCDCSetup() failed: %x", res);

        waitButtonInput();
        return;
    }

    gfx_print(16, index, 0, "Get pincode...");
    index += CHAR_SIZE_DRC_Y + 4;

    uint8_t pincode[4];
    res = CCRSysGetPincode(pincode);
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "Failed to get pincode: %x", res);

        waitButtonInput();
        return;
    }

    static const char symbol_names[][8] = {
        "spade",
        "heart",
        "diamond",
        "clubs",
    };

    int pairHandle;
    int pairres = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/pairing.txt", "w", &pairHandle);
    if (pairres < 0) {
        FSA_CloseFile(fsaHandle, pairHandle);
    }
    void* dataBuffer = IOS_HeapAllocAligned(CROSS_PROCESS_HEAP_ID, 0x100, 0x40);
    snprintf(dataBuffer, 0x40, "%d %d %d %d\n", pincode[0], pincode[1], pincode[2], pincode[3]);
    pairres = FSA_WriteFile(fsaHandle, dataBuffer, 1, strnlen(dataBuffer, 0x40), pairHandle, 0);
    FSA_CloseFile(fsaHandle, pairHandle);
    SMC_SetNotificationLED(NOTIF_LED_OFF);
    for (int i = 0; i < 4; i++) {
        usleep(3*1000*1000);
        for (int j = 1; j < pincode[i]+1; j++ ) {
            SMC_SetNotificationLED(NOTIF_LED_RED);
            usleep(1000*500);
            SMC_SetNotificationLED(NOTIF_LED_OFF);
            usleep(1000*500);
        }
    }
    SMC_SetNotificationLED(NOTIF_LED_BLUE|NOTIF_LED_RED);

    gfx_set_font_color(COLOR_SUCCESS);
    gfx_printf(16, index, 0, "Pincode: %x%x%x%x (%s %s %s %s)",
        pincode[0], pincode[1], pincode[2], pincode[3],
        symbol_names[pincode[0]], symbol_names[pincode[1]], symbol_names[pincode[2]], symbol_names[pincode[3]]);
    gfx_set_font_color(COLOR_PRIMARY);
    index += CHAR_SIZE_DRC_Y + 4;

    // DRC pairing timeout
    int timeout = DRC_PAIRING_TIMEOUT;

    // display a "this gamepad has already been paired" message, if a gamepad is alrady connected
    res = CCRCDCDevicePing(CCR_DEST_DRC0);
    if (res == 0) {
        gfx_print(16, index, 0, "Gamepad already connected, displaying message...");
        index += CHAR_SIZE_DRC_Y + 4;

        uint16_t args[2] = { 0, timeout + 5 };
        CCRCDCSysDrcDisplayMessage(CCR_DEST_DRC0, args);
    }

    usleep(1000 * 100);

    uint8_t mute = 0xff;
    CCRCDCPerSetLcdMute(CCR_DEST_DRC0, &mute);

    gfx_print(16, index, 0, "Starting pairing...");
    index += CHAR_SIZE_DRC_Y + 4;

    res = CCRSysStartPairing(CCR_DEST_DRC0, timeout, pincode);
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "Failed to start pairing: %x", res);

        waitButtonInput();
        return;
    }

    uint32_t status;
    while (timeout--) {
        res = CCRCDCWpsStatus(&status);
        if (res < 0) {
            gfx_set_font_color(COLOR_ERROR);
            gfx_printf(16, index, GfxPrintFlag_ClearBG, "Failed to get status: %x", res);

            CCRCDCWpsStop();

            waitButtonInput();
            return;
        }

        if (status == 0) {
            // paired
            break;
        } else if (status == 2) {
            // pairing
        } else if (status == 1) {
            // searching
        } else {
            // error
            break;
        }

        gfx_printf(16, index, GfxPrintFlag_ClearBG, "Waiting for Gamepad... (%lx) (Timeout: %d) ", status, timeout);
        usleep(1000 * 1000);
    }

    if (status != 0 || timeout <= 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, GfxPrintFlag_ClearBG, "Failed to pair GamePad: %lx", status);

        CCRCDCWpsStop();
    } else {
        gfx_set_font_color(COLOR_SUCCESS);
        gfx_print(16, index, GfxPrintFlag_ClearBG, "Paired GamePad");
    }

    waitButtonInput();
}

I've attached the built files (they also contain the changes of issue 18). recovery_menu.zip

filipe-maia commented 1 year ago

Hi. Just adding here this idea that "W00fer" said on discord: The LED blinking could be accompanied with the Wii U beeping, to understand the code easier. I think it's a good idea, if it is possible.

EDIT: The Wii U beeps when turning ON, doesn't it? Now I can't remember :S

Leerz commented 1 month ago

Hi.

Thank you @nelgin .

I've added your code, the end result is:

(...)
#include "utils.h"
#include "fsa.h"
#include <string.h>
(...)
void option_PairDRC(void)
{
    gfx_clear(COLOR_BACKGROUND);
    drawTopBar("Pairing Gamepad...");

    uint32_t index = 16 + 8 + 2 + 8;

    int res = CCRCDCSetup();
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "CCRCDCSetup() failed: %x", res);

        waitButtonInput();
        return;
    }

    gfx_print(16, index, 0, "Get pincode...");
    index += CHAR_SIZE_DRC_Y + 4;

    uint8_t pincode[4];
    res = CCRSysGetPincode(pincode);
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "Failed to get pincode: %x", res);

        waitButtonInput();
        return;
    }

    static const char symbol_names[][8] = {
        "spade",
        "heart",
        "diamond",
        "clubs",
    };

    int pairHandle;
    int pairres = FSA_OpenFile(fsaHandle, "/vol/storage_recovsd/pairing.txt", "w", &pairHandle);
    if (pairres < 0) {
        FSA_CloseFile(fsaHandle, pairHandle);
    }
    void* dataBuffer = IOS_HeapAllocAligned(CROSS_PROCESS_HEAP_ID, 0x100, 0x40);
    snprintf(dataBuffer, 0x40, "%d %d %d %d\n", pincode[0], pincode[1], pincode[2], pincode[3]);
    pairres = FSA_WriteFile(fsaHandle, dataBuffer, 1, strnlen(dataBuffer, 0x40), pairHandle, 0);
    FSA_CloseFile(fsaHandle, pairHandle);
    SMC_SetNotificationLED(NOTIF_LED_OFF);
    for (int i = 0; i < 4; i++) {
        usleep(3*1000*1000);
        for (int j = 1; j < pincode[i]+1; j++ ) {
            SMC_SetNotificationLED(NOTIF_LED_RED);
            usleep(1000*500);
            SMC_SetNotificationLED(NOTIF_LED_OFF);
            usleep(1000*500);
        }
    }
    SMC_SetNotificationLED(NOTIF_LED_BLUE|NOTIF_LED_RED);

    gfx_set_font_color(COLOR_SUCCESS);
    gfx_printf(16, index, 0, "Pincode: %x%x%x%x (%s %s %s %s)",
        pincode[0], pincode[1], pincode[2], pincode[3],
        symbol_names[pincode[0]], symbol_names[pincode[1]], symbol_names[pincode[2]], symbol_names[pincode[3]]);
    gfx_set_font_color(COLOR_PRIMARY);
    index += CHAR_SIZE_DRC_Y + 4;

    // DRC pairing timeout
    int timeout = DRC_PAIRING_TIMEOUT;

    // display a "this gamepad has already been paired" message, if a gamepad is alrady connected
    res = CCRCDCDevicePing(CCR_DEST_DRC0);
    if (res == 0) {
        gfx_print(16, index, 0, "Gamepad already connected, displaying message...");
        index += CHAR_SIZE_DRC_Y + 4;

        uint16_t args[2] = { 0, timeout + 5 };
        CCRCDCSysDrcDisplayMessage(CCR_DEST_DRC0, args);
    }

    usleep(1000 * 100);

    uint8_t mute = 0xff;
    CCRCDCPerSetLcdMute(CCR_DEST_DRC0, &mute);

    gfx_print(16, index, 0, "Starting pairing...");
    index += CHAR_SIZE_DRC_Y + 4;

    res = CCRSysStartPairing(CCR_DEST_DRC0, timeout, pincode);
    if (res < 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, 0, "Failed to start pairing: %x", res);

        waitButtonInput();
        return;
    }

    uint32_t status;
    while (timeout--) {
        res = CCRCDCWpsStatus(&status);
        if (res < 0) {
            gfx_set_font_color(COLOR_ERROR);
            gfx_printf(16, index, GfxPrintFlag_ClearBG, "Failed to get status: %x", res);

            CCRCDCWpsStop();

            waitButtonInput();
            return;
        }

        if (status == 0) {
            // paired
            break;
        } else if (status == 2) {
            // pairing
        } else if (status == 1) {
            // searching
        } else {
            // error
            break;
        }

        gfx_printf(16, index, GfxPrintFlag_ClearBG, "Waiting for Gamepad... (%lx) (Timeout: %d) ", status, timeout);
        usleep(1000 * 1000);
    }

    if (status != 0 || timeout <= 0) {
        gfx_set_font_color(COLOR_ERROR);
        gfx_printf(16, index, GfxPrintFlag_ClearBG, "Failed to pair GamePad: %lx", status);

        CCRCDCWpsStop();
    } else {
        gfx_set_font_color(COLOR_SUCCESS);
        gfx_print(16, index, GfxPrintFlag_ClearBG, "Paired GamePad");
    }

    waitButtonInput();
}

I've attached the built files (they also contain the changes of issue 18). recovery_menu.zip

20240626 If anyone faces this issue before where the wiiu doesn't work on hdmi and is not set on the av-multi or with a broken hdmi circuit (filters/encoder)

get the recovery files attached above then once the led turns purple press Eject 5 times (this puts the cursor to the gamepad sync row) then power to accept - observe the light then key in to your gamepad