掌機 - GKD Pixel - C/C++ - USB Boot



main.c

#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <errno.h>
#include <getopt.h>
#include <unistd.h>
#include <libusb-1.0/libusb.h>
 
#define TIMEOUT_MS          5000
#define STAGE1_LOAD_ADDR    0x80000000
#define STAGE2_LOAD_ADDR    0x81000000
 
#define ARRAY_SIZE(x) (sizeof(x) ? sizeof(x) / sizeof((x)[0]) : 0)
 
enum commands {
    VR_GET_CPU_INFO = 0,
    VR_SET_DATA_ADDR,
    VR_SET_DATA_LEN,
    VR_FLUSH_CACHES,
    VR_START1,
    VR_START2,
};

static unsigned int stage1_load_addr = STAGE1_LOAD_ADDR;
static unsigned int stage2_load_addr = STAGE2_LOAD_ADDR;

static FILE *stage1 = NULL;
static FILE *stage2 = NULL;
static FILE *devicetree = NULL;

static int cmd_get_info(libusb_device_handle *hdl)
{
    int ret = 0;
    unsigned char info[8] = {0};

    ret = libusb_control_transfer(hdl, LIBUSB_ENDPOINT_IN |
        LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
        VR_GET_CPU_INFO, 0, 0, info, sizeof(info), TIMEOUT_MS);

    if (ret != sizeof(info)) {
        return -EIO;
    }
    return 0;
}

static int cmd_control(libusb_device_handle *hdl, uint32_t cmd, uint32_t attr)
{
    return libusb_control_transfer(hdl, LIBUSB_ENDPOINT_OUT |
        LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
        cmd, (attr >> 16) & 0xffff, attr & 0xffff,
        NULL, 0, TIMEOUT_MS);
}

static int cmd_load_data(libusb_device_handle *hdl, FILE *f, uint32_t addr, size_t *data_size)
{
    int ret = 0;
    int bytes_transferred = 0;
    size_t size = 0, to_read = 0, to_write = 0;
    unsigned char *data = NULL;
    char *ptr = NULL;

    fseek(f, 0, SEEK_END);
    size = ftell(f);
    fseek(f, 0, SEEK_SET);

    if (data_size) {
        *data_size = size;
    }

    data = malloc(size);
    if (!data) {
        return -ENOMEM;
    }

    ptr = (char *)data;
    to_read = size;
    do {
        size_t bytes_read = fread(ptr, 1, to_read, f);
        if (!bytes_read) {
            ret = -EIO;
            goto out_free;
        }

        ptr += bytes_read;
        to_read -= bytes_read;
    } while (to_read > 0);
 
    ret = cmd_control(hdl, VR_SET_DATA_LEN, size);
    if (ret) {
        goto out_free;
    }

    ret = cmd_control(hdl, VR_SET_DATA_ADDR, addr);
    if (ret) {
        goto out_free;
    }

    ptr = (char *)data;
    to_write = size;
    do {
        ret = libusb_bulk_transfer(hdl, LIBUSB_ENDPOINT_OUT | 1,
            ptr, (int)to_write,
            &bytes_transferred, TIMEOUT_MS);
        if (ret) {
            goto out_free;
        }

        to_write -= bytes_transferred;
        ptr += bytes_transferred;
    } while (to_write > 0);

    printf("Uploaded %lu bytes at address 0x%08x\n", size, addr);

out_free:
    free(data);
    return ret;
}

int main(int argc, char **argv)
{
    char *end = NULL;
    int ret = 0, c = 0;
    size_t kernel_size = 0;
    unsigned int i = 0, j = 0;
    libusb_context *usb_ctx = NULL;
    libusb_device_handle *hdl = NULL;

    if (argc == 1) {
        printf("usage: %s stage1 stage2 devicetree\n", argv[0]);
        return -1;
    }

    stage1 = fopen(argv[1], "rb");
    if (!stage1) {
        printf("Failed to open %s file\n", argv[1]);
        goto Err;
    }
    if (argc > 2) {
        stage2 = fopen(argv[2], "rb");
    }
    if (argc > 3) {
        devicetree = fopen(argv[3], "rb");
    }

    ret = libusb_init(&usb_ctx);
    if (ret) {
        printf("Failed to init libusb\n");
        goto Err;
    }

    hdl = libusb_open_device_with_vid_pid(usb_ctx, 0xa108, 0xc309);
    if (!hdl) {
        printf("Failed to open GKD Pixel\n");
        goto Err;
    }

    ret = libusb_claim_interface(hdl, 0);
    if (ret) {
        printf("Failed to claim interface 0\n");
        goto Err;
    }

    if (cmd_get_info(hdl)) {
        printf("Failed to read CPU info\n");
        goto Err;
    } 
    printf("Found GKD Pixel\n");

    ret = cmd_load_data(hdl, stage1, stage1_load_addr, NULL);
    if (ret) {
        printf("Failed to upload stage1 file\n");
        goto Err;
    }

    ret = cmd_control(hdl, VR_START1, stage1_load_addr);
    if (ret) {
        printf("Failed to execute stage1 bootloader\n");
        goto Err;
    }
    printf("Run stage1 at 0x%08x\n", stage1_load_addr);

    if (!stage2) {
        goto Err;
    }

    printf("Waiting for stage1 bootloader to complete operation...\n");
    for (i = 0; i < 100; i++) {
        if (!cmd_get_info(hdl)) {
            break;
        }
        usleep(10000);
    }

    if (i == 100) {
        printf("Stage1 bootloader did not return\n");
        goto Err;
    }

    ret = cmd_load_data(hdl, stage2, stage2_load_addr, &kernel_size);
    if (ret) {
        printf("Failed to upload stage2 program\n");
        goto Err;
    }

    if (devicetree) {
        ret = cmd_load_data(hdl, devicetree, stage2_load_addr + kernel_size, NULL);
        if (ret) {
            printf("Failed to upload devicetree\n");
            goto Err;
        }
    }

    ret = cmd_control(hdl, VR_FLUSH_CACHES, 0);
    if (ret) {
        printf("Failed to flush caches\n");
        goto Err;
    }
 
    ret = cmd_control(hdl, VR_START2, stage2_load_addr);
    if (ret) {
        printf("Failed to execute program\n");
        goto Err;
    }
    printf("Run stage2 at 0x%08x\n", stage1_load_addr);

Err:
    if (hdl) { 
        libusb_close(hdl);
    }
    if (usb_ctx) {
        libusb_exit(usb_ctx);
    }
    if (stage1) {
        fclose(stage1);
    }
    if (stage2) {
        fclose(stage2);
    }
    if (devicetree) {
        fclose(devicetree);
    }
    return 0;
}