summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAidan MacDonald <amachronic@protonmail.com>2021-04-13 16:58:15 +0100
committerAidan MacDonald <amachronic@protonmail.com>2021-04-17 20:23:19 +0000
commitb41d53792c4c4e4abd6d810b2765d865775f5104 (patch)
treeae3e02b961c655cd49b3c13bc895a18547ceeec1
parent1b8542490da3283dfa0ce0f3363f16eab0609815 (diff)
downloadrockbox-b41d53792c4c4e4abd6d810b2765d865775f5104.tar.gz
rockbox-b41d53792c4c4e4abd6d810b2765d865775f5104.zip
jztool: New utility for installing a bootloader on FiiO M3K
At present, this is just a command line tool for Linux only. Integrating this with the Rockbox utility and porting to other platforms should be straightforward; the README contains more information. Change-Id: Ie66fc837a02ab13c878925360cabc9805597548a
-rw-r--r--.gitignore1
-rw-r--r--rbutil/jztool/Makefile37
-rw-r--r--rbutil/jztool/README.md60
-rw-r--r--rbutil/jztool/include/jztool.h200
-rw-r--r--rbutil/jztool/jztool.c227
-rw-r--r--rbutil/jztool/src/buffer.c134
-rw-r--r--rbutil/jztool/src/context.c167
-rw-r--r--rbutil/jztool/src/device_info.c98
-rw-r--r--rbutil/jztool/src/fiiom3k.c283
-rw-r--r--rbutil/jztool/src/identify_file.c179
-rw-r--r--rbutil/jztool/src/jztool_private.h44
-rw-r--r--rbutil/jztool/src/paramlist.c135
-rw-r--r--rbutil/jztool/src/usb.c203
-rw-r--r--rbutil/jztool/src/x1000.c193
14 files changed, 1961 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
index 6e22d4dcf8..93253775a8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -97,6 +97,7 @@ __pycache__
97/rbutil/mknwzboot/mknwzboot 97/rbutil/mknwzboot/mknwzboot
98/rbutil/mkzenboot/mkzenboot 98/rbutil/mkzenboot/mkzenboot
99/rbutil/sansapatcher/sansapatcher 99/rbutil/sansapatcher/sansapatcher
100/rbutil/jztool/jztool
100/rbutil/tools/bin2c 101/rbutil/tools/bin2c
101 102
102# /tools/ 103# /tools/
diff --git a/rbutil/jztool/Makefile b/rbutil/jztool/Makefile
new file mode 100644
index 0000000000..aa434adf1f
--- /dev/null
+++ b/rbutil/jztool/Makefile
@@ -0,0 +1,37 @@
1# __________ __ ___.
2# Open \______ \ ____ ____ | | _\_ |__ _______ ___
3# Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
4# Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
5# Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
6# \/ \/ \/ \/ \/
7
8CFLAGS += -Wall -Wextra -Iinclude
9OUTPUT = jztool
10
11ifdef RELEASE
12CFLAGS += -Os -DNDEBUG
13else
14CFLAGS += -O0 -ggdb
15endif
16
17LIBSOURCES := src/buffer.c src/context.c src/device_info.c \
18 src/fiiom3k.c src/identify_file.c src/paramlist.c \
19 src/usb.c src/x1000.c
20SOURCES := $(LIBSOURCES) jztool.c
21EXTRADEPS :=
22
23CPPDEFINES := $(shell echo foo | $(CROSS)$(CC) -dM -E -)
24
25ifeq ($(findstring WIN32,$(CPPDEFINES)),WIN32)
26# TODO: support Windows
27else
28ifeq ($(findstring APPLE,$(CPPDEFINES)),APPLE)
29# TODO: support OSX
30else
31# Linux
32CFLAGS += `pkg-config --cflags libusb-1.0`
33LDOPTS += `pkg-config --libs libusb-1.0`
34endif
35endif
36
37include ../libtools.make
diff --git a/rbutil/jztool/README.md b/rbutil/jztool/README.md
new file mode 100644
index 0000000000..6a9b78f8d7
--- /dev/null
+++ b/rbutil/jztool/README.md
@@ -0,0 +1,60 @@
1# jztool -- Ingenic device utility & bootloader installer
2
3The `jztool` utility can install, backup, and restore the bootloader on
4Rockbox players based on a supported Ingenic SoC.
5
6## FiiO M3K
7
8To use `jztool` on the FiiO M3K you have to connect the player to your
9computer in USB boot mode.
10
11The easiest way to do this is by plugging in the microUSB cable to the M3K
12and holding the volume down button while plugging the USB into your computer.
13If you entered USB boot mode, the button light will turn on but the LCD will
14turn off.
15
16To install or update the Rockbox bootloader on the M3K, use the command
17`jztool fiiom3k install`. It is recommended that you take a backup of your
18current bootloader so you can restore it in case of any problems.
19
20After any operation finishes, you will have to force a power off of the M3K
21by holding down the power button for at least 10 seconds. This must be done
22whether the operation succeeds or fails. Just don't power off or unplug the
23device in the middle of an operation -- that might make bad things happen.
24
25See `jztool --help` for info.
26
27## TODO list
28
29### Add better documentation and logging
30
31There's only a bare minimum of documentation, and logging is sparse, not
32really enough to debug problems.
33
34Some of the error messages could be friendlier too.
35
36### Integration with the Rockbox utility
37
38Adding support to the Rockbox utility should be mostly boilerplate since the
39jztool library wraps all the troublesome details.
40
41Getting appropriate privileges to access the USB device is the main issue.
42Preferably, the Rockbox utility should not run as root/admin/etc.
43
44- Windows: not sure
45- Linux: needs udev rules or root privileges
46- Mac: apparently does not need privileges
47
48### Porting to Windows
49
50Windows wants to see a driver installed before we can access the USB device,
51the easiest way to do this is by having the user run Zadig, a 3rd party app
52which can install the WinUSB driver. WinUSB itself is from Microsoft and
53bundled with Windows.
54
55Zadig's homepage: https://zadig.akeo.ie/
56
57### Porting to Mac
58
59According to the libusb wiki, libusb works on Mac without any special setup or
60privileges, presumably porting there is easy.
diff --git a/rbutil/jztool/include/jztool.h b/rbutil/jztool/include/jztool.h
new file mode 100644
index 0000000000..e16bc9765f
--- /dev/null
+++ b/rbutil/jztool/include/jztool.h
@@ -0,0 +1,200 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef JZTOOL_H
23#define JZTOOL_H
24
25#include <stdint.h>
26#include <stddef.h>
27
28#ifdef __cplusplus
29extern "C" {
30#endif
31
32/******************************************************************************
33 * Types, enumerations, etc
34 */
35
36typedef struct jz_context jz_context;
37typedef struct jz_usbdev jz_usbdev;
38typedef struct jz_device_info jz_device_info;
39typedef struct jz_buffer jz_buffer;
40typedef struct jz_paramlist jz_paramlist;
41
42typedef enum jz_error jz_error;
43typedef enum jz_identify_error jz_identify_error;
44typedef enum jz_log_level jz_log_level;
45typedef enum jz_device_type jz_device_type;
46typedef enum jz_cpu_type jz_cpu_type;
47
48typedef void(*jz_log_cb)(jz_log_level, const char*);
49typedef int(*jz_device_action_fn)(jz_context*, jz_paramlist*);
50
51enum jz_error {
52 JZ_SUCCESS = 0,
53 JZ_ERR_OUT_OF_MEMORY = -1,
54 JZ_ERR_OPEN_FILE = -2,
55 JZ_ERR_FILE_IO = -3,
56 JZ_ERR_USB = -4,
57 JZ_ERR_NO_DEVICE = -5,
58 JZ_ERR_BAD_FILE_FORMAT = -6,
59 JZ_ERR_FLASH_ERROR = -7,
60 JZ_ERR_OTHER = -99,
61};
62
63enum jz_identify_error {
64 JZ_IDERR_OTHER = -1,
65 JZ_IDERR_WRONG_SIZE = -2,
66 JZ_IDERR_BAD_HEADER = -3,
67 JZ_IDERR_BAD_CHECKSUM = -4,
68 JZ_IDERR_UNRECOGNIZED_MODEL = -5,
69};
70
71enum jz_log_level {
72 JZ_LOG_IGNORE = -1,
73 JZ_LOG_ERROR = 0,
74 JZ_LOG_WARNING = 1,
75 JZ_LOG_NOTICE = 2,
76 JZ_LOG_DETAIL = 3,
77 JZ_LOG_DEBUG = 4,
78};
79
80enum jz_device_type {
81 JZ_DEVICE_FIIOM3K,
82};
83
84enum jz_cpu_type {
85 JZ_CPU_X1000,
86};
87
88struct jz_device_info {
89 const char* name;
90 const char* description;
91 jz_device_type device_type;
92 jz_cpu_type cpu_type;
93 uint16_t vendor_id;
94 uint16_t product_id;
95 int num_actions;
96 const char* const* action_names;
97 const jz_device_action_fn* action_funcs;
98 const char* const* const* action_params;
99};
100
101struct jz_buffer {
102 size_t size;
103 uint8_t* data;
104};
105
106/******************************************************************************
107 * Library context and general functions
108 */
109
110jz_context* jz_context_create(void);
111void jz_context_destroy(jz_context* jz);
112
113void jz_context_set_user_data(jz_context* jz, void* ptr);
114void* jz_context_get_user_data(jz_context* jz);
115
116void jz_context_set_log_cb(jz_context* jz, jz_log_cb cb);
117void jz_context_set_log_level(jz_context* jz, jz_log_level lev);
118
119void jz_log(jz_context* jz, jz_log_level lev, const char* fmt, ...);
120void jz_log_cb_stderr(jz_log_level lev, const char* msg);
121
122void jz_sleepms(int ms);
123
124/******************************************************************************
125 * Device and file info
126 */
127
128int jz_get_num_device_info(void);
129const jz_device_info* jz_get_device_info(jz_device_type type);
130const jz_device_info* jz_get_device_info_named(const char* name);
131const jz_device_info* jz_get_device_info_indexed(int index);
132
133int jz_identify_x1000_spl(const void* data, size_t len);
134int jz_identify_scramble_image(const void* data, size_t len);
135int jz_identify_fiiom3k_bootimage(const void* data, size_t len);
136
137/******************************************************************************
138 * USB boot ROM protocol
139 */
140
141int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id);
142void jz_usb_close(jz_usbdev* dev);
143
144int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data);
145int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data);
146int jz_usb_start1(jz_usbdev* dev, uint32_t addr);
147int jz_usb_start2(jz_usbdev* dev, uint32_t addr);
148int jz_usb_flush_caches(jz_usbdev* dev);
149
150/******************************************************************************
151 * X1000 flash protocol
152 */
153
154int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data);
155int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data);
156int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data);
157int jz_x1000_boot_rockbox(jz_usbdev* dev);
158
159/******************************************************************************
160 * FiiO M3K bootloader backup/installation
161 */
162
163int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr);
164int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf);
165int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size,
166 const void* spl_buf, size_t spl_size,
167 const void* boot_buf, size_t boot_size);
168
169int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl);
170int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl);
171int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl);
172
173/******************************************************************************
174 * Simple buffer API
175 */
176
177jz_buffer* jz_buffer_alloc(size_t size, const void* data);
178void jz_buffer_free(jz_buffer* buf);
179
180int jz_buffer_load(jz_buffer** buf, const char* filename);
181int jz_buffer_save(jz_buffer* buf, const char* filename);
182
183/******************************************************************************
184 * Parameter list
185 */
186
187jz_paramlist* jz_paramlist_new(void);
188void jz_paramlist_free(jz_paramlist* pl);
189int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value);
190const char* jz_paramlist_get(jz_paramlist* pl, const char* param);
191
192/******************************************************************************
193 * END
194 */
195
196#ifdef __cplusplus
197}
198#endif
199
200#endif /* JZTOOL_H */
diff --git a/rbutil/jztool/jztool.c b/rbutil/jztool/jztool.c
new file mode 100644
index 0000000000..5aae8d7457
--- /dev/null
+++ b/rbutil/jztool/jztool.c
@@ -0,0 +1,227 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <stdio.h>
24#include <stdlib.h>
25#include <string.h>
26#include <stdbool.h>
27
28jz_context* jz = NULL;
29const jz_device_info* dev_info = NULL;
30int dev_action = -1;
31jz_paramlist* action_params = NULL;
32
33void usage(void)
34{
35 printf("Usage:\n"
36 " jztool [global options] <device> <action> [action options]\n"
37 "\n"
38 "Global options:\n"
39 "\n"
40 " -h, --help Display this help\n"
41 " -q, --quiet Don't log anything except errors\n"
42 " -v, --verbose Display detailed logging output\n"
43 " -l, --loglevel LEVEL Set log level\n");
44
45 printf("Supported devices:\n\n");
46 int n = jz_get_num_device_info();
47 for(int i = 0; i < n; ++i) {
48 const jz_device_info* info = jz_get_device_info_indexed(i);
49 printf(" %s - %s\n", info->name, info->description);
50 }
51
52 printf(
53"\n"
54"Available actions for fiiom3k:\n"
55"\n"
56" install --spl <spl.m3k> --bootloader <bootloader.m3k>\n"
57" [--without-backup yes] [--backup IMAGE]\n"
58" Install or update the Rockbox bootloader on a device.\n"
59"\n"
60" If --backup is given, back up the current bootloader to IMAGE before\n"
61" installing the new bootloader. The installer will normally refuse to\n"
62" overwrite your current bootloader; pass '--without-backup yes' if you\n"
63" really want to proceed without taking a backup.\n"
64"\n"
65" WARNING: it is NOT RECOMMENDED to install the Rockbox bootloader\n"
66" without taking a backup of the original firmware bootloader. It may\n"
67" be very difficult or impossible to recover your player without one.\n"
68" At least one M3Ks is known to not to work with the Rockbox bootloader,\n"
69" so it is very important to take a backup.\n"
70"\n"
71" backup --image IMAGE\n"
72" Backup the current bootloader to the file IMAGE\n"
73"\n"
74" restore --image IMAGE\n"
75" Restore a bootloader image backup from the file IMAGE\n"
76"\n");
77
78 exit(4);
79}
80
81void cleanup(void)
82{
83 if(action_params)
84 jz_paramlist_free(action_params);
85 if(jz)
86 jz_context_destroy(jz);
87}
88
89int main(int argc, char** argv)
90{
91 if(argc < 2)
92 usage();
93
94 /* Library initialization */
95 jz = jz_context_create();
96 if(!jz) {
97 fprintf(stderr, "ERROR: Can't create context");
98 return 1;
99 }
100
101 atexit(cleanup);
102 jz_context_set_log_cb(jz, jz_log_cb_stderr);
103 jz_context_set_log_level(jz, JZ_LOG_NOTICE);
104
105 /* Parse global options */
106 --argc, ++argv;
107 while(argc > 0 && argv[0][0] == '-') {
108 if(!strcmp(*argv, "-h") || !strcmp(*argv, "--help"))
109 usage();
110 else if(!strcmp(*argv, "-q") || !strcmp(*argv, "--quiet"))
111 jz_context_set_log_level(jz, JZ_LOG_ERROR);
112 else if(!strcmp(*argv, "-v") || !strcmp(*argv, "--verbose"))
113 jz_context_set_log_level(jz, JZ_LOG_DETAIL);
114 else if(!strcmp(*argv, "-l") || !strcmp(*argv, "--loglevel")) {
115 ++argv;
116 if(--argc == 0) {
117 jz_log(jz, JZ_LOG_ERROR, "Missing argument to option %s", *argv);
118 exit(2);
119 }
120
121 enum jz_log_level level;
122 if(!strcmp(*argv, "ignore"))
123 level = JZ_LOG_IGNORE;
124 else if(!strcmp(*argv, "error"))
125 level = JZ_LOG_ERROR;
126 else if(!strcmp(*argv, "warning"))
127 level = JZ_LOG_WARNING;
128 else if(!strcmp(*argv, "notice"))
129 level = JZ_LOG_NOTICE;
130 else if(!strcmp(*argv, "detail"))
131 level = JZ_LOG_DETAIL;
132 else if(!strcmp(*argv, "debug"))
133 level = JZ_LOG_DEBUG;
134 else {
135 jz_log(jz, JZ_LOG_ERROR, "Invalid log level '%s'", *argv);
136 exit(2);
137 }
138
139 jz_context_set_log_level(jz, level);
140 } else {
141 jz_log(jz, JZ_LOG_ERROR, "Invalid global option '%s'", *argv);
142 exit(2);
143 }
144
145 --argc, ++argv;
146 }
147
148 /* Read the device type */
149 if(argc == 0) {
150 jz_log(jz, JZ_LOG_ERROR, "No device specified (try jztool --help)");
151 exit(2);
152 }
153
154 dev_info = jz_get_device_info_named(*argv);
155 if(!dev_info) {
156 jz_log(jz, JZ_LOG_ERROR, "Unknown device '%s' (try jztool --help)", *argv);
157 exit(2);
158 }
159
160 /* Read the action */
161 --argc, ++argv;
162 if(argc == 0) {
163 jz_log(jz, JZ_LOG_ERROR, "No action specified (try jztool --help)");
164 exit(2);
165 }
166
167 for(dev_action = 0; dev_action < dev_info->num_actions; ++dev_action)
168 if(!strcmp(*argv, dev_info->action_names[dev_action]))
169 break;
170
171 if(dev_action == dev_info->num_actions) {
172 jz_log(jz, JZ_LOG_ERROR, "Unknown action '%s' (try jztool --help)", *argv);
173 exit(2);
174 }
175
176 /* Parse the action options */
177 action_params = jz_paramlist_new();
178 if(!action_params) {
179 jz_log(jz, JZ_LOG_ERROR, "Out of memory: can't create paramlist");
180 exit(1);
181 }
182
183 const char* const* allowed_params = dev_info->action_params[dev_action];
184
185 --argc, ++argv;
186 while(argc > 0 && argv[0][0] == '-') {
187 if(argv[0][1] != '-') {
188 jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv);
189 exit(2);
190 }
191
192 bool bad_option = true;
193 for(int i = 0; allowed_params[i] != NULL; ++i) {
194 if(!strcmp(&argv[0][2], allowed_params[i])) {
195 ++argv;
196 if(--argc == 0) {
197 jz_log(jz, JZ_LOG_ERROR, "Missing argument for parameter '%s'", *argv);
198 exit(2);
199 }
200
201 int rc = jz_paramlist_set(action_params, allowed_params[i], *argv);
202 if(rc < 0) {
203 jz_log(jz, JZ_LOG_ERROR, "Out of memory");
204 exit(1);
205 }
206
207 bad_option = false;
208 }
209 }
210
211 if(bad_option) {
212 jz_log(jz, JZ_LOG_ERROR, "Invalid option '%s' for action", *argv);
213 exit(2);
214 }
215
216 --argc, ++argv;
217 }
218
219 if(argc != 0) {
220 jz_log(jz, JZ_LOG_ERROR, "Excess arguments on command line");
221 exit(2);
222 }
223
224 /* Invoke action handler */
225 int rc = dev_info->action_funcs[dev_action](jz, action_params);
226 return (rc < 0) ? 1 : 0;
227}
diff --git a/rbutil/jztool/src/buffer.c b/rbutil/jztool/src/buffer.c
new file mode 100644
index 0000000000..9e9c9ff5d1
--- /dev/null
+++ b/rbutil/jztool/src/buffer.c
@@ -0,0 +1,134 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <stdlib.h>
24#include <string.h>
25#include <stdio.h>
26
27/** \brief Allocate a buffer, optionally providing its contents.
28 * \param size Number of bytes to allocate
29 * \param data Initial contents of the buffer, must be at least `size` bytes
30 * \return Pointer to buffer or NULL if out of memory.
31 * \note The buffer will not take ownership of the `data` pointer, instead it
32 * allocates a fresh buffer and copies the contents of `data` into it.
33 */
34jz_buffer* jz_buffer_alloc(size_t size, const void* data)
35{
36 jz_buffer* buf = malloc(sizeof(struct jz_buffer));
37 if(!buf)
38 return NULL;
39
40 buf->data = malloc(size);
41 if(!buf->data) {
42 free(buf);
43 return NULL;
44 }
45
46 if(data)
47 memcpy(buf->data, data, size);
48
49 buf->size = size;
50 return buf;
51}
52
53/** \brief Free a buffer
54 */
55void jz_buffer_free(jz_buffer* buf)
56{
57 if(buf) {
58 free(buf->data);
59 free(buf);
60 }
61}
62
63/** \brief Load a buffer from a file
64 * \param buf Returns loaded buffer on success, unmodified on error
65 * \param filename Path to the file
66 * \return either JZ_SUCCESS, or one of the following errors
67 * \retval JZ_ERR_OPEN_FILE file cannot be opened
68 * \retval JZ_ERR_OUT_OF_MEMORY cannot allocate buffer to hold file contents
69 * \retval JZ_ERR_FILE_IO problem reading file data
70 */
71int jz_buffer_load(jz_buffer** buf, const char* filename)
72{
73 FILE* f;
74 jz_buffer* b;
75 int rc;
76
77 f = fopen(filename, "rb");
78 if(!f)
79 return JZ_ERR_OPEN_FILE;
80
81 fseek(f, 0, SEEK_END);
82 int size = ftell(f);
83 fseek(f, 0, SEEK_SET);
84
85 b = jz_buffer_alloc(size, NULL);
86 if(!b) {
87 rc = JZ_ERR_OUT_OF_MEMORY;
88 goto err_fclose;
89 }
90
91 if(fread(b->data, size, 1, f) != 1) {
92 rc = JZ_ERR_FILE_IO;
93 goto err_free_buf;
94 }
95
96 rc = JZ_SUCCESS;
97 *buf = b;
98
99 err_fclose:
100 fclose(f);
101 return rc;
102
103 err_free_buf:
104 jz_buffer_free(b);
105 goto err_fclose;
106}
107
108/** \brief Save a buffer to a file
109 * \param buf Buffer to be written out
110 * \param filename Path to the file
111 * \return either JZ_SUCCESS, or one of the following errors
112 * \retval JZ_ERR_OPEN_FILE file cannot be opened
113 * \retval JZ_ERR_FILE_IO problem writing file data
114 */
115int jz_buffer_save(jz_buffer* buf, const char* filename)
116{
117 int rc;
118 FILE* f;
119
120 f = fopen(filename, "wb");
121 if(!f)
122 return JZ_ERR_OPEN_FILE;
123
124 if(fwrite(buf->data, buf->size, 1, f) != 1) {
125 rc = JZ_ERR_FILE_IO;
126 goto err_fclose;
127 }
128
129 rc = JZ_SUCCESS;
130
131 err_fclose:
132 fclose(f);
133 return rc;
134}
diff --git a/rbutil/jztool/src/context.c b/rbutil/jztool/src/context.c
new file mode 100644
index 0000000000..94b21b5196
--- /dev/null
+++ b/rbutil/jztool/src/context.c
@@ -0,0 +1,167 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool_private.h"
23#include <string.h>
24#include <stdlib.h>
25#include <stddef.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <time.h>
29
30/** \brief Allocate a library context
31 * \returns New context or NULL if out of memory
32 */
33jz_context* jz_context_create(void)
34{
35 jz_context* jz = malloc(sizeof(struct jz_context));
36 if(!jz)
37 return NULL;
38
39 memset(jz, 0, sizeof(struct jz_context));
40 jz->log_level = JZ_LOG_ERROR;
41 return jz;
42}
43
44/** \brief Destroy the context and free its memory */
45void jz_context_destroy(jz_context* jz)
46{
47 if(jz->usb_ctx) {
48 jz_log(jz, JZ_LOG_ERROR, "BUG: USB was not cleaned up properly");
49 libusb_exit(jz->usb_ctx);
50 }
51
52 free(jz);
53}
54
55/** \brief Set a user data pointer. Useful for callbacks. */
56void jz_context_set_user_data(jz_context* jz, void* ptr)
57{
58 jz->user_data = ptr;
59}
60
61/** \brief Get the user data pointer */
62void* jz_context_get_user_data(jz_context* jz)
63{
64 return jz->user_data;
65}
66
67/** \brief Set the log message callback.
68 * \note By default, no message callback is set! No messages will be logged
69 * in this case, so ensure you set a callback if messages are desired.
70 */
71void jz_context_set_log_cb(jz_context* jz, jz_log_cb cb)
72{
73 jz->log_cb = cb;
74}
75
76/** \brief Set the log level.
77 *
78 * Messages of less importance than the set log level are not logged.
79 * The default log level is `JZ_LOG_WARNING`. The special log level
80 * `JZ_LOG_IGNORE` can be used to disable all logging temporarily.
81 *
82 * The `JZ_LOG_DEBUG` log level is extremely verbose and will log all calls,
83 * normally it's only useful for catching bugs.
84 */
85void jz_context_set_log_level(jz_context* jz, jz_log_level lev)
86{
87 jz->log_level = lev;
88}
89
90/** \brief Log an informational message.
91 * \param lev Log level for this message
92 * \param fmt `printf` style message format string
93 */
94void jz_log(jz_context* jz, jz_log_level lev, const char* fmt, ...)
95{
96 if(!jz->log_cb)
97 return;
98 if(lev == JZ_LOG_IGNORE)
99 return;
100 if(lev > jz->log_level)
101 return;
102
103 va_list ap;
104
105 va_start(ap, fmt);
106 int n = vsnprintf(NULL, 0, fmt, ap);
107 va_end(ap);
108
109 if(n < 0)
110 return;
111
112 char* buf = malloc(n + 1);
113 if(!buf)
114 return;
115
116 va_start(ap, fmt);
117 n = vsnprintf(buf, n + 1, fmt, ap);
118 va_end(ap);
119
120 if(n >= 0)
121 jz->log_cb(lev, buf);
122
123 free(buf);
124}
125
126/** \brief Log callback which writes messages to `stderr`.
127 */
128void jz_log_cb_stderr(jz_log_level lev, const char* msg)
129{
130 static const char* const tags[] =
131 {"ERROR", "WARNING", "NOTICE", "DETAIL", "DEBUG"};
132 fprintf(stderr, "[%7s] %s\n", tags[lev], msg);
133 fflush(stderr);
134}
135
136/** \brief Sleep for `ms` milliseconds.
137 */
138void jz_sleepms(int ms)
139{
140 struct timespec ts;
141 long ns = ms % 1000;
142 ts.tv_nsec = ns * 1000 * 1000;
143 ts.tv_sec = ms / 1000;
144 nanosleep(&ts, NULL);
145}
146
147int jz_context_ref_libusb(jz_context* jz)
148{
149 if(jz->usb_ctxref == 0) {
150 int rc = libusb_init(&jz->usb_ctx);
151 if(rc < 0) {
152 jz_log(jz, JZ_LOG_ERROR, "libusb_init: %s", libusb_strerror(rc));
153 return JZ_ERR_USB;
154 }
155 }
156
157 jz->usb_ctxref += 1;
158 return JZ_SUCCESS;
159}
160
161void jz_context_unref_libusb(jz_context* jz)
162{
163 if(--jz->usb_ctxref == 0) {
164 libusb_exit(jz->usb_ctx);
165 jz->usb_ctx = NULL;
166 }
167}
diff --git a/rbutil/jztool/src/device_info.c b/rbutil/jztool/src/device_info.c
new file mode 100644
index 0000000000..bc1477be32
--- /dev/null
+++ b/rbutil/jztool/src/device_info.c
@@ -0,0 +1,98 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <string.h>
24
25static const char* const fiiom3k_action_names[] = {
26 "install",
27 "backup",
28 "restore",
29};
30
31static const char* const fiiom3k_install_action_params[] =
32 {"spl", "bootloader", "backup", "without-backup", NULL};
33
34static const char* const fiiom3k_backuprestore_action_params[] =
35 {"spl", "image", NULL};
36
37static const char* const* fiiom3k_action_params[] = {
38 fiiom3k_install_action_params,
39 fiiom3k_backuprestore_action_params,
40 fiiom3k_backuprestore_action_params,
41};
42
43static const jz_device_action_fn fiiom3k_action_funcs[] = {
44 jz_fiiom3k_install,
45 jz_fiiom3k_backup,
46 jz_fiiom3k_restore,
47};
48
49static const jz_device_info infotable[] = {
50 {
51 .name = "fiiom3k",
52 .description = "FiiO M3K",
53 .device_type = JZ_DEVICE_FIIOM3K,
54 .cpu_type = JZ_CPU_X1000,
55 .vendor_id = 0xa108,
56 .product_id = 0x1000,
57 .num_actions = sizeof(fiiom3k_action_names)/sizeof(void*),
58 .action_names = fiiom3k_action_names,
59 .action_funcs = fiiom3k_action_funcs,
60 .action_params = fiiom3k_action_params,
61 },
62};
63
64static const int infotable_size = sizeof(infotable)/sizeof(struct jz_device_info);
65
66/** \brief Get the number of entries in the device info list */
67int jz_get_num_device_info(void)
68{
69 return infotable_size;
70}
71
72const jz_device_info* jz_get_device_info(jz_device_type type)
73{
74 for(int i = 0; i < infotable_size; ++i)
75 if(infotable[i].device_type == type)
76 return &infotable[i];
77
78 return NULL;
79}
80
81/** \brief Lookup info for a device by name, returns NULL if not found. */
82const jz_device_info* jz_get_device_info_named(const char* name)
83{
84 for(int i = 0; i < infotable_size; ++i)
85 if(!strcmp(infotable[i].name, name))
86 return &infotable[i];
87
88 return NULL;
89}
90
91/** \brief Get a device info entry by index, returns NULL if out of range. */
92const jz_device_info* jz_get_device_info_indexed(int index)
93{
94 if(index < infotable_size)
95 return &infotable[index];
96 else
97 return NULL;
98}
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/fiiom3k.c
new file mode 100644
index 0000000000..a43863c2f7
--- /dev/null
+++ b/rbutil/jztool/src/fiiom3k.c
@@ -0,0 +1,283 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <string.h>
24
25#define IMAGE_ADDR 0
26#define IMAGE_SIZE (128 * 1024)
27#define SPL_OFFSET 0
28#define SPL_SIZE (12 * 1024)
29#define BOOT_OFFSET (26 * 1024)
30#define BOOT_SIZE (102 * 1024)
31
32int jz_fiiom3k_readboot(jz_usbdev* dev, jz_buffer** bufptr)
33{
34 jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL);
35 if(!buf)
36 return JZ_ERR_OUT_OF_MEMORY;
37
38 int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data);
39 if(rc < 0) {
40 jz_buffer_free(buf);
41 return rc;
42 }
43
44 *bufptr = buf;
45 return JZ_SUCCESS;
46}
47
48int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf)
49{
50 int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size);
51 if(rc < 0 || image_size != IMAGE_SIZE)
52 return JZ_ERR_BAD_FILE_FORMAT;
53
54 rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf);
55 if(rc < 0)
56 return rc;
57
58 return JZ_SUCCESS;
59}
60
61int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size,
62 const void* spl_buf, size_t spl_size,
63 const void* boot_buf, size_t boot_size)
64{
65 int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size);
66 if(rc < 0) {
67 jz_log(jz, JZ_LOG_ERROR, "Boot image is invalid: %d", rc);
68 return JZ_ERR_BAD_FILE_FORMAT;
69 }
70
71 rc = jz_identify_x1000_spl(spl_buf, spl_size);
72 if(rc < 0) {
73 jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc);
74 return JZ_ERR_BAD_FILE_FORMAT;
75 }
76
77 if(spl_size > SPL_SIZE) {
78 jz_log(jz, JZ_LOG_ERROR, "SPL is too big");
79 return JZ_ERR_BAD_FILE_FORMAT;
80 }
81
82 rc = jz_identify_scramble_image(boot_buf, boot_size);
83 if(rc < 0) {
84 jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc);
85 return JZ_ERR_BAD_FILE_FORMAT;
86 }
87
88 if(boot_size > BOOT_SIZE) {
89 jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big");
90 return JZ_ERR_BAD_FILE_FORMAT;
91 }
92
93 uint8_t* imgdat = (uint8_t*)image_buf;
94 memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE);
95 memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size);
96 memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE);
97 memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size);
98 return JZ_SUCCESS;
99}
100
101#define IMGBUF 0
102#define SPLBUF 1
103#define BOOTBUF 2
104#define NUMBUFS 3
105#define IMGBUF_NAME "image"
106#define SPLBUF_NAME "spl"
107#define BOOTBUF_NAME "bootloader"
108#define FIIOM3K_INIT_WORKSTATE {0}
109
110struct fiiom3k_workstate {
111 jz_usbdev* dev;
112 jz_buffer* bufs[NUMBUFS];
113};
114
115static void fiiom3k_action_cleanup(struct fiiom3k_workstate* state)
116{
117 for(int i = 0; i < NUMBUFS; ++i)
118 if(state->bufs[i])
119 jz_buffer_free(state->bufs[i]);
120
121 if(state->dev)
122 jz_usb_close(state->dev);
123}
124
125static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl,
126 struct fiiom3k_workstate* state, int idx)
127{
128 const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME};
129
130 if(state->bufs[idx])
131 return JZ_SUCCESS;
132
133 const char* filename = jz_paramlist_get(pl, paramnames[idx]);
134 if(!filename) {
135 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]);
136 return JZ_ERR_OTHER;
137 }
138
139 int rc = jz_buffer_load(&state->bufs[idx], filename);
140 if(rc < 0) {
141 jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename);
142 return rc;
143 }
144
145 return JZ_SUCCESS;
146}
147
148static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl,
149 struct fiiom3k_workstate* state)
150{
151 const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K);
152 if(!info)
153 return JZ_ERR_OTHER;
154
155 int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF);
156 if(rc < 0)
157 return rc;
158
159 jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x",
160 (unsigned int)info->vendor_id, (unsigned int)info->product_id);
161 rc = jz_usb_open(jz, &state->dev, info->vendor_id, info->product_id);
162 if(rc < 0)
163 return rc;
164
165 jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access");
166 jz_buffer* splbuf = state->bufs[SPLBUF];
167 return jz_x1000_setup(state->dev, splbuf->size, splbuf->data);
168}
169
170int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl)
171{
172 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
173 int rc;
174
175 rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF);
176 if(rc < 0)
177 goto error;
178
179 rc = fiiom3k_action_setup(jz, pl, &state);
180 if(rc < 0)
181 goto error;
182
183 jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device");
184 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]);
185 if(rc < 0)
186 goto error;
187
188 jz_buffer* img_buf = state.bufs[IMGBUF];
189 const char* backupfile = jz_paramlist_get(pl, "backup");
190 const char* without_backup = jz_paramlist_get(pl, "without-backup");
191 if(backupfile) {
192 jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile);
193 rc = jz_buffer_save(img_buf, backupfile);
194 if(rc < 0) {
195 jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile);
196 goto error;
197 }
198 } else if(!without_backup || strcmp(without_backup, "yes")) {
199 jz_log(jz, JZ_LOG_ERROR, "No --backup option given and --without-backup yes not specified");
200 jz_log(jz, JZ_LOG_ERROR, "Refusing to flash a new bootloader without taking a backup");
201 goto error;
202 }
203
204 jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader");
205 jz_buffer* boot_buf = state.bufs[BOOTBUF];
206 jz_buffer* spl_buf = state.bufs[SPLBUF];
207 rc = jz_fiiom3k_patchboot(jz, img_buf->data, img_buf->size,
208 spl_buf->data, spl_buf->size,
209 boot_buf->data, boot_buf->size);
210 if(rc < 0) {
211 jz_log(jz, JZ_LOG_ERROR, "Error patching image: %d", rc);
212 goto error;
213 }
214
215 jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device");
216 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data);
217 if(rc < 0)
218 goto error;
219
220 rc = JZ_SUCCESS;
221
222 error:
223 fiiom3k_action_cleanup(&state);
224 return rc;
225}
226
227int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl)
228{
229 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
230 int rc;
231
232 const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME);
233 if(!outfile_path) {
234 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME);
235 rc = JZ_ERR_OTHER;
236 goto error;
237 }
238
239 rc = fiiom3k_action_setup(jz, pl, &state);
240 if(rc < 0)
241 goto error;
242
243 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]);
244 if(rc < 0)
245 goto error;
246
247 rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path);
248 if(rc < 0) {
249 jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path);
250 goto error;
251 }
252
253 rc = JZ_SUCCESS;
254
255 error:
256 fiiom3k_action_cleanup(&state);
257 return rc;
258}
259
260int jz_fiiom3k_restore(jz_context* jz, jz_paramlist* pl)
261{
262 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE;
263 int rc;
264
265 rc = fiiom3k_action_loadbuf(jz, pl, &state, IMGBUF);
266 if(rc < 0)
267 goto error;
268
269 rc = fiiom3k_action_setup(jz, pl, &state);
270 if(rc < 0)
271 goto error;
272
273 jz_buffer* img_buf = state.bufs[IMGBUF];
274 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data);
275 if(rc < 0)
276 goto error;
277
278 rc = JZ_SUCCESS;
279
280 error:
281 fiiom3k_action_cleanup(&state);
282 return rc;
283}
diff --git a/rbutil/jztool/src/identify_file.c b/rbutil/jztool/src/identify_file.c
new file mode 100644
index 0000000000..3bf4a9ced7
--- /dev/null
+++ b/rbutil/jztool/src/identify_file.c
@@ -0,0 +1,179 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <string.h>
24
25/* Following is copied from mkspl-x1000, basically */
26struct x1000_spl_header {
27 uint8_t magic[8];
28 uint8_t type;
29 uint8_t crc7;
30 uint8_t ppb;
31 uint8_t bpp;
32 uint32_t length;
33};
34
35static const uint8_t x1000_spl_header_magic[8] =
36 {0x06, 0x05, 0x04, 0x03, 0x02, 0x55, 0xaa, 0x55};
37
38static const size_t X1000_SPL_HEADER_SIZE = 2 * 1024;
39
40static uint8_t crc7(const uint8_t* buf, size_t len)
41{
42 /* table-based computation of CRC7 */
43 static const uint8_t t[256] = {
44 0x00, 0x09, 0x12, 0x1b, 0x24, 0x2d, 0x36, 0x3f,
45 0x48, 0x41, 0x5a, 0x53, 0x6c, 0x65, 0x7e, 0x77,
46 0x19, 0x10, 0x0b, 0x02, 0x3d, 0x34, 0x2f, 0x26,
47 0x51, 0x58, 0x43, 0x4a, 0x75, 0x7c, 0x67, 0x6e,
48 0x32, 0x3b, 0x20, 0x29, 0x16, 0x1f, 0x04, 0x0d,
49 0x7a, 0x73, 0x68, 0x61, 0x5e, 0x57, 0x4c, 0x45,
50 0x2b, 0x22, 0x39, 0x30, 0x0f, 0x06, 0x1d, 0x14,
51 0x63, 0x6a, 0x71, 0x78, 0x47, 0x4e, 0x55, 0x5c,
52 0x64, 0x6d, 0x76, 0x7f, 0x40, 0x49, 0x52, 0x5b,
53 0x2c, 0x25, 0x3e, 0x37, 0x08, 0x01, 0x1a, 0x13,
54 0x7d, 0x74, 0x6f, 0x66, 0x59, 0x50, 0x4b, 0x42,
55 0x35, 0x3c, 0x27, 0x2e, 0x11, 0x18, 0x03, 0x0a,
56 0x56, 0x5f, 0x44, 0x4d, 0x72, 0x7b, 0x60, 0x69,
57 0x1e, 0x17, 0x0c, 0x05, 0x3a, 0x33, 0x28, 0x21,
58 0x4f, 0x46, 0x5d, 0x54, 0x6b, 0x62, 0x79, 0x70,
59 0x07, 0x0e, 0x15, 0x1c, 0x23, 0x2a, 0x31, 0x38,
60 0x41, 0x48, 0x53, 0x5a, 0x65, 0x6c, 0x77, 0x7e,
61 0x09, 0x00, 0x1b, 0x12, 0x2d, 0x24, 0x3f, 0x36,
62 0x58, 0x51, 0x4a, 0x43, 0x7c, 0x75, 0x6e, 0x67,
63 0x10, 0x19, 0x02, 0x0b, 0x34, 0x3d, 0x26, 0x2f,
64 0x73, 0x7a, 0x61, 0x68, 0x57, 0x5e, 0x45, 0x4c,
65 0x3b, 0x32, 0x29, 0x20, 0x1f, 0x16, 0x0d, 0x04,
66 0x6a, 0x63, 0x78, 0x71, 0x4e, 0x47, 0x5c, 0x55,
67 0x22, 0x2b, 0x30, 0x39, 0x06, 0x0f, 0x14, 0x1d,
68 0x25, 0x2c, 0x37, 0x3e, 0x01, 0x08, 0x13, 0x1a,
69 0x6d, 0x64, 0x7f, 0x76, 0x49, 0x40, 0x5b, 0x52,
70 0x3c, 0x35, 0x2e, 0x27, 0x18, 0x11, 0x0a, 0x03,
71 0x74, 0x7d, 0x66, 0x6f, 0x50, 0x59, 0x42, 0x4b,
72 0x17, 0x1e, 0x05, 0x0c, 0x33, 0x3a, 0x21, 0x28,
73 0x5f, 0x56, 0x4d, 0x44, 0x7b, 0x72, 0x69, 0x60,
74 0x0e, 0x07, 0x1c, 0x15, 0x2a, 0x23, 0x38, 0x31,
75 0x46, 0x4f, 0x54, 0x5d, 0x62, 0x6b, 0x70, 0x79
76 };
77
78 uint8_t crc = 0;
79 while(len--)
80 crc = t[(crc << 1) ^ *buf++];
81 return crc;
82}
83
84int jz_identify_x1000_spl(const void* data, size_t len)
85{
86 /* Use <= check because a header-only file is not really valid,
87 * it should have at least one byte in it... */
88 if(len <= X1000_SPL_HEADER_SIZE)
89 return JZ_IDERR_WRONG_SIZE;
90
91 /* Look for header magic bytes */
92 const struct x1000_spl_header* header = (const struct x1000_spl_header*)data;
93 if(memcmp(header->magic, x1000_spl_header_magic, 8))
94 return JZ_IDERR_BAD_HEADER;
95
96 /* Length stored in the header should equal the length of the file */
97 if(header->length != len)
98 return JZ_IDERR_WRONG_SIZE;
99
100 /* Compute the CRC7 checksum; it only covers the SPL code */
101 const uint8_t* dat = (const uint8_t*)data;
102 uint8_t sum = crc7(&dat[X1000_SPL_HEADER_SIZE], len - X1000_SPL_HEADER_SIZE);
103 if(header->crc7 != sum)
104 return JZ_IDERR_BAD_CHECKSUM;
105
106 return JZ_SUCCESS;
107
108}
109
110static const struct scramble_model_info {
111 const char* name;
112 int model_num;
113} scramble_models[] = {
114 {"fiio", 114},
115 {NULL, 0},
116};
117
118int jz_identify_scramble_image(const void* data, size_t len)
119{
120 /* 4 bytes checksum + 4 bytes player model */
121 if(len < 8)
122 return JZ_IDERR_WRONG_SIZE;
123
124 /* Look up the model number */
125 const uint8_t* dat = (const uint8_t*)data;
126 const struct scramble_model_info* model_info = &scramble_models[0];
127 for(; model_info->name != NULL; ++model_info)
128 if(!memcmp(&dat[4], model_info->name, 4))
129 break;
130
131 if(model_info->name == NULL)
132 return JZ_IDERR_UNRECOGNIZED_MODEL;
133
134 /* Compute the checksum */
135 uint32_t sum = model_info->model_num;
136 for(size_t i = 8; i < len; ++i)
137 sum += dat[i];
138
139 /* Compare with file's checksum, it's stored in big-endian form */
140 uint32_t fsum = (dat[0] << 24) | (dat[1] << 16) | (dat[2] << 8) | dat[3];
141 if(sum != fsum)
142 return JZ_IDERR_BAD_CHECKSUM;
143
144 return JZ_SUCCESS;
145}
146
147int jz_identify_fiiom3k_bootimage(const void* data, size_t len)
148{
149 /* The bootloader image is simply a dump of the first NAND eraseblock,
150 * so it has a fixed 128 KiB size */
151 if(len != 128*1024)
152 return JZ_IDERR_WRONG_SIZE;
153
154 /* We'll verify the embedded SPL, but we have to drag out the correct
155 * length from the header. Length should be more than 12 KiB, due to
156 * limitations of the hardware */
157 const struct x1000_spl_header* spl_header;
158 spl_header = (const struct x1000_spl_header*)data;
159 if(spl_header->length > 12 * 1024)
160 return JZ_IDERR_BAD_HEADER;
161
162 int rc = jz_identify_x1000_spl(data, spl_header->length);
163 if(rc < 0)
164 return rc;
165
166 const uint8_t* dat = (const uint8_t*)data;
167
168 /* Check the partition table is present */
169 if(memcmp(&dat[0x3c00], "nand", 4))
170 return JZ_IDERR_OTHER;
171
172 /* Check first bytes of PDMA firmware. It doesn't change
173 * between OF versions, and Rockbox doesn't modify it. */
174 static const uint8_t pdma_fw[] = {0x54, 0x25, 0x42, 0xb3, 0x70, 0x25, 0x42, 0xb3};
175 if(memcmp(&dat[0x4000], pdma_fw, sizeof(pdma_fw)))
176 return JZ_IDERR_OTHER;
177
178 return JZ_SUCCESS;
179}
diff --git a/rbutil/jztool/src/jztool_private.h b/rbutil/jztool/src/jztool_private.h
new file mode 100644
index 0000000000..11299f21f9
--- /dev/null
+++ b/rbutil/jztool/src/jztool_private.h
@@ -0,0 +1,44 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#ifndef JZTOOL_PRIVATE_H
23#define JZTOOL_PRIVATE_H
24
25#include "jztool.h"
26#include <libusb.h>
27
28struct jz_context {
29 void* user_data;
30 jz_log_cb log_cb;
31 jz_log_level log_level;
32 libusb_context* usb_ctx;
33 int usb_ctxref;
34};
35
36struct jz_usbdev {
37 jz_context* jz;
38 libusb_device_handle* handle;
39};
40
41int jz_context_ref_libusb(jz_context* jz);
42void jz_context_unref_libusb(jz_context* jz);
43
44#endif /* JZTOOL_PRIVATE_H */
diff --git a/rbutil/jztool/src/paramlist.c b/rbutil/jztool/src/paramlist.c
new file mode 100644
index 0000000000..05bcf97a13
--- /dev/null
+++ b/rbutil/jztool/src/paramlist.c
@@ -0,0 +1,135 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool.h"
23#include <stdlib.h>
24#include <string.h>
25#include <stdbool.h>
26
27struct jz_paramlist {
28 int size;
29 char** keys;
30 char** values;
31};
32
33static int jz_paramlist_extend(jz_paramlist* pl, int count)
34{
35 int nsize = pl->size + count;
36
37 /* Reallocate key list */
38 char** nkeys = realloc(pl->keys, nsize * sizeof(char*));
39 if(!nkeys)
40 return JZ_ERR_OUT_OF_MEMORY;
41
42 for(int i = pl->size; i < nsize; ++i)
43 nkeys[i] = NULL;
44
45 pl->keys = nkeys;
46
47 /* Reallocate value list */
48 char** nvalues = realloc(pl->values, nsize * sizeof(char*));
49 if(!nvalues)
50 return JZ_ERR_OUT_OF_MEMORY;
51
52 for(int i = pl->size; i < nsize; ++i)
53 nvalues[i] = NULL;
54
55 pl->values = nvalues;
56
57 pl->size = nsize;
58 return JZ_SUCCESS;
59}
60
61jz_paramlist* jz_paramlist_new(void)
62{
63 jz_paramlist* pl = malloc(sizeof(struct jz_paramlist));
64 if(!pl)
65 return NULL;
66
67 pl->size = 0;
68 pl->keys = NULL;
69 pl->values = NULL;
70 return pl;
71}
72
73void jz_paramlist_free(jz_paramlist* pl)
74{
75 for(int i = 0; i < pl->size; ++i) {
76 free(pl->keys[i]);
77 free(pl->values[i]);
78 }
79
80 if(pl->size > 0) {
81 free(pl->keys);
82 free(pl->values);
83 }
84
85 free(pl);
86}
87
88int jz_paramlist_set(jz_paramlist* pl, const char* param, const char* value)
89{
90 int pos = -1;
91 for(int i = 0; i < pl->size; ++i) {
92 if(!pl->keys[i] || !strcmp(pl->keys[i], param)) {
93 pos = i;
94 break;
95 }
96 }
97
98 if(pos == -1) {
99 pos = pl->size;
100 int rc = jz_paramlist_extend(pl, 1);
101 if(rc < 0)
102 return rc;
103 }
104
105 bool need_key = (pl->keys[pos] == NULL);
106 if(need_key) {
107 char* newparam = strdup(param);
108 if(!newparam)
109 return JZ_ERR_OUT_OF_MEMORY;
110
111 pl->keys[pos] = newparam;
112 }
113
114 char* newvalue = strdup(value);
115 if(!newvalue) {
116 if(need_key) {
117 free(pl->keys[pos]);
118 pl->keys[pos] = NULL;
119 }
120
121 return JZ_ERR_OUT_OF_MEMORY;
122 }
123
124 pl->values[pos] = newvalue;
125 return JZ_SUCCESS;
126}
127
128const char* jz_paramlist_get(jz_paramlist* pl, const char* param)
129{
130 for(int i = 0; i < pl->size; ++i)
131 if(pl->keys[i] && !strcmp(pl->keys[i], param))
132 return pl->values[i];
133
134 return NULL;
135}
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c
new file mode 100644
index 0000000000..7e4a5f3388
--- /dev/null
+++ b/rbutil/jztool/src/usb.c
@@ -0,0 +1,203 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool_private.h"
23#include <stdlib.h>
24#include <stdbool.h>
25
26#define VR_GET_CPU_INFO 0
27#define VR_SET_DATA_ADDRESS 1
28#define VR_SET_DATA_LENGTH 2
29#define VR_FLUSH_CACHES 3
30#define VR_PROGRAM_START1 4
31#define VR_PROGRAM_START2 5
32
33int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id)
34{
35 int rc;
36 jz_usbdev* dev = NULL;
37 libusb_device_handle* usb_handle = NULL;
38 libusb_device** dev_list = NULL;
39 ssize_t dev_index = -1, dev_count;
40
41 rc = jz_context_ref_libusb(jz);
42 if(rc < 0)
43 return rc;
44
45 dev = malloc(sizeof(struct jz_usbdev));
46 if(!dev) {
47 rc = JZ_ERR_OUT_OF_MEMORY;
48 goto error;
49 }
50
51 dev_count = libusb_get_device_list(jz->usb_ctx, &dev_list);
52 if(dev_count < 0) {
53 jz_log(jz, JZ_LOG_ERROR, "libusb_get_device_list: %s", libusb_strerror(dev_count));
54 rc = JZ_ERR_USB;
55 goto error;
56 }
57
58 for(ssize_t i = 0; i < dev_count; ++i) {
59 struct libusb_device_descriptor desc;
60 rc = libusb_get_device_descriptor(dev_list[i], &desc);
61 if(rc < 0) {
62 jz_log(jz, JZ_LOG_WARNING, "libusb_get_device_descriptor: %s",
63 libusb_strerror(rc));
64 continue;
65 }
66
67 if(desc.idVendor != vend_id || desc.idProduct != prod_id)
68 continue;
69
70 if(dev_index >= 0) {
71 /* not the best, but it is the safest thing */
72 jz_log(jz, JZ_LOG_ERROR, "Multiple devices match ID %04x:%04x",
73 (unsigned int)vend_id, (unsigned int)prod_id);
74 jz_log(jz, JZ_LOG_ERROR, "Please ensure only one player is plugged in, and try again");
75 rc = JZ_ERR_NO_DEVICE;
76 goto error;
77 }
78
79 dev_index = i;
80 }
81
82 if(dev_index < 0) {
83 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found",
84 (unsigned int)vend_id, (unsigned int)prod_id);
85 rc = JZ_ERR_NO_DEVICE;
86 goto error;
87 }
88
89 rc = libusb_open(dev_list[dev_index], &usb_handle);
90 if(rc < 0) {
91 jz_log(jz, JZ_LOG_ERROR, "libusb_open: %s", libusb_strerror(rc));
92 rc = JZ_ERR_USB;
93 goto error;
94 }
95
96 rc = libusb_claim_interface(usb_handle, 0);
97 if(rc < 0) {
98 jz_log(jz, JZ_LOG_ERROR, "libusb_claim_interface: %s", libusb_strerror(rc));
99 rc = JZ_ERR_USB;
100 goto error;
101 }
102
103 dev->jz = jz;
104 dev->handle = usb_handle;
105 *devptr = dev;
106 rc = JZ_SUCCESS;
107
108 exit:
109 if(dev_list)
110 libusb_free_device_list(dev_list, true);
111 return rc;
112
113 error:
114 if(dev)
115 free(dev);
116 if(usb_handle)
117 libusb_close(usb_handle);
118 jz_context_unref_libusb(jz);
119 goto exit;
120}
121
122void jz_usb_close(jz_usbdev* dev)
123{
124 libusb_release_interface(dev->handle, 0);
125 libusb_close(dev->handle);
126 jz_context_unref_libusb(dev->jz);
127 free(dev);
128}
129
130static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
131{
132 int rc = libusb_control_transfer(dev->handle,
133 LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE,
134 req, arg >> 16, arg & 0xffff, NULL, 0, 1000);
135
136 if(rc < 0) {
137 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc));
138 rc = JZ_ERR_USB;
139 } else {
140 rc = JZ_SUCCESS;
141 }
142
143 return rc;
144}
145
146static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
147{
148 int xfered = 0;
149 int ep = write ? LIBUSB_ENDPOINT_OUT|1 : LIBUSB_ENDPOINT_IN|1;
150 int rc = libusb_bulk_transfer(dev->handle, ep, buf, len, &xfered, 10000);
151
152 if(rc < 0) {
153 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: %s", libusb_strerror(rc));
154 rc = JZ_ERR_USB;
155 } else if(xfered != (int)len) {
156 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered");
157 rc = JZ_ERR_USB;
158 } else {
159 rc = JZ_SUCCESS;
160 }
161
162 return rc;
163}
164
165static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
166 size_t len, void* data)
167{
168 int rc;
169 rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr);
170 if(rc < 0)
171 return rc;
172
173 rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len);
174 if(rc < 0)
175 return rc;
176
177 return jz_usb_transfer(dev, write, len, data);
178}
179
180int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
181{
182 return jz_usb_sendrecv(dev, true, addr, len, (void*)data);
183}
184
185int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
186{
187 return jz_usb_sendrecv(dev, false, addr, len, data);
188}
189
190int jz_usb_start1(jz_usbdev* dev, uint32_t addr)
191{
192 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr);
193}
194
195int jz_usb_start2(jz_usbdev* dev, uint32_t addr)
196{
197 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr);
198}
199
200int jz_usb_flush_caches(jz_usbdev* dev)
201{
202 return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0);
203}
diff --git a/rbutil/jztool/src/x1000.c b/rbutil/jztool/src/x1000.c
new file mode 100644
index 0000000000..049344e5e6
--- /dev/null
+++ b/rbutil/jztool/src/x1000.c
@@ -0,0 +1,193 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 Aidan MacDonald
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "jztool_private.h"
23#include "../../../firmware/target/mips/ingenic_x1000/spl-x1000-defs.h"
24#include "../../../firmware/target/mips/ingenic_x1000/nand-x1000-err.h"
25#include <endian.h> // TODO: portability
26#include <string.h>
27
28static const char* jz_x1000_nand_strerror(int rc)
29{
30 switch(rc) {
31 case NANDERR_CHIP_UNSUPPORTED:
32 return "Chip unsupported";
33 case NANDERR_WRITE_PROTECTED:
34 return "Operation forbidden by write-protect";
35 case NANDERR_UNALIGNED_ADDRESS:
36 return "Improperly aligned address";
37 case NANDERR_UNALIGNED_LENGTH:
38 return "Improperly aligned length";
39 case NANDERR_READ_FAILED:
40 return "Read operation failed";
41 case NANDERR_ECC_FAILED:
42 return "Uncorrectable ECC error on read";
43 case NANDERR_ERASE_FAILED:
44 return "Erase operation failed";
45 case NANDERR_PROGRAM_FAILED:
46 return "Program operation failed";
47 case NANDERR_COMMAND_FAILED:
48 return "NAND command failed";
49 default:
50 return "Unknown NAND error";
51 }
52}
53
54
55static int jz_x1000_send_args(jz_usbdev* dev, struct x1000_spl_arguments* args)
56{
57 args->command = htole32(args->command);
58 args->param1 = htole32(args->param1);
59 args->param2 = htole32(args->param2);
60 args->flags = htole32(args->flags);
61 return jz_usb_send(dev, SPL_ARGUMENTS_ADDRESS, sizeof(*args), args);
62}
63
64static int jz_x1000_recv_status(jz_usbdev* dev, struct x1000_spl_status* status)
65{
66 int rc = jz_usb_recv(dev, SPL_STATUS_ADDRESS, sizeof(*status), status);
67 if(rc < 0)
68 return rc;
69
70 status->err_code = le32toh(status->err_code);
71 status->reserved = le32toh(status->reserved);
72 return JZ_SUCCESS;
73}
74
75int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data)
76{
77 int rc = jz_identify_x1000_spl(spl_data, spl_len);
78 if(rc < 0)
79 return JZ_ERR_BAD_FILE_FORMAT;
80 if(spl_len > SPL_MAX_SIZE)
81 return JZ_ERR_BAD_FILE_FORMAT;
82
83 rc = jz_usb_send(dev, SPL_LOAD_ADDRESS, spl_len, spl_data);
84 if(rc < 0)
85 return rc;
86
87 struct x1000_spl_arguments args;
88 args.command = SPL_CMD_BOOT;
89 args.param1 = SPL_BOOTOPT_NONE;
90 args.param2 = 0;
91 args.flags = 0;
92 rc = jz_x1000_send_args(dev, &args);
93 if(rc < 0)
94 return rc;
95
96 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
97 if(rc < 0)
98 return rc;
99
100 jz_sleepms(100);
101
102 struct x1000_spl_status status;
103 rc = jz_x1000_recv_status(dev, &status);
104 if(rc < 0)
105 return rc;
106
107 if(status.err_code != 0) {
108 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 device init error: %d", status.err_code);
109 return JZ_ERR_OTHER;
110 }
111
112 return JZ_SUCCESS;
113}
114
115int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
116{
117 struct x1000_spl_arguments args;
118 args.command = SPL_CMD_FLASH_READ;
119 args.param1 = addr;
120 args.param2 = len;
121 args.flags = SPL_FLAG_SKIP_INIT;
122 int rc = jz_x1000_send_args(dev, &args);
123 if(rc < 0)
124 return rc;
125
126 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
127 if(rc < 0)
128 return rc;
129
130 jz_sleepms(500);
131
132 struct x1000_spl_status status;
133 rc = jz_x1000_recv_status(dev, &status);
134 if(rc < 0)
135 return rc;
136
137 if(status.err_code != 0) {
138 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash read error: %s",
139 jz_x1000_nand_strerror(status.err_code));
140 return JZ_ERR_FLASH_ERROR;
141 }
142
143 return jz_usb_recv(dev, SPL_BUFFER_ADDRESS, len, data);
144}
145
146int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
147{
148 int rc = jz_usb_send(dev, SPL_BUFFER_ADDRESS, len, data);
149 if(rc < 0)
150 return rc;
151
152 struct x1000_spl_arguments args;
153 args.command = SPL_CMD_FLASH_WRITE;
154 args.param1 = addr;
155 args.param2 = len;
156 args.flags = SPL_FLAG_SKIP_INIT;
157 rc = jz_x1000_send_args(dev, &args);
158 if(rc < 0)
159 return rc;
160
161 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
162 if(rc < 0)
163 return rc;
164
165 jz_sleepms(500);
166
167 struct x1000_spl_status status;
168 rc = jz_x1000_recv_status(dev, &status);
169 if(rc < 0)
170 return rc;
171
172 if(status.err_code != 0) {
173 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash write error: %s",
174 jz_x1000_nand_strerror(status.err_code));
175 return JZ_ERR_FLASH_ERROR;
176 }
177
178 return JZ_SUCCESS;
179}
180
181int jz_x1000_boot_rockbox(jz_usbdev* dev)
182{
183 struct x1000_spl_arguments args;
184 args.command = SPL_CMD_BOOT;
185 args.param1 = SPL_BOOTOPT_ROCKBOX;
186 args.param2 = 0;
187 args.flags = 0;
188 int rc = jz_x1000_send_args(dev, &args);
189 if(rc < 0)
190 return rc;
191
192 return jz_usb_start1(dev, SPL_EXEC_ADDRESS);
193}