83155f32bf
Change-Id: I21b61a3f56d718bef3aa0cf5096359c463c1f93a
373 lines
11 KiB
C
373 lines
11 KiB
C
/***************************************************************************
|
|
* __________ __ ___.
|
|
* Open \______ \ ____ ____ | | _\_ |__ _______ ___
|
|
* Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
|
|
* Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
|
|
* Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
|
|
* \/ \/ \/ \/ \/
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2015 by Amaury Pouly
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
|
|
* KIND, either express or implied.
|
|
*
|
|
****************************************************************************/
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <stdint.h>
|
|
#include <libusb.h>
|
|
#include <getopt.h>
|
|
#include <stdbool.h>
|
|
#include <unistd.h>
|
|
|
|
#define VR_GET_CPU_INFO 0
|
|
#define VR_SET_DATA_ADDRESS 1
|
|
#define VR_SET_DATA_LENGTH 2
|
|
#define VR_FLUSH_CACHES 3
|
|
#define VR_PROGRAM_START1 4
|
|
#define VR_PROGRAM_START2 5
|
|
|
|
bool g_verbose = false;
|
|
|
|
int jz_cpuinfo(libusb_device_handle *dev)
|
|
{
|
|
if(g_verbose)
|
|
printf("Get CPU Info...\n");
|
|
uint8_t cpuinfo[9];
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_GET_CPU_INFO, 0, 0, cpuinfo, 8, 1000);
|
|
if(ret != 8)
|
|
{
|
|
printf("Cannot get CPU info: %d\n", ret);
|
|
return ret;
|
|
}
|
|
cpuinfo[8] = 0;
|
|
printf("CPU Info: %s\n", cpuinfo);
|
|
return 0;
|
|
}
|
|
|
|
int jz_set_addr(libusb_device_handle *dev, unsigned long addr)
|
|
{
|
|
if(g_verbose)
|
|
printf("Set address to 0x%lx...\n", addr);
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_SET_DATA_ADDRESS, addr >> 16, addr & 0xffff, NULL, 0, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot set address: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int jz_set_length(libusb_device_handle *dev, unsigned long length)
|
|
{
|
|
if(g_verbose)
|
|
printf("Set length to 0x%lx...\n", length);
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_SET_DATA_LENGTH, length >> 16, length & 0xffff, NULL, 0, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot set length: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int jz_start1(libusb_device_handle *dev, unsigned long addr)
|
|
{
|
|
if(g_verbose)
|
|
printf("Start 1 at 0x%lx...\n", addr);
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_PROGRAM_START1, addr >> 16, addr & 0xffff, NULL, 0, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot start1: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int jz_start2(libusb_device_handle *dev, unsigned long addr)
|
|
{
|
|
if(g_verbose)
|
|
printf("Start 2 at 0x%lx...\n", addr);
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_PROGRAM_START2, addr >> 16, addr & 0xffff, NULL, 0, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot start2: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int jz_flush_caches(libusb_device_handle *dev)
|
|
{
|
|
if(g_verbose)
|
|
printf("Flush caches...\n");
|
|
int ret = libusb_control_transfer(dev,
|
|
LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
|
|
VR_FLUSH_CACHES, 0, 0, NULL, 0, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot flush caches: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
int jz_upload(libusb_device_handle *dev, const char *file, unsigned long length)
|
|
{
|
|
if(g_verbose)
|
|
printf("Upload %lu bytes...\n", length);
|
|
void *data = malloc(length);
|
|
int xfered;
|
|
int ret = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_IN | 1, data, length,
|
|
&xfered, 10000);
|
|
if(ret != 0)
|
|
printf("Cannot upload data from device: %d\n", ret);
|
|
if(ret == 0 && xfered != length)
|
|
{
|
|
printf("Device did not send all the data\n");
|
|
ret = -1;
|
|
}
|
|
if(ret == 0)
|
|
{
|
|
FILE *f = fopen(file, "wb");
|
|
if(f != NULL)
|
|
{
|
|
if(fwrite(data, length, 1, f) != 1)
|
|
{
|
|
printf("Cannot write file\n");
|
|
ret = -3;
|
|
}
|
|
fclose(f);
|
|
}
|
|
else
|
|
{
|
|
printf("Cannot open file for writing\n");
|
|
ret = -2;
|
|
}
|
|
}
|
|
free(data);
|
|
return ret;
|
|
}
|
|
|
|
int jz_download(libusb_device_handle *dev, const char *file)
|
|
{
|
|
FILE *f = fopen(file, "rb");
|
|
if(f == NULL)
|
|
{
|
|
printf("Cannot open file for reading\n");
|
|
return -1;
|
|
}
|
|
fseek(f, 0, SEEK_END);
|
|
size_t length = ftell(f);
|
|
fseek(f, 0, SEEK_SET);
|
|
if(g_verbose)
|
|
printf("Download %lu bytes..\n", length);
|
|
void *data = malloc(length);
|
|
if(fread(data, length, 1, f) != 1)
|
|
{
|
|
printf("Cannot read file\n");
|
|
free(data);
|
|
fclose(f);
|
|
return -1;
|
|
}
|
|
fclose(f);
|
|
int xfered;
|
|
int ret = libusb_bulk_transfer(dev, LIBUSB_ENDPOINT_OUT | 1, data, length,
|
|
&xfered, 1000);
|
|
if(ret != 0)
|
|
printf("Cannot download data from device: %d\n", ret);
|
|
if(ret == 0 && xfered != length)
|
|
{
|
|
printf("Device did not receive all the data\n");
|
|
ret = -1;
|
|
}
|
|
free(data);
|
|
return ret;
|
|
}
|
|
|
|
int renumerate(libusb_device_handle **dev)
|
|
{
|
|
if(g_verbose)
|
|
printf("Look for device again...\n");
|
|
libusb_close(*dev);
|
|
*dev = libusb_open_device_with_vid_pid(NULL, 0x601a, 0x4760);
|
|
if(dev == NULL)
|
|
{
|
|
printf("Cannot open device\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int jz_stage1(libusb_device_handle *dev, unsigned long addr, const char *file)
|
|
{
|
|
int ret = jz_set_addr(dev, addr);
|
|
if(ret != 0)
|
|
return ret;
|
|
ret = jz_download(dev, file);
|
|
if(ret != 0)
|
|
return ret;
|
|
return jz_start1(dev, addr);
|
|
}
|
|
|
|
void usage()
|
|
{
|
|
printf("Usage: usbboot [options]\n");
|
|
printf("\n");
|
|
printf("Basic options:\n");
|
|
printf(" --stage1 <file> Upload first stage program (<=16Kio)\n");
|
|
printf(" --s1-addr <addr> Change first stage address (default is 0x80000000)\n");
|
|
printf(" --stage2 <file> Upload second stage program to SDRAM\n");
|
|
printf(" --s2-addr <addr> Change second stage address (default is 0x80000000)\n");
|
|
printf(" --ram <target> Setup SDRAM for <target>, see list below\n");
|
|
printf("\n");
|
|
printf("Advanced options:\n");
|
|
printf(" --addr <addr> Set address for next operation\n");
|
|
printf(" --length <len> Set length for next operation\n");
|
|
printf(" --download <file> Download data in file to the device (use file length)\n");
|
|
printf(" --upload <file> Upload data from the device to the file\n");
|
|
printf(" --cpuinfo Print CPU info\n");
|
|
printf(" --flush-caches Flush CPU caches\n");
|
|
printf(" --start1 <addr> Execute first stage from I-cache\n");
|
|
printf(" --start2 <addr> Execute second stage\n");
|
|
printf(" --wait <time> Wait <time> seconds\n");
|
|
printf(" --renumerate Try to look for device again\n");
|
|
printf(" --ram <ramopt> Setup SDRAM with parameters, see descrition below\n");
|
|
printf(" -v Be verbose\n");
|
|
printf("\n");
|
|
printf("Targets for RAM setup:\n");
|
|
printf(" fiiox1 Use <>\n");
|
|
printf("\n");
|
|
printf("Format of <ramopt> for RAM setup:\n");
|
|
exit(1);
|
|
}
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
if(argc <= 1)
|
|
usage();
|
|
int ret = 0;
|
|
libusb_init(NULL);
|
|
libusb_device_handle *dev = libusb_open_device_with_vid_pid(NULL, 0x601a, 0x4760);
|
|
if(dev == NULL)
|
|
{
|
|
printf("Cannot open device\n");
|
|
return -1;
|
|
}
|
|
if(libusb_claim_interface(dev, 0) != 0)
|
|
{
|
|
printf("Cannot claim interface\n");
|
|
libusb_close(dev);
|
|
return -2;
|
|
}
|
|
|
|
enum
|
|
{
|
|
OPT_ADDR = 0x100, OPT_LENGTH, OPT_UPLOAD, OPT_CPUINFO, OPT_DOWNLOAD,
|
|
OPT_START1, OPT_WAIT, OPT_RENUMERATE, OPT_START2, OPT_FLUSH_CACHES,
|
|
OPT_S1_ADDR, OPT_STAGE1
|
|
};
|
|
unsigned long last_length = 0;
|
|
unsigned long s1_addr = 0x80000000;
|
|
while(1)
|
|
{
|
|
static struct option long_options[] =
|
|
{
|
|
{"help", no_argument, 0, 'h'},
|
|
{"cpuinfo", no_argument, 0, OPT_CPUINFO},
|
|
{"addr", required_argument, 0, OPT_ADDR},
|
|
{"length", required_argument, 0, OPT_LENGTH},
|
|
{"upload", required_argument, 0, OPT_UPLOAD},
|
|
{"download", required_argument, 0, OPT_DOWNLOAD},
|
|
{"start1", required_argument, 0, OPT_START1},
|
|
{"wait", required_argument, 0, OPT_WAIT},
|
|
{"renumerate", no_argument, 0, OPT_RENUMERATE},
|
|
{"start2", required_argument, 0, OPT_START2},
|
|
{"flush-caches", no_argument, 0, OPT_FLUSH_CACHES},
|
|
{"s1-addr", required_argument, 0, OPT_S1_ADDR},
|
|
{"stage1", required_argument, 0, OPT_STAGE1},
|
|
{0, 0, 0, 0}
|
|
};
|
|
|
|
int c = getopt_long(argc, argv, "hv", long_options, NULL);
|
|
char *end = 0;
|
|
unsigned long param;
|
|
if(c == OPT_ADDR || c == OPT_LENGTH || c == OPT_START1 || c == OPT_WAIT
|
|
|| c == OPT_S1_ADDR)
|
|
{
|
|
param = strtoul(optarg, &end, 0);
|
|
if(*end)
|
|
{
|
|
printf("Invalid argument '%s'\n", optarg);
|
|
ret = 1;
|
|
break;
|
|
}
|
|
}
|
|
if(c == -1)
|
|
break;
|
|
switch(c)
|
|
{
|
|
default:
|
|
case -1:
|
|
break;
|
|
case 'h':
|
|
usage();
|
|
break;
|
|
case 'v':
|
|
g_verbose = true;
|
|
break;
|
|
case OPT_ADDR:
|
|
ret = jz_set_addr(dev, param);
|
|
break;
|
|
case OPT_LENGTH:
|
|
last_length = param;
|
|
ret = jz_set_length(dev, param);
|
|
break;
|
|
case OPT_UPLOAD:
|
|
ret = jz_upload(dev, optarg, last_length);
|
|
break;
|
|
case OPT_DOWNLOAD:
|
|
ret = jz_download(dev, optarg);
|
|
break;
|
|
case OPT_CPUINFO:
|
|
ret = jz_cpuinfo(dev);
|
|
break;
|
|
case OPT_START1:
|
|
ret = jz_start1(dev, param);
|
|
break;
|
|
case OPT_WAIT:
|
|
if(g_verbose)
|
|
printf("Wait for %lu seconds...\n", param);
|
|
sleep(param);
|
|
break;
|
|
case OPT_RENUMERATE:
|
|
ret = renumerate(&dev);
|
|
break;
|
|
case OPT_START2:
|
|
ret = jz_start2(dev, param);
|
|
break;
|
|
case OPT_FLUSH_CACHES:
|
|
ret = jz_flush_caches(dev);
|
|
break;
|
|
case OPT_S1_ADDR:
|
|
s1_addr = param;
|
|
break;
|
|
case OPT_STAGE1:
|
|
ret = jz_stage1(dev, s1_addr, optarg);
|
|
break;
|
|
}
|
|
if(ret != 0)
|
|
break;
|
|
}
|
|
if(optind != argc)
|
|
{
|
|
printf("Error: extra arguments on command line\n");
|
|
ret = 1;
|
|
}
|
|
|
|
libusb_close(dev);
|
|
libusb_exit(NULL);
|
|
return ret;
|
|
}
|