dekuNukem / Nintendo_Switch_Reverse_Engineering

A look at inner workings of Joycon and Nintendo Switch
3.45k stars 194 forks source link

USB Spi Read #45

Open greasysock opened 7 years ago

greasysock commented 7 years ago

I'm trying to read from SPI via USB, but all I ever am returned is "FF" of the length of the request.

81 92 00 92 00 31 00 00 6d 55 21 60 81 00 80 00 aa 97 77 18 18 81 00 90 10 26 80 00 00 0f ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff

Here is an example response.

Does anybody know what I should be doing differently? I'm using the subcommand 10 to read from spi

CTCaer commented 7 years ago

Can you give an example of your full command?

EDIT: If you send 80 92 00 31 00 00 00 00 01 00 00 00 00 00 00 00 00 00 10 26 80 00 00 0f and you haven't manually calibrated the motion sensor before, then the response you get is correct and the x8026 data should be 0xFF.

Also the data you want to read should be x1a size. 2 bytes magic + 24bytes user sensor calibration.

Lastly, notice that the maximum size of data it can reply is 0x1D in BT. I never checked for USB, but from other users, it's probably x1B. You can test it by reading 0x0000 address and find out.

greasysock commented 7 years ago

Thank you for the thoughtful response and insight.

I did not realize that I had not calibrated this device before. However, even trying to read the serial for the controller I am given the same response.

here is a better look at what I am sending and receiving for the serial number.

Sent

80 92 00 31 00 00 00 00 01 04 00 01 40 40 00 01 40 40 10 02 60 00 00 0e

80 92 00 31 00 00 00 00 01 05 00 01 40 40 00 01 40 40 10 02 60 00 00 0e

Received

81 92 00 92 00 31 00 00 bc 77 21 6a 81 00 80 00 b6 67 7a 10 78 7f 00 90 10 02 60 00 00 0e ff ff ff ff ff ff ff ff ff ff ff ff ff ff

So now looking at this, it appears that I am sending the command twice in the function spi_read.

Below are the functions that are used to send subcommands to the device and to read from SPI, taken from the HID-Joy-Con-Whispering project.

void joycon_send_command(hid_device *handle, int command, uint8_t *data, int len)
{
    unsigned char buf[0x400];
    memset(buf, 0, 0x400);

    if(!bluetooth)
    {
        buf[0x00] = 0x80;
        buf[0x01] = 0x92;
        buf[0x03] = 0x31;
    }

    buf[bluetooth ? 0x0 : 0x8] = command;
    if(data != NULL && len != 0)
        memcpy(buf + (bluetooth ? 0x1 : 0x9), data, len);
    printf("command: ");
    hex_dump_line(buf, 40);
    printf("\n");
    hid_exchange(handle, buf, len + (bluetooth ? 0x1 : 0x9));

    if(data)
        memcpy(data, buf, 0x40);
}

void joycon_send_subcommand(hid_device *handle, int command, int subcommand, uint8_t *data, int len)
{
    unsigned char buf[0x400];
    memset(buf, 0, 0x400);

    uint8_t rumble_base[9] = {(++global_count) & 0xF, 0x00, 0x01, 0x40, 0x40, 0x00, 0x01, 0x40, 0x40};
    memcpy(buf, rumble_base, 9);

    buf[9] = subcommand;
    if(data && len != 0)
        memcpy(buf + 10, data, len);

    joycon_send_command(handle, command, buf, 10 + len);

    if(data)
        memcpy(data, buf, 0x40); //TODO
}

void spi_read(hid_device *handle, uint32_t offs, uint8_t *data, uint8_t len)
{
    unsigned char buf[0x400];
    uint8_t *spi_read_cmd = (uint8_t*)calloc(1, 0x26 * sizeof(uint8_t));
    uint32_t* offset = (uint32_t*)(&spi_read_cmd[0]);
    uint8_t* length = (uint8_t*)(&spi_read_cmd[4]);

    *length = len;
    *offset = offs;

    int max_read_count = 2000;
    int read_count = 0;
    do
    {
        //usleep(300000);
        read_count += 1;
        memcpy(buf, spi_read_cmd, 0x36);
        joycon_send_subcommand(handle, 0x1, 0x10, buf, 0x26);
    }
    while(*(uint32_t*)&buf[0xF + (bluetooth ? 0 : 10)] != *offset && read_count < max_read_count);
    if(read_count > max_read_count)
        printf("ERROR: Read error or timeout\nSkipped reading of %dBytes at address 0x%05X...\n", 
            *length, *offset);

    printf("SERIAL: \n");
    hex_dump_line(buf, 0x40);
    printf("\n");

    memcpy(data, &buf[0x14 + (bluetooth ? 0 : 10)], len);
}
CTCaer commented 7 years ago

Pro controller maybe? Pro controllers do not have S/N and they are 0xFF. Additionally, your command and the reply structure are absolutely correct.

I would suggest to first dump the whole SPI of your device and then check if your individual spi read commands work and you got the correct data.

Also, read the documentation here to know what to expect and what not in each SPI address.

Lastly, if you want to experiment with USB commands other than x80, I can provide you the full list (not only what you can see here).

greasysock commented 7 years ago

Yes, it is a pro controller. I would really appreciate seeing the full command list for USB.

greasysock commented 7 years ago

Asking for the device model gave me the right response from SPI.

Davidobot commented 6 years ago

I am having the same problems as greasysock, whereby reading SPI data from USB gives the wrong values.

If a solution was found, could you post it?

CTCaer commented 6 years ago

@Davidobot What do you send and what do you get?

Davidobot commented 6 years ago

Doing ReadSPI(0x80, 0x12, 9); to read from 0x8012 and get the user stick calibration data would hang the code for a while and then just output a completely zero array. I use the same implementation of the spi read function as shown further up in this thread.

If the function doesn't hang, it just outputs an array of FFs the length of the requested read.

CTCaer commented 6 years ago

The hanging comes from the retries. That's because in USB mode the x30 input report mode is enabled by default. So the code expects a x21 but also gets x30 reports. This can be mitigated by rejecting the x31 report around 8 times and then retry to send the command again.

To do this, you need to change the joycon_send_subcommand() to identify the x10 subcmd and check for x21 inputreports in hid_read(). And better change the hid_read to hid_read_timeout with 100ms timeout.

About the xFF problem, as I said before, this section is empty (all 0xFF) if the controller was never manually calibrated. You said in the email that everything works as it should in BT mode, so I assume you have calibration data there. Then what happens is that you reach max_read_count.

So, if max_read_count is the problem, it will probably be fixed if you implement the x30 reports reject mechanism above.

Davidobot commented 6 years ago

Thanks for your help! Setting the input mode to 0x3F (before dumping the spi data) and then back to 0x30 solved the problem!