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;
}