summaryrefslogtreecommitdiff
path: root/rbutil
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-05-11 13:25:26 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-05-12 10:35:20 +0000
commitcc22df198d0ccb64dfdfe0c2f247f7d86b7fd750 (patch)
treee6cd8aaa33c161bc0f72a9b35e763372b20f910d /rbutil
parent3748117ee3623437d63ba2016637fd532dbdd4b9 (diff)
jztool: Support new M3K bootloader
Change-Id: Ia2d96893a9a5c77deb71c1fe32ae5a0585093f5b
Diffstat (limited to 'rbutil')
-rw-r--r--rbutil/jztool/Makefile8
-rw-r--r--rbutil/jztool/README.md118
-rw-r--r--rbutil/jztool/include/jztool.h37
-rw-r--r--rbutil/jztool/jztool.c162
-rw-r--r--rbutil/jztool/src/context.c10
-rw-r--r--rbutil/jztool/src/device_info.c29
-rw-r--r--rbutil/jztool/src/fiiom3k.c373
-rw-r--r--rbutil/jztool/src/identify_file.c50
-rw-r--r--rbutil/jztool/src/paramlist.c135
-rw-r--r--rbutil/jztool/src/usb.c64
-rw-r--r--rbutil/jztool/src/x1000.c209
-rw-r--r--rbutil/libtools.make6
12 files changed, 433 insertions, 768 deletions
diff --git a/rbutil/jztool/Makefile b/rbutil/jztool/Makefile
index 6ab990d9d8..bc2724ef59 100644
--- a/rbutil/jztool/Makefile
+++ b/rbutil/jztool/Makefile
@@ -5,7 +5,7 @@
# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
# \/ \/ \/ \/ \/
-CFLAGS += -Wall -Wextra -Iinclude
+CFLAGS += -Wall -Wextra -Iinclude -I../../tools/ucl/include -I../../lib/microtar/src
OUTPUT = jztool
ifdef RELEASE
@@ -15,10 +15,9 @@ CFLAGS += -O0 -ggdb
endif
LIBSOURCES := src/buffer.c src/context.c src/device_info.c \
- src/fiiom3k.c src/identify_file.c src/paramlist.c \
- src/usb.c src/x1000.c
+ src/identify_file.c src/fiiom3k.c src/usb.c
SOURCES := $(LIBSOURCES) jztool.c
-EXTRADEPS :=
+EXTRADEPS := libucl.a libmicrotar.a
CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -)
@@ -27,6 +26,7 @@ ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32)
else
ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE)
# OSX -- /opt location is cheesy attempt to support ARM macs
+# COMPLETELY UNTESTED, testing from someone with an actual Mac is appreciated!
CFLAGS += -I/usr/local/include -I/opt/homebrew/include
LDOPTS += -L/usr/local/lib -L/opt/homebrew/lib -lusb-1.0
else
diff --git a/rbutil/jztool/README.md b/rbutil/jztool/README.md
index 6a9b78f8d7..2c4dd4992c 100644
--- a/rbutil/jztool/README.md
+++ b/rbutil/jztool/README.md
@@ -1,28 +1,90 @@
# jztool -- Ingenic device utility & bootloader installer
-The `jztool` utility can install, backup, and restore the bootloader on
+The `jztool` utility can help install, backup, and restore the bootloader on
Rockbox players based on a supported Ingenic SoC.
## FiiO M3K
-To use `jztool` on the FiiO M3K you have to connect the player to your
-computer in USB boot mode.
+First, get a copy of the `bootloader.m3k` file, either by downloading it
+from <https://rockbox.org>, or by compiling it yourself (choose 'B'ootloader
+build when configuring your build).
-The easiest way to do this is by plugging in the microUSB cable to the M3K
-and holding the volume down button while plugging the USB into your computer.
-If you entered USB boot mode, the button light will turn on but the LCD will
-turn off.
+The first time you install Rockbox, you need to load the Rockbox bootloader
+over USB by entering USB boot mode. The easiest way to do this is by plugging
+in the microUSB cable to the M3K and holding the VOL- button while plugging
+the USB into your computer. If you entered USB boot mode, the button light
+will turn on but the LCD will remain black.
-To install or update the Rockbox bootloader on the M3K, use the command
-`jztool fiiom3k install`. It is recommended that you take a backup of your
-current bootloader so you can restore it in case of any problems.
+Copy the `bootloader.m3k` next to the `jztool` executable and follow the
+instructions below which are appropriate to your OS.
-After any operation finishes, you will have to force a power off of the M3K
-by holding down the power button for at least 10 seconds. This must be done
-whether the operation succeeds or fails. Just don't power off or unplug the
-device in the middle of an operation -- that might make bad things happen.
+### Running jztool
+
+#### Linux/Mac
+
+Run the following command in a terminal. Note that on Linux, you will need to
+have root access to allow libusb to access the USB device.
+
+```sh
+# Linux / Mac
+# NOTE: root permissions are required on Linux to access the USB device
+# eg. with 'sudo' or 'su -c' depending on your distro.
+$ ./jztool fiiom3k load bootloader.m3k
+```
+
+#### Windows
+
+To allow `jztool` access to the M3K in USB boot mode, you need to install
+the WinUSB driver. The recommended way to install it is using Zadig, which
+may be downloaded from its homepage <https://zadig.akeo.ie>. Please note
+this is 3rd party software not maintained or supported by Rockbox developers.
+(Zadig will require administrator access on the machine you are using.)
+
+When running Zadig you must select the WinUSB driver; the other driver options
+will not work properly with `jztool`. You will have to select the correct USB
+device in Zadig -- the name and USB IDs of the M3K in USB boot mode are listed
+below. NOTE: the device name may show only as "X" and a hollow square in Zadig.
+The IDs will not change, so those are the most reliable way to confirm you have
+selected the correct device.
+
+```
+Name: Ingenic Semiconductor Co.,Ltd X1000
+USB ID: A108 1000
+```
+
+Assuming you installed the WinUSB driver successfully, open a command prompt
+in the folder containing `jztool`. Administrator access is not required for
+this step.
+
+Type the following command to load the Rockbox bootloader:
+
+```sh
+# Windows
+$ jztool.exe fiiom3k load bootloader.m3k
+```
+
+### Further instructions
+
+After running `jztool` successfully your M3K will display the recovery menu
+of the Rockbox bootloader. If you want to permanently install Rockbox to your
+M3K, copy `bootloader.m3k` to the root of an SD card, insert it to your device,
+then choose "Install/update bootloader" from the menu.
+
+It is _highly_ recommended that you take a backup of your existing bootloader
+in case of any trouble -- choose "Backup bootloader" from the recovery menu.
+The backup file is called "fiiom3k-boot.bin" and will be saved to the root of
+the SD card. If you need to restore it, simply place the file at the root of
+your SD card and select "Restore bootloader".
+
+In the future if you want to backup, restore, or update the bootloader, you
+can access the Rockbox bootloader's recovery menu by holding VOL+ when booting.
+
+### Known issues
+
+- When using the bootloader's USB mode, you may get stuck on "Waiting for USB"
+ even though the cable is already plugged in. If this occurs, unplug the USB
+ cable and plug it back in to trigger the connection.
-See `jztool --help` for info.
## TODO list
@@ -38,23 +100,11 @@ Some of the error messages could be friendlier too.
Adding support to the Rockbox utility should be mostly boilerplate since the
jztool library wraps all the troublesome details.
-Getting appropriate privileges to access the USB device is the main issue.
-Preferably, the Rockbox utility should not run as root/admin/etc.
-
-- Windows: not sure
-- Linux: needs udev rules or root privileges
-- Mac: apparently does not need privileges
-
-### Porting to Windows
-
-Windows wants to see a driver installed before we can access the USB device,
-the easiest way to do this is by having the user run Zadig, a 3rd party app
-which can install the WinUSB driver. WinUSB itself is from Microsoft and
-bundled with Windows.
-
-Zadig's homepage: https://zadig.akeo.ie/
-
-### Porting to Mac
+Permissions are an issue on Linux because by default only root can access
+"raw" USB devices. If we want to package rbutil for distro we can install
+a udev rule to allow access to the specific USB IDs we need, eg. allowing
+users in the "wheel" group to access the device.
-According to the libusb wiki, libusb works on Mac without any special setup or
-privileges, presumably porting there is easy.
+On Windows and Mac, no special permissions are needed to access USB devices
+assuming the drivers are set up. (Zadig does require administrator access
+to run, but that's external to the Rockbox utility.)
diff --git a/rbutil/jztool/include/jztool.h b/rbutil/jztool/include/jztool.h
index e16bc9765f..df51fe9f44 100644
--- a/rbutil/jztool/include/jztool.h
+++ b/rbutil/jztool/include/jztool.h
@@ -37,7 +37,6 @@ typedef struct jz_context jz_context;
typedef struct jz_usbdev jz_usbdev;
typedef struct jz_device_info jz_device_info;
typedef struct jz_buffer jz_buffer;
-typedef struct jz_paramlist jz_paramlist;
typedef enum jz_error jz_error;
typedef enum jz_identify_error jz_identify_error;
@@ -46,7 +45,6 @@ typedef enum jz_device_type jz_device_type;
typedef enum jz_cpu_type jz_cpu_type;
typedef void(*jz_log_cb)(jz_log_level, const char*);
-typedef int(*jz_device_action_fn)(jz_context*, jz_paramlist*);
enum jz_error {
JZ_SUCCESS = 0,
@@ -92,10 +90,6 @@ struct jz_device_info {
jz_cpu_type cpu_type;
uint16_t vendor_id;
uint16_t product_id;
- int num_actions;
- const char* const* action_names;
- const jz_device_action_fn* action_funcs;
- const char* const* const* action_params;
};
struct jz_buffer {
@@ -132,7 +126,6 @@ const jz_device_info* jz_get_device_info_indexed(int index);
int jz_identify_x1000_spl(const void* data, size_t len);
int jz_identify_scramble_image(const void* data, size_t len);
-int jz_identify_fiiom3k_bootimage(const void* data, size_t len);
/******************************************************************************
* USB boot ROM protocol
@@ -148,27 +141,10 @@ int jz_usb_start2(jz_usbdev* dev, uint32_t addr);
int jz_usb_flush_caches(jz_usbdev* dev);
/******************************************************************************
- * X1000 flash protocol
+ * Rockbox loader (all functions are model-specific, see docs)
*/
-int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data);
-int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data);
-int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data);
-int jz_x1000_boot_rockbox(jz_usbdev* dev);
-
-/******************************************************************************
- * FiiO M3K bootloader backup/installation
- */
-
-int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr);
-int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf);
-int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size,
- const void* spl_buf, size_t spl_size,
- const void* boot_buf, size_t boot_size);
-
-int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl);
-int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl);
-int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl);
+int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename);
/******************************************************************************
* Simple buffer API
@@ -181,15 +157,6 @@ int jz_buffer_load(jz_buffer** buf, const char* filename);
int jz_buffer_save(jz_buffer* buf, const char* filename);
/******************************************************************************
- * Parameter list
- */
-
-jz_paramlist* jz_paramlist_new(void);
-void jz_paramlist_free(jz_paramlist* pl);
-int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value);
-const char* jz_paramlist_get(jz_paramlist* pl, const char* param);
-
-/******************************************************************************
* END
*/
diff --git a/rbutil/jztool/jztool.c b/rbutil/jztool/jztool.c
index 5aae8d7457..5fb6dc173f 100644
--- a/rbutil/jztool/jztool.c
+++ b/rbutil/jztool/jztool.c
@@ -26,21 +26,68 @@
#include <stdbool.h>
jz_context* jz = NULL;
+jz_usbdev* usbdev = NULL;
const jz_device_info* dev_info = NULL;
-int dev_action = -1;
-jz_paramlist* action_params = NULL;
+
+void usage_fiiom3k(void)
+{
+ printf("Usage:\n"
+ " jztool fiiom3k load <bootloader.m3k>\n"
+ "\n"
+ "The 'load' command is used to boot the Rockbox bootloader in\n"
+ "recovery mode, which allows you to install the Rockbox bootloader\n"
+ "and backup or restore bootloader images. You need to connect the\n"
+ "M3K in USB boot mode in order to use this tool.\n"
+ "\n"
+ "On Windows, you will need to install the WinUSB driver for the M3K\n"
+ "using a 3rd-party tool such as Zadig <https://zadig.akeo.ie>. For\n"
+ "more details check the jztool README.md file or the Rockbox wiki at\n"
+ "<https://rockbox.org/wiki/FiioM3K>.\n"
+ "\n"
+ "To connect the M3K in USB boot mode, plug the microUSB into the\n"
+ "M3K, and hold the VOL- button while plugging the USB into your\n"
+ "computer. If successful, the button light will turn on and the\n"
+ "LCD will remain black. If you encounter any errors and need to\n"
+ "reconnect the device, you must force a power off by holding POWER\n"
+ "for more than 10 seconds.\n"
+ "\n"
+ "Once the Rockbox bootloader is installed on your M3K, you can\n"
+ "access the recovery menu by holding VOL+ while powering on the\n"
+ "device.\n");
+ exit(4);
+}
+
+int cmdline_fiiom3k(int argc, char** argv)
+{
+ if(argc < 2 || strcmp(argv[0], "load")) {
+ usage_fiiom3k();
+ return 2;
+ }
+
+ int rc = jz_usb_open(jz, &usbdev, dev_info->vendor_id, dev_info->product_id);
+ if(rc < 0) {
+ jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc);
+ return 1;
+ }
+
+ rc = jz_fiiom3k_boot(usbdev, argv[1]);
+ if(rc < 0) {
+ jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc);
+ return 1;
+ }
+
+ return 0;
+}
void usage(void)
{
printf("Usage:\n"
- " jztool [global options] <device> <action> [action options]\n"
+ " jztool [global options] <device> <command> [command arguments]\n"
"\n"
"Global options:\n"
- "\n"
" -h, --help Display this help\n"
" -q, --quiet Don't log anything except errors\n"
- " -v, --verbose Display detailed logging output\n"
- " -l, --loglevel LEVEL Set log level\n");
+ " -v, --verbose Display detailed logging output\n\n");
printf("Supported devices:\n\n");
int n = jz_get_num_device_info();
@@ -49,39 +96,17 @@ void usage(void)
printf(" %s - %s\n", info->name, info->description);
}
- printf(
-"\n"
-"Available actions for fiiom3k:\n"
-"\n"
-" install --spl <spl.m3k> --bootloader <bootloader.m3k>\n"
-" [--without-backup yes] [--backup IMAGE]\n"
-" Install or update the Rockbox bootloader on a device.\n"
-"\n"
-" If --backup is given, back up the current bootloader to IMAGE before\n"
-" installing the new bootloader. The installer will normally refuse to\n"
-" overwrite your current bootloader; pass '--without-backup yes' if you\n"
-" really want to proceed without taking a backup.\n"
-"\n"
-" WARNING: it is NOT RECOMMENDED to install the Rockbox bootloader\n"
-" without taking a backup of the original firmware bootloader. It may\n"
-" be very difficult or impossible to recover your player without one.\n"
-" At least one M3Ks is known to not to work with the Rockbox bootloader,\n"
-" so it is very important to take a backup.\n"
-"\n"
-" backup --image IMAGE\n"
-" Backup the current bootloader to the file IMAGE\n"
-"\n"
-" restore --image IMAGE\n"
-" Restore a bootloader image backup from the file IMAGE\n"
-"\n");
+ printf("\n"
+ "For device-specific help run 'jztool DEVICE' without arguments,\n"
+ "eg. 'jztool fiiom3k' will display help for the FiiO M3K.\n");
exit(4);
}
void cleanup(void)
{
- if(action_params)
- jz_paramlist_free(action_params);
+ if(usbdev)
+ jz_usb_close(usbdev);
if(jz)
jz_context_destroy(jz);
}
@@ -157,71 +182,14 @@ int main(int argc, char** argv)
exit(2);
}
- /* Read the action */
- --argc, ++argv;
- if(argc == 0) {
- jz_log(jz, JZ_LOG_ERROR, "No action specified (try jztool --help)");
- exit(2);
- }
-
- for(dev_action = 0; dev_action < dev_info->num_actions; ++dev_action)
- if(!strcmp(*argv, dev_info->action_names[dev_action]))
- break;
-
- if(dev_action == dev_info->num_actions) {
- jz_log(jz, JZ_LOG_ERROR, "Unknown action '%s' (try jztool --help)", *argv);
- exit(2);
- }
-
- /* Parse the action options */
- action_params = jz_paramlist_new();
- if(!action_params) {
- jz_log(jz, JZ_LOG_ERROR, "Out of memory: can't create paramlist");
- exit(1);
- }
-
- const char* const* allowed_params = dev_info->action_params[dev_action];
-
+ /* Dispatch to device handler */
--argc, ++argv;
- while(argc > 0 && argv[0][0] == '-') {
- if(argv[0][1] != '-') {
- jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv);
- exit(2);
- }
+ switch(dev_info->device_type) {
+ case JZ_DEVICE_FIIOM3K:
+ return cmdline_fiiom3k(argc, argv);
- bool bad_option = true;
- for(int i = 0; allowed_params[i] != NULL; ++i) {
- if(!strcmp(&argv[0][2], allowed_params[i])) {
- ++argv;
- if(--argc == 0) {
- jz_log(jz, JZ_LOG_ERROR, "Missing argument for parameter '%s'", *argv);
- exit(2);
- }
-
- int rc = jz_paramlist_set(action_params, allowed_params[i], *argv);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Out of memory");
- exit(1);
- }
-
- bad_option = false;
- }
- }
-
- if(bad_option) {
- jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv);
- exit(2);
- }
-
- --argc, ++argv;
- }
-
- if(argc != 0) {
- jz_log(jz, JZ_LOG_ERROR, "Excess arguments on command line");
- exit(2);
+ default:
+ jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type");
+ return 1;
}
-
- /* Invoke action handler */
- int rc = dev_info->action_funcs[dev_action](jz, action_params);
- return (rc < 0) ? 1 : 0;
}
diff --git a/rbutil/jztool/src/context.c b/rbutil/jztool/src/context.c
index 94b21b5196..d269d1eece 100644
--- a/rbutil/jztool/src/context.c
+++ b/rbutil/jztool/src/context.c
@@ -27,6 +27,10 @@
#include <stdio.h>
#include <time.h>
+#ifdef WIN32
+# include <windows.h>
+#endif
+
/** \brief Allocate a library context
* \returns New context or NULL if out of memory
*/
@@ -137,13 +141,18 @@ void jz_log_cb_stderr(jz_log_level lev, const char* msg)
*/
void jz_sleepms(int ms)
{
+#ifdef WIN32
+ Sleep(ms);
+#else
struct timespec ts;
long ns = ms % 1000;
ts.tv_nsec = ns * 1000 * 1000;
ts.tv_sec = ms / 1000;
nanosleep(&ts, NULL);
+#endif
}
+/** \brief Add reference to libusb context, allocating it if necessary */
int jz_context_ref_libusb(jz_context* jz)
{
if(jz->usb_ctxref == 0) {
@@ -158,6 +167,7 @@ int jz_context_ref_libusb(jz_context* jz)
return JZ_SUCCESS;
}
+/** \brief Remove reference to libusb context, freeing if it hits zero */
void jz_context_unref_libusb(jz_context* jz)
{
if(--jz->usb_ctxref == 0) {
diff --git a/rbutil/jztool/src/device_info.c b/rbutil/jztool/src/device_info.c
index bc1477be32..5ce3899262 100644
--- a/rbutil/jztool/src/device_info.c
+++ b/rbutil/jztool/src/device_info.c
@@ -22,30 +22,6 @@
#include "jztool.h"
#include <string.h>
-static const char* const fiiom3k_action_names[] = {
- "install",
- "backup",
- "restore",
-};
-
-static const char* const fiiom3k_install_action_params[] =
- {"spl", "bootloader", "backup", "without-backup", NULL};
-
-static const char* const fiiom3k_backuprestore_action_params[] =
- {"spl", "image", NULL};
-
-static const char* const* fiiom3k_action_params[] = {
- fiiom3k_install_action_params,
- fiiom3k_backuprestore_action_params,
- fiiom3k_backuprestore_action_params,
-};
-
-static const jz_device_action_fn fiiom3k_action_funcs[] = {
- jz_fiiom3k_install,
- jz_fiiom3k_backup,
- jz_fiiom3k_restore,
-};
-
static const jz_device_info infotable[] = {
{
.name = "fiiom3k",
@@ -54,10 +30,6 @@ static const jz_device_info infotable[] = {
.cpu_type = JZ_CPU_X1000,
.vendor_id = 0xa108,
.product_id = 0x1000,
- .num_actions = sizeof(fiiom3k_action_names)/sizeof(void*),
- .action_names = fiiom3k_action_names,
- .action_funcs = fiiom3k_action_funcs,
- .action_params = fiiom3k_action_params,
},
};
@@ -69,6 +41,7 @@ int jz_get_num_device_info(void)
return infotable_size;
}
+/** \brief Lookup info for a device by type, returns NULL if not found. */
const jz_device_info* jz_get_device_info(jz_device_type type)
{
for(int i = 0; i < infotable_size; ++i)
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/fiiom3k.c
index a43863c2f7..72e25a1220 100644
--- a/rbutil/jztool/src/fiiom3k.c
+++ b/rbutil/jztool/src/fiiom3k.c
@@ -20,264 +20,255 @@
****************************************************************************/
#include "jztool.h"
+#include "jztool_private.h"
+#include "microtar.h"
+#include "ucl/ucl.h"
+#include <stdbool.h>
#include <string.h>
-#define IMAGE_ADDR 0
-#define IMAGE_SIZE (128 * 1024)
-#define SPL_OFFSET 0
-#define SPL_SIZE (12 * 1024)
-#define BOOT_OFFSET (26 * 1024)
-#define BOOT_SIZE (102 * 1024)
-
-int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr)
+static uint32_t xread32(const uint8_t* d)
{
- jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL);
- if(!buf)
- return JZ_ERR_OUT_OF_MEMORY;
-
- int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data);
- if(rc < 0) {
- jz_buffer_free(buf);
- return rc;
- }
-
- *bufptr = buf;
- return JZ_SUCCESS;
+ uint32_t r = 0;
+ r |= d[0] << 24;
+ r |= d[1] << 16;
+ r |= d[2] << 8;
+ r |= d[3] << 0;
+ return r;
}
-int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf)
+/* adapted from firmware/common/ucl_decompress.c */
+static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len,
+ uint32_t* dst_len)
{
- int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size);
- if(rc < 0 || image_size != IMAGE_SIZE)
- return JZ_ERR_BAD_FILE_FORMAT;
+ static const uint8_t magic[8] =
+ {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a};
- rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf);
- if(rc < 0)
- return rc;
+ jz_buffer* buffer = NULL;
- return JZ_SUCCESS;
-}
+ /* make sure there are enough bytes for the header */
+ if(src_len < 18)
+ goto error;
-int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size,
- const void* spl_buf, size_t spl_size,
- const void* boot_buf, size_t boot_size)
-{
- int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Boot image is invalid: %d", rc);
- return JZ_ERR_BAD_FILE_FORMAT;
- }
+ /* avoid memcmp for reasons of code size */
+ for(size_t i = 0; i < sizeof(magic); ++i)
+ if(src[i] != magic[i])
+ goto error;
- rc = jz_identify_x1000_spl(spl_buf, spl_size);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc);
- return JZ_ERR_BAD_FILE_FORMAT;
- }
+ /* read the other header fields */
+ /* uint32_t flags = xread32(&src[8]); */
+ uint8_t method = src[12];
+ /* uint8_t level = src[13]; */
+ uint32_t block_size = xread32(&src[14]);
- if(spl_size > SPL_SIZE) {
- jz_log(jz, JZ_LOG_ERROR, "SPL is too big");
- return JZ_ERR_BAD_FILE_FORMAT;
- }
+ /* check supported compression method */
+ if(method != 0x2e)
+ goto error;
- rc = jz_identify_scramble_image(boot_buf, boot_size);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc);
- return JZ_ERR_BAD_FILE_FORMAT;
- }
+ /* validate */
+ if(block_size < 1024 || block_size > 8*1024*1024)
+ goto error;
- if(boot_size > BOOT_SIZE) {
- jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big");
- return JZ_ERR_BAD_FILE_FORMAT;
- }
+ src += 18;
+ src_len -= 18;
+
+ /* Calculate amount of space that we might need & allocate a buffer:
+ * - subtract 4 to account for end of file marker
+ * - each block is block_size bytes + 8 bytes of header
+ * - add one to nr_blocks to account for case where file size < block size
+ * - total size = max uncompressed size of block * nr_blocks
+ */
+ uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1;
+ uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256);
+ buffer = jz_buffer_alloc(max_size, NULL);
+ if(!buffer)
+ goto error;
- uint8_t* imgdat = (uint8_t*)image_buf;
- memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE);
- memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size);
- memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE);
- memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size);
- return JZ_SUCCESS;
-}
+ /* perform the decompression */
+ uint32_t dst_ilen = buffer->size;
+ uint8_t* dst = buffer->data;
+ while(1) {
+ if(src_len < 4)
+ goto error;
-#define IMGBUF 0
-#define SPLBUF 1
-#define BOOTBUF 2
-#define NUMBUFS 3
-#define IMGBUF_NAME "image"
-#define SPLBUF_NAME "spl"
-#define BOOTBUF_NAME "bootloader"
-#define FIIOM3K_INIT_WORKSTATE {0}
-
-struct fiiom3k_workstate {
- jz_usbdev* dev;
- jz_buffer* bufs[NUMBUFS];
-};
-
-static void fiiom3k_action_cleanup(struct fiiom3k_workstate* state)
-{
- for(int i = 0; i < NUMBUFS; ++i)
- if(state->bufs[i])
- jz_buffer_free(state->bufs[i]);
+ uint32_t out_len = xread32(src); src += 4, src_len -= 4;
+ if(out_len == 0)
+ break;
- if(state->dev)
- jz_usb_close(state->dev);
-}
+ if(src_len < 4)
+ goto error;
-static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl,
- struct fiiom3k_workstate* state, int idx)
-{
- const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME};
+ uint32_t in_len = xread32(src); src += 4, src_len -= 4;
+ if(in_len > block_size || out_len > block_size ||
+ in_len == 0 || in_len > out_len)
+ goto error;
- if(state->bufs[idx])
- return JZ_SUCCESS;
+ if(src_len < in_len)
+ goto error;
- const char* filename = jz_paramlist_get(pl, paramnames[idx]);
- if(!filename) {
- jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]);
- return JZ_ERR_OTHER;
- }
+ if(in_len < out_len) {
+ uint32_t actual_out_len = dst_ilen;
+ int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL);
+ if(rc != UCL_E_OK)
+ goto error;
+ if(actual_out_len != out_len)
+ goto error;
+ } else {
+ for(size_t i = 0; i < in_len; ++i)
+ dst[i] = src[i];
+ }
- int rc = jz_buffer_load(&state->bufs[idx], filename);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename);
- return rc;
+ src += in_len;
+ src_len -= in_len;
+ dst += out_len;
+ dst_ilen -= out_len;
}
- return JZ_SUCCESS;
+ /* subtract leftover number of bytes to get size of compressed output */
+ *dst_len = buffer->size - dst_ilen;
+ return buffer;
+
+ error:
+ jz_buffer_free(buffer);
+ return NULL;
}
-static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl,
- struct fiiom3k_workstate* state)
+static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf)
{
- const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K);
- if(!info)
- return JZ_ERR_OTHER;
+ int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data);
+ if(rc < 0)
+ return rc;
- int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF);
+ return jz_usb_start1(dev, 0xf4001800);
+}
+
+static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf)
+{
+ int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data);
if(rc < 0)
return rc;
- jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x",
- (unsigned int)info->vendor_id, (unsigned int)info->product_id);
- rc = jz_usb_open(jz, &state->dev, info->vendor_id, info->product_id);
+ rc = jz_usb_flush_caches(dev);
if(rc < 0)
return rc;
- jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access");
- jz_buffer* splbuf = state->bufs[SPLBUF];
- return jz_x1000_setup(state->dev, splbuf->size, splbuf->data);
+ return jz_usb_start2(dev, 0x80004000);
}
-int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl)
+static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file,
+ bool decompress, jz_buffer** buf)
{
- struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
+ jz_buffer* buffer = NULL;
+ mtar_header_t h;
int rc;
- rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF);
- if(rc < 0)
- goto error;
+ rc = mtar_find(tar, file, &h);
+ if(rc != MTAR_ESUCCESS) {
+ jz_log(jz, JZ_LOG_ERROR, "can't find %s in boot file, tar error %d", file, rc);
+ return JZ_ERR_BAD_FILE_FORMAT;
+ }
- rc = fiiom3k_action_setup(jz, pl, &state);
- if(rc < 0)
- goto error;
+ buffer = jz_buffer_alloc(h.size, NULL);
+ if(!buffer)
+ return JZ_ERR_OUT_OF_MEMORY;
- jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device");
- rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]);
- if(rc < 0)
- goto error;
+ rc = mtar_read_data(tar, buffer->data, buffer->size);
+ if(rc != MTAR_ESUCCESS) {
+ jz_buffer_free(buffer);
+ jz_log(jz, JZ_LOG_ERROR, "can't read %s in boot file, tar error %d", file, rc);
+ return JZ_ERR_BAD_FILE_FORMAT;
+ }
- jz_buffer* img_buf = state.bufs[IMGBUF];
- const char* backupfile = jz_paramlist_get(pl, "backup");
- const char* without_backup = jz_paramlist_get(pl, "without-backup");
- if(backupfile) {
- jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile);
- rc = jz_buffer_save(img_buf, backupfile);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile);
- goto error;
+ if(decompress) {
+ uint32_t dst_len;
+ jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len);
+ jz_buffer_free(buffer);
+ if(!nbuf) {
+ jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file);
+ return JZ_ERR_BAD_FILE_FORMAT;
}
- } else if(!without_backup || strcmp(without_backup, "yes")) {
- jz_log(jz, JZ_LOG_ERROR, "No --backup option given and --without-backup yes not specified");
- jz_log(jz, JZ_LOG_ERROR, "Refusing to flash a new bootloader without taking a backup");
- goto error;
- }
- jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader");
- jz_buffer* boot_buf = state.bufs[BOOTBUF];
- jz_buffer* spl_buf = state.bufs[SPLBUF];
- rc = jz_fiiom3k_patchboot(jz, img_buf->data, img_buf->size,
- spl_buf->data, spl_buf->size,
- boot_buf->data, boot_buf->size);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Error patching image: %d", rc);
- goto error;
+ /* for simplicity just forget original size of buffer */
+ nbuf->size = dst_len;
+ buffer = nbuf;
}
- jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device");
- rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data);
- if(rc < 0)
- goto error;
+ *buf = buffer;
+ return JZ_SUCCESS;
+}
- rc = JZ_SUCCESS;
+static int m3k_show_version(jz_context* jz, jz_buffer* info_file)
+{
+ /* Extract the version string and log it for informational purposes */
+ char* boot_version = (char*)info_file->data;
+ char* endpos = memchr(boot_version, '\n', info_file->size);
+ if(!endpos) {
+ jz_log(jz, JZ_LOG_ERROR, "invalid metadata in boot file");
+ return JZ_ERR_BAD_FILE_FORMAT;
+ }
- error:
- fiiom3k_action_cleanup(&state);
- return rc;
+ *endpos = 0;
+ jz_log(jz, JZ_LOG_NOTICE, "Rockbox bootloader version: %s", boot_version);
+ return JZ_SUCCESS;
}
-int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl)
+/** \brief Load the Rockbox bootloader on the FiiO M3K
+ * \param dev USB device freshly returned by jz_usb_open()
+ * \param filename Path to the "bootloader.m3k" file
+ * \return either JZ_SUCCESS or an error code
+ */
+int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename)
{
- struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
+ jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL;
+ mtar_t tar;
int rc;
- const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME);
- if(!outfile_path) {
- jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME);
- rc = JZ_ERR_OTHER;
- goto error;
+ rc = mtar_open(&tar, filename, "r");
+ if(rc != MTAR_ESUCCESS) {
+ jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc);
+ return JZ_ERR_OPEN_FILE;
}
- rc = fiiom3k_action_setup(jz, pl, &state);
- if(rc < 0)
+ /* Extract all necessary files */
+ rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl);
+ if(rc != JZ_SUCCESS)
goto error;
- rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]);
- if(rc < 0)
+ rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader);
+ if(rc != JZ_SUCCESS)
goto error;
- rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path);
- if(rc < 0) {
- jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path);
+ rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file);
+ if(rc != JZ_SUCCESS)
goto error;
- }
-
- rc = JZ_SUCCESS;
- error:
- fiiom3k_action_cleanup(&state);
- return rc;
-}
-
-int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl)
-{
- struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
- int rc;
-
- rc = fiiom3k_action_loadbuf(jz, pl, &state, IMGBUF);
- if(rc < 0)
+ /* Display the version string */
+ rc = m3k_show_version(dev->jz, info_file);
+ if(rc != JZ_SUCCESS)
goto error;
- rc = fiiom3k_action_setup(jz, pl, &state);
- if(rc < 0)
+ /* Stage1 boot of SPL to set up hardware */
+ rc = m3k_stage1(dev, spl);
+ if(rc != JZ_SUCCESS)
goto error;
- jz_buffer* img_buf = state.bufs[IMGBUF];
- rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data);
- if(rc < 0)
+ /* Need a bit of time for SPL to handle init */
+ jz_sleepms(500);
+
+ /* Stage2 boot into the bootloader's recovery menu
+ * User has to take manual action from there */
+ rc = m3k_stage2(dev, bootloader);
+ if(rc != JZ_SUCCESS)
goto error;
rc = JZ_SUCCESS;
error:
- fiiom3k_action_cleanup(&state);
+ if(spl)
+ jz_buffer_free(spl);
+ if(bootloader)
+ jz_buffer_free(bootloader);
+ if(info_file)
+ jz_buffer_free(info_file);
+ mtar_close(&tar);
return rc;
}
diff --git a/rbutil/jztool/src/identify_file.c b/rbutil/jztool/src/identify_file.c
index 3bf4a9ced7..e735075687 100644
--- a/rbutil/jztool/src/identify_file.c
+++ b/rbutil/jztool/src/identify_file.c
@@ -81,6 +81,14 @@ static uint8_t crc7(const uint8_t* buf, size_t len)
return crc;
}
+/** \brief Identify a file as an SPL for X1000 CPUs
+ * \param data File data buffer
+ * \param len Length of file
+ * \return JZ_SUCCESS if file looks correct, or one of the following errors
+ * \retval JZ_IDERR_WRONG_SIZE file too small or size doesn't match header
+ * \retval JZ_IDERR_BAD_HEADER missing magic bytes from header
+ * \retval JZ_IDERR_BAD_CHECKSUM CRC7 mismatch
+ */
int jz_identify_x1000_spl(const void* data, size_t len)
{
/* Use <= check because a header-only file is not really valid,
@@ -115,6 +123,14 @@ static const struct scramble_model_info {
{NULL, 0},
};
+/** \brief Identify a file as a Rockbox `scramble` image
+ * \param data File data buffer
+ * \param len Length of file
+ * \return JZ_SUCCESS if file looks correct, or one of the following errors
+ * \retval JZ_IDERR_WRONG_SIZE file too small to be valid
+ * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type
+ * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch
+ */
int jz_identify_scramble_image(const void* data, size_t len)
{
/* 4 bytes checksum + 4 bytes player model */
@@ -143,37 +159,3 @@ int jz_identify_scramble_image(const void* data, size_t len)
return JZ_SUCCESS;
}
-
-int jz_identify_fiiom3k_bootimage(const void* data, size_t len)
-{
- /* The bootloader image is simply a dump of the first NAND eraseblock,
- * so it has a fixed 128 KiB size */
- if(len != 128*1024)
- return JZ_IDERR_WRONG_SIZE;
-
- /* We'll verify the embedded SPL, but we have to drag out the correct
- * length from the header. Length should be more than 12 KiB, due to
- * limitations of the hardware */
- const struct x1000_spl_header* spl_header;
- spl_header = (const struct x1000_spl_header*)data;
- if(spl_header->length > 12 * 1024)
- return JZ_IDERR_BAD_HEADER;
-
- int rc = jz_identify_x1000_spl(data, spl_header->length);
- if(rc < 0)
- return rc;
-
- const uint8_t* dat = (const uint8_t*)data;
-
- /* Check the partition table is present */
- if(memcmp(&dat[0x3c00], "nand", 4))
- return JZ_IDERR_OTHER;
-
- /* Check first bytes of PDMA firmware. It doesn't change
- * between OF versions, and Rockbox doesn't modify it. */
- static const uint8_t pdma_fw[] = {0x54, 0x25, 0x42, 0xb3, 0x70, 0x25, 0x42, 0xb3};
- if(memcmp(&dat[0x4000], pdma_fw, sizeof(pdma_fw)))
- return JZ_IDERR_OTHER;
-
- return JZ_SUCCESS;
-}
diff --git a/rbutil/jztool/src/paramlist.c b/rbutil/jztool/src/paramlist.c
deleted file mode 100644
index 05bcf97a13..0000000000
--- a/rbutil/jztool/src/paramlist.c
+++ /dev/null
@@ -1,135 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 "jztool.h"
-#include <stdlib.h>
-#include <string.h>
-#include <stdbool.h>
-
-struct jz_paramlist {
- int size;
- char** keys;
- char** values;
-};
-
-static int jz_paramlist_extend(jz_paramlist* pl, int count)
-{
- int nsize = pl->size + count;
-
- /* Reallocate key list */
- char** nkeys = realloc(pl->keys, nsize * sizeof(char*));
- if(!nkeys)
- return JZ_ERR_OUT_OF_MEMORY;
-
- for(int i = pl->size; i < nsize; ++i)
- nkeys[i] = NULL;
-
- pl->keys = nkeys;
-
- /* Reallocate value list */
- char** nvalues = realloc(pl->values, nsize * sizeof(char*));
- if(!nvalues)
- return JZ_ERR_OUT_OF_MEMORY;
-
- for(int i = pl->size; i < nsize; ++i)
- nvalues[i] = NULL;
-
- pl->values = nvalues;
-
- pl->size = nsize;
- return JZ_SUCCESS;
-}
-
-jz_paramlist* jz_paramlist_new(void)
-{
- jz_paramlist* pl = malloc(sizeof(struct jz_paramlist));
- if(!pl)
- return NULL;
-
- pl->size = 0;
- pl->keys = NULL;
- pl->values = NULL;
- return pl;
-}
-
-void jz_paramlist_free(jz_paramlist* pl)
-{
- for(int i = 0; i < pl->size; ++i) {
- free(pl->keys[i]);
- free(pl->values[i]);
- }
-
- if(pl->size > 0) {
- free(pl->keys);
- free(pl->values);
- }
-
- free(pl);
-}
-
-int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value)
-{
- int pos = -1;
- for(int i = 0; i < pl->size; ++i) {
- if(!pl->keys[i] || !strcmp(pl->keys[i], param)) {
- pos = i;
- break;
- }
- }
-
- if(pos == -1) {
- pos = pl->size;
- int rc = jz_paramlist_extend(pl, 1);
- if(rc < 0)
- return rc;
- }
-
- bool need_key = (pl->keys[pos] == NULL);
- if(need_key) {
- char* newparam = strdup(param);
- if(!newparam)
- return JZ_ERR_OUT_OF_MEMORY;
-
- pl->keys[pos] = newparam;
- }
-
- char* newvalue = strdup(value);
- if(!newvalue) {
- if(need_key) {
- free(pl->keys[pos]);
- pl->keys[pos] = NULL;
- }
-
- return JZ_ERR_OUT_OF_MEMORY;
- }
-
- pl->values[pos] = newvalue;
- return JZ_SUCCESS;
-}
-
-const char* jz_paramlist_get(jz_paramlist* pl, const char* param)
-{
- for(int i = 0; i < pl->size; ++i)
- if(pl->keys[i] && !strcmp(pl->keys[i], param))
- return pl->values[i];
-
- return NULL;
-}
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c
index 7e4a5f3388..c101f2be77 100644
--- a/rbutil/jztool/src/usb.c
+++ b/rbutil/jztool/src/usb.c
@@ -30,6 +30,16 @@
#define VR_PROGRAM_START1 4
#define VR_PROGRAM_START2 5
+/** \brief Open a USB device
+ * \param jz Context
+ * \param devptr Returns pointer to the USB device upon success
+ * \param vend_id USB vendor ID
+ * \param prod_id USB product ID
+ * \return either JZ_SUCCESS if device was opened, or an error below
+ * \retval JZ_ERR_OUT_OF_MEMORY malloc failed
+ * \retval JZ_ERR_USB libusb error (details are logged)
+ * \retval JZ_ERR_NO_DEVICE can't unambiguously find the device
+ */
int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id)
{
int rc;
@@ -80,7 +90,7 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
}
if(dev_index < 0) {
- jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found",
+ jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%04x found",
(unsigned int)vend_id, (unsigned int)prod_id);
rc = JZ_ERR_NO_DEVICE;
goto error;
@@ -100,6 +110,8 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
goto error;
}
+ jz_log(jz, JZ_LOG_DEBUG, "Opened device (%p, ID %04x:%04x)",
+ dev, (unsigned int)vend_id, (unsigned int)prod_id);
dev->jz = jz;
dev->handle = usb_handle;
*devptr = dev;
@@ -119,14 +131,20 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
goto exit;
}
+/** \brief Close a USB device
+ * \param dev Device to close; memory will be freed automatically
+ */
void jz_usb_close(jz_usbdev* dev)
{
+ jz_log(dev->jz, JZ_LOG_DEBUG, "Closing device (%p)", dev);
libusb_release_interface(dev->handle, 0);
libusb_close(dev->handle);
jz_context_unref_libusb(dev->jz);
free(dev);
}
+// Does an Ingenic-specific vendor request
+// Written with X1000 in mind but other Ingenic CPUs have the same commands
static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
{
int rc = libusb_control_transfer(dev->handle,
@@ -137,12 +155,24 @@ static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc));
rc = JZ_ERR_USB;
} else {
+ static const char* req_names[] = {
+ "GET_CPU_INFO",
+ "SET_DATA_ADDRESS",
+ "SET_DATA_LENGTH",
+ "FLUSH_CACHES",
+ "PROGRAM_START1",
+ "PROGRAM_START2",
+ };
+
+ jz_log(dev->jz, JZ_LOG_DEBUG, "Issued %s %08lu",
+ req_names[req], (unsigned long)arg);
rc = JZ_SUCCESS;
}
return rc;
}
+// Bulk transfer wrapper
static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
{
int xfered = 0;
@@ -156,12 +186,16 @@ static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered");
rc = JZ_ERR_USB;
} else {
+ jz_log(dev->jz, JZ_LOG_DEBUG, "Transferred %zu bytes %s",
+ len, write ? "to device" : "from device");
rc = JZ_SUCCESS;
}
return rc;
}
+// Memory send/receive primitive, performs the necessary vendor requests
+// and then tranfers data using the bulk endpoint
static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
size_t len, void* data)
{
@@ -177,26 +211,54 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
return jz_usb_transfer(dev, write, len, data);
}
+/** \brief Write data to device memory
+ * \param dev USB device
+ * \param addr Address where data should be written
+ * \param len Length of the data, in bytes, should be positive
+ * \param data Data buffer
+ * \return either JZ_SUCCESS on success or a failure code
+ */
int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
{
return jz_usb_sendrecv(dev, true, addr, len, (void*)data);
}
+/** \brief Read data to device memory
+ * \param dev USB device
+ * \param addr Address to read from
+ * \param len Length of the data, in bytes, should be positive
+ * \param data Data buffer
+ * \return either JZ_SUCCESS on success or a failure code
+ */
int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
{
return jz_usb_sendrecv(dev, false, addr, len, data);
}
+/** \brief Execute stage1 program jumping to the specified address
+ * \param dev USB device
+ * \param addr Address to begin execution at
+ * \return either JZ_SUCCESS on success or a failure code
+ */
int jz_usb_start1(jz_usbdev* dev, uint32_t addr)
{
return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr);
}
+/** \brief Execute stage2 program jumping to the specified address
+ * \param dev USB device
+ * \param addr Address to begin execution at
+ * \return either JZ_SUCCESS on success or a failure code
+ */
int jz_usb_start2(jz_usbdev* dev, uint32_t addr)
{
return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr);
}
+/** \brief Ask device to flush CPU caches
+ * \param dev USB device
+ * \return either JZ_SUCCESS on success or a failure code
+ */
int jz_usb_flush_caches(jz_usbdev* dev)
{
return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0);
diff --git a/rbutil/jztool/src/x1000.c b/rbutil/jztool/src/x1000.c
deleted file mode 100644
index 1a12340316..0000000000
--- a/rbutil/jztool/src/x1000.c
+++ /dev/null
@@ -1,209 +0,0 @@
-/***************************************************************************
- * __________ __ ___.
- * Open \______ \ ____ ____ | | _\_ |__ _______ ___
- * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
- * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
- * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
- * \/ \/ \/ \/ \/
- * $Id$
- *
- * Copyright (C) 2021 Aidan MacDonald
- *
- * 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 "jztool_private.h"
-#include "../../../firmware/target/mips/ingenic_x1000/spl-x1000-defs.h"
-#include "../../../firmware/target/mips/ingenic_x1000/nand-x1000-err.h"
-#include <string.h>
-
-static uint32_t to_le32(uint32_t x)
-{
- union { uint32_t u; uint8_t p[4]; } f;
- f.p[0] = x & 0xff;
- f.p[1] = (x >> 8) & 0xff;
- f.p[2] = (x >> 16) & 0xff;
- f.p[3] = (x >> 24) & 0xff;
- return f.u;
-}
-
-static uint32_t from_le32(uint32_t x)
-{
- union { uint32_t u; uint8_t p[4]; } f;
- f.u = x;
- return f.p[0] | (f.p[1] << 8) | (f.p[2] << 16) | (f.p[3] << 24);
-}
-
-static const char* jz_x1000_nand_strerror(int rc)
-{
- switch(rc) {
- case NANDERR_CHIP_UNSUPPORTED:
- return "Chip unsupported";
- case NANDERR_WRITE_PROTECTED:
- return "Operation forbidden by write-protect";
- case NANDERR_UNALIGNED_ADDRESS:
- return "Improperly aligned address";
- case NANDERR_UNALIGNED_LENGTH:
- return "Improperly aligned length";
- case NANDERR_READ_FAILED:
- return "Read operation failed";
- case NANDERR_ECC_FAILED:
- return "Uncorrectable ECC error on read";
- case NANDERR_ERASE_FAILED:
- return "Erase operation failed";
- case NANDERR_PROGRAM_FAILED:
- return "Program operation failed";
- case NANDERR_COMMAND_FAILED:
- return "NAND command failed";
- default:
- return "Unknown NAND error";
- }
-}
-
-
-static int jz_x1000_send_args(jz_usbdev* dev, struct x1000_spl_arguments* args)
-{
- args->command = to_le32(args->command);
- args->param1 = to_le32(args->param1);
- args->param2 = to_le32(args->param2);
- args->flags = to_le32(args->flags);
- return jz_usb_send(dev, SPL_ARGUMENTS_ADDRESS, sizeof(*args), args);
-}
-
-static int jz_x1000_recv_status(jz_usbdev* dev, struct x1000_spl_status* status)
-{
- int rc = jz_usb_recv(dev, SPL_STATUS_ADDRESS, sizeof(*status), status);
- if(rc < 0)
- return rc;
-
- status->err_code = from_le32(status->err_code);
- status->reserved = from_le32(status->reserved);
- return JZ_SUCCESS;
-}
-
-int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data)
-{
- int rc = jz_identify_x1000_spl(spl_data, spl_len);
- if(rc < 0)
- return JZ_ERR_BAD_FILE_FORMAT;
- if(spl_len > SPL_MAX_SIZE)
- return JZ_ERR_BAD_FILE_FORMAT;
-
- rc = jz_usb_send(dev, SPL_LOAD_ADDRESS, spl_len, spl_data);
- if(rc < 0)
- return rc;
-
- struct x1000_spl_arguments args;
- args.command = SPL_CMD_BOOT;
- args.param1 = SPL_BOOTOPT_NONE;
- args.param2 = 0;
- args.flags = 0;
- rc = jz_x1000_send_args(dev, &args);
- if(rc < 0)
- return rc;
-
- rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
- if(rc < 0)
- return rc;
-
- jz_sleepms(100);
-
- struct x1000_spl_status status;
- rc = jz_x1000_recv_status(dev, &status);
- if(rc < 0)
- return rc;
-
- if(status.err_code != 0) {
- jz_log(dev->jz, JZ_LOG_ERROR, "X1000 device init error: %d", status.err_code);
- return JZ_ERR_OTHER;
- }
-
- return JZ_SUCCESS;
-}
-
-int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
-{
- struct x1000_spl_arguments args;
- args.command = SPL_CMD_FLASH_READ;
- args.param1 = addr;
- args.param2 = len;
- args.flags = SPL_FLAG_SKIP_INIT;
- int rc = jz_x1000_send_args(dev, &args);
- if(rc < 0)
- return rc;
-
- rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
- if(rc < 0)
- return rc;
-
- jz_sleepms(500);
-
- struct x1000_spl_status status;
- rc = jz_x1000_recv_status(dev, &status);
- if(rc < 0)
- return rc;
-
- if(status.err_code != 0) {
- jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash read error: %s",
- jz_x1000_nand_strerror(status.err_code));
- return JZ_ERR_FLASH_ERROR;
- }
-
- return jz_usb_recv(dev, SPL_BUFFER_ADDRESS, len, data);
-}
-
-int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
-{
- int rc = jz_usb_send(dev, SPL_BUFFER_ADDRESS, len, data);
- if(rc < 0)
- return rc;
-
- struct x1000_spl_arguments args;
- args.command = SPL_CMD_FLASH_WRITE;
- args.param1 = addr;
- args.param2 = len;
- args.flags = SPL_FLAG_SKIP_INIT;
- rc = jz_x1000_send_args(dev, &args);
- if(rc < 0)
- return rc;
-
- rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
- if(rc < 0)
- return rc;
-
- jz_sleepms(500);
-
- struct x1000_spl_status status;
- rc = jz_x1000_recv_status(dev, &status);
- if(rc < 0)
- return rc;
-
- if(status.err_code != 0) {
- jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash write error: %s",
- jz_x1000_nand_strerror(status.err_code));
- return JZ_ERR_FLASH_ERROR;
- }
-
- return JZ_SUCCESS;
-}
-
-int jz_x1000_boot_rockbox(jz_usbdev* dev)
-{
- struct x1000_spl_arguments args;
- args.command = SPL_CMD_BOOT;
- args.param1 = SPL_BOOTOPT_ROCKBOX;
- args.param2 = 0;
- args.flags = 0;
- int rc = jz_x1000_send_args(dev, &args);
- if(rc < 0)
- return rc;
-
- return jz_usb_start1(dev, SPL_EXEC_ADDRESS);
-}
diff --git a/rbutil/libtools.make b/rbutil/libtools.make
index 46244c07a0..3ab150e876 100644
--- a/rbutil/libtools.make
+++ b/rbutil/libtools.make
@@ -118,6 +118,12 @@ $(LIBBZIP2): $(OBJDIR)$(LIBBZIP2)
$(OBJDIR)$(LIBBZIP2):
$(SILENT)$(MAKE) -C $(TOP)/bzip2 TARGET_DIR=$(OBJDIR) CC=$(CC) $@
+LIBMICROTAR = libmicrotar.a
+$(LIBMICROTAR): $(OBJDIR)$(LIBMICROTAR)
+
+$(OBJDIR)$(LIBMICROTAR):
+ $(SILENT)$(MAKE) -C $(TOP)/../lib/microtar/src TARGET_DIR=$(OBJDIR) CC=$(CC) $@
+
# building the standalone executable
$(BINARY): $(OBJS) $(EXTRADEPS) $(addprefix $(OBJDIR),$(EXTRALIBOBJS)) $(TARGET_DIR)lib$(OUTPUT).a
$(info LD $@)