/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2021 Aidan MacDonald * * Directly adapted from jz4760_tools/usbboot.c, * 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 #include #include #include #include #include #include #include #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 /* Global variables */ bool g_verbose = false; libusb_device_handle* g_usb_dev = NULL; int g_vid = 0, g_pid = 0; /* Utility functions */ void die(const char* msg, ...) { va_list ap; va_start(ap, msg); vfprintf(stderr, msg, ap); fprintf(stderr, "\n"); va_end(ap); exit(1); } void verbose(const char* msg, ...) { if(!g_verbose) return; va_list ap; va_start(ap, msg); vprintf(msg, ap); printf("\n"); va_end(ap); } void open_usb(void) { if(g_usb_dev) { verbose("Closing USB device"); libusb_close(g_usb_dev); } if(g_vid == 0 || g_pid == 0) die("Can't open USB device: vendor/product ID not specified"); verbose("Opening USB device %04x:%04x", g_vid, g_pid); g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid); if(!g_usb_dev) die("Could not open USB device"); int ret = libusb_claim_interface(g_usb_dev, 0); if(ret != 0) { libusb_close(g_usb_dev); die("Could not claim interface: %d", ret); } } void ensure_usb(void) { if(!g_usb_dev) open_usb(); } /* USB communication functions */ void jz_get_cpu_info(void) { ensure_usb(); verbose("Issue GET_CPU_INFO"); uint8_t buf[9]; int ret = libusb_control_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, VR_GET_CPU_INFO, 0, 0, buf, 8, 1000); if(ret != 0) die("Can't get CPU info: %d", ret); buf[8] = 0; printf("CPU info: %s\n", buf); } void jz_upload(const char* filename, int length) { if(length < 0) die("invalid upload length: %d", length); ensure_usb(); verbose("Transfer %d bytes from device to host", length); void* data = malloc(length); int xfered = 0; int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1, data, length, &xfered, 10000); if(ret != 0) die("Transfer failed: %d", ret); if(xfered != length) die("Transfer error: got %d bytes, expected %d", xfered, length); FILE* f = fopen(filename, "wb"); if(f == NULL) die("Can't open file '%s' for writing", filename); if(fwrite(data, length, 1, f) != 1) die("Error writing transfered data to file"); fclose(f); free(data); } void jz_download(const char* filename) { FILE* f = fopen(filename, "rb"); if(f == NULL) die("Can't open file '%s' for reading", filename); fseek(f, 0, SEEK_END); int length = ftell(f); fseek(f, 0, SEEK_SET); void* data = malloc(length); if(fread(data, length, 1, f) != 1) die("Error reading data from file"); fclose(f); verbose("Transfer %d bytes from host to device", length); int xfered = 0; int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1, data, length, &xfered, 10000); if(ret != 0) die("Transfer failed: %d", ret); if(xfered != length) die("Transfer error: %d bytes recieved, expected %d", xfered, length); free(data); } #define jz_vendor_out_func(name, type, fmt) \ void name(unsigned long param) { \ ensure_usb(); \ verbose("Issue " #type fmt, param); \ int ret = libusb_control_transfer(g_usb_dev, \ LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, \ VR_##type, param >> 16, param & 0xffff, NULL, 0, 1000); \ if(ret != 0) \ die("Request " #type " failed: %d", ret); \ } jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx") jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx") jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "") jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx") jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx") #define jz_flush_caches() _jz_flush_caches(0) /* Default settings */ struct cpu_profile { const char* name; int vid, pid; unsigned long s1_load_addr, s1_exec_addr; unsigned long s2_load_addr, s2_exec_addr; }; static const struct cpu_profile cpu_profiles[] = { {"x1000", 0xa108, 0x1000, 0xf4001000, 0xf4001800, 0x80004000, 0x80004000}, {"jz4760", 0x601a, 0x4760, 0x80000000, 0x80000000, 0x80000000, 0x80000000}, {NULL} }; /* Simple "download and run" functions for dev purposes */ unsigned long s1_load_addr = 0, s1_exec_addr = 0; unsigned long s2_load_addr = 0, s2_exec_addr = 0; void apply_cpu_profile(const char* name) { const struct cpu_profile* p = &cpu_profiles[0]; for(p = &cpu_profiles[0]; p->name != NULL; ++p) { if(strcmp(p->name, name) != 0) continue; g_vid = p->vid; g_pid = p->pid; s1_load_addr = p->s1_load_addr; s1_exec_addr = p->s1_exec_addr; s2_load_addr = p->s2_load_addr; s2_exec_addr = p->s2_exec_addr; return; } die("CPU '%s' not known", name); } void run_stage1(const char* filename) { if(s1_load_addr == 0 || s1_exec_addr == 0) die("No stage1 binary settings -- did you specify --cpu?"); jz_set_data_address(s1_load_addr); jz_download(filename); jz_program_start1(s1_exec_addr); } void run_stage2(const char* filename) { if(s2_load_addr == 0 || s2_exec_addr == 0) die("No stage2 binary settings -- did you specify --cpu?"); jz_set_data_address(s2_load_addr); jz_download(filename); jz_flush_caches(); jz_program_start2(s2_exec_addr); } /* Main functions */ void usage() { printf("\ Usage: usbboot [options]\n\ \n\ Basic options:\n\ --cpu Select device CPU type\n\ --stage1 Download and execute stage1 binary\n\ --stage2 Download and execute stage2 binary\n\ \n\ Advanced options:\n\ --vid Specify USB vendor ID\n\ --pid Specify USB product ID\n\ --cpuinfo Ask device for CPU info\n\ --addr Set data address\n\ --length Set data length\n\ --upload Transfer data from device (needs prior --length)\n\ --download Transfer data to device\n\ --start1 Execute stage1 code at address\n\ --start2 Execute stage2 code at address\n\ --flush-caches Flush device CPU caches\n\ --renumerate Close and re-open the USB device\n\ --wait