summaryrefslogtreecommitdiff
path: root/rbutil/jztool/src
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/jztool/src')
-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
7 files changed, 272 insertions, 598 deletions
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 @@
27#include <stdio.h> 27#include <stdio.h>
28#include <time.h> 28#include <time.h>
29 29
30#ifdef WIN32
31# include <windows.h>
32#endif
33
30/** \brief Allocate a library context 34/** \brief Allocate a library context
31 * \returns New context or NULL if out of memory 35 * \returns New context or NULL if out of memory
32 */ 36 */
@@ -137,13 +141,18 @@ void jz_log_cb_stderr(jz_log_level lev, const char* msg)
137 */ 141 */
138void jz_sleepms(int ms) 142void jz_sleepms(int ms)
139{ 143{
144#ifdef WIN32
145 Sleep(ms);
146#else
140 struct timespec ts; 147 struct timespec ts;
141 long ns = ms % 1000; 148 long ns = ms % 1000;
142 ts.tv_nsec = ns * 1000 * 1000; 149 ts.tv_nsec = ns * 1000 * 1000;
143 ts.tv_sec = ms / 1000; 150 ts.tv_sec = ms / 1000;
144 nanosleep(&ts, NULL); 151 nanosleep(&ts, NULL);
152#endif
145} 153}
146 154
155/** \brief Add reference to libusb context, allocating it if necessary */
147int jz_context_ref_libusb(jz_context* jz) 156int jz_context_ref_libusb(jz_context* jz)
148{ 157{
149 if(jz->usb_ctxref == 0) { 158 if(jz->usb_ctxref == 0) {
@@ -158,6 +167,7 @@ int jz_context_ref_libusb(jz_context* jz)
158 return JZ_SUCCESS; 167 return JZ_SUCCESS;
159} 168}
160 169
170/** \brief Remove reference to libusb context, freeing if it hits zero */
161void jz_context_unref_libusb(jz_context* jz) 171void jz_context_unref_libusb(jz_context* jz)
162{ 172{
163 if(--jz->usb_ctxref == 0) { 173 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 @@
22#include "jztool.h" 22#include "jztool.h"
23#include <string.h> 23#include <string.h>
24 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[] = { 25static const jz_device_info infotable[] = {
50 { 26 {
51 .name = "fiiom3k", 27 .name = "fiiom3k",
@@ -54,10 +30,6 @@ static const jz_device_info infotable[] = {
54 .cpu_type = JZ_CPU_X1000, 30 .cpu_type = JZ_CPU_X1000,
55 .vendor_id = 0xa108, 31 .vendor_id = 0xa108,
56 .product_id = 0x1000, 32 .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 }, 33 },
62}; 34};
63 35
@@ -69,6 +41,7 @@ int jz_get_num_device_info(void)
69 return infotable_size; 41 return infotable_size;
70} 42}
71 43
44/** \brief Lookup info for a device by type, returns NULL if not found. */
72const jz_device_info* jz_get_device_info(jz_device_type type) 45const jz_device_info* jz_get_device_info(jz_device_type type)
73{ 46{
74 for(int i = 0; i < infotable_size; ++i) 47 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 @@
20 ****************************************************************************/ 20 ****************************************************************************/
21 21
22#include "jztool.h" 22#include "jztool.h"
23#include "jztool_private.h"
24#include "microtar.h"
25#include "ucl/ucl.h"
26#include <stdbool.h>
23#include <string.h> 27#include <string.h>
24 28
25#define IMAGE_ADDR 0 29static uint32_t xread32(const uint8_t* d)
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{ 30{
34 jz_buffer* buf = jz_buffer_alloc(IMAGE_SIZE, NULL); 31 uint32_t r = 0;
35 if(!buf) 32 r |= d[0] << 24;
36 return JZ_ERR_OUT_OF_MEMORY; 33 r |= d[1] << 16;
37 34 r |= d[2] << 8;
38 int rc = jz_x1000_read_flash(dev, IMAGE_ADDR, buf->size, buf->data); 35 r |= d[3] << 0;
39 if(rc < 0) { 36 return r;
40 jz_buffer_free(buf);
41 return rc;
42 }
43
44 *bufptr = buf;
45 return JZ_SUCCESS;
46} 37}
47 38
48int jz_fiiom3k_writeboot(jz_usbdev* dev, size_t image_size, const void* image_buf) 39/* adapted from firmware/common/ucl_decompress.c */
40static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len,
41 uint32_t* dst_len)
49{ 42{
50 int rc = jz_identify_fiiom3k_bootimage(image_buf, image_size); 43 static const uint8_t magic[8] =
51 if(rc < 0 || image_size != IMAGE_SIZE) 44 {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a};
52 return JZ_ERR_BAD_FILE_FORMAT;
53 45
54 rc = jz_x1000_write_flash(dev, IMAGE_ADDR, image_size, image_buf); 46 jz_buffer* buffer = NULL;
55 if(rc < 0)
56 return rc;
57 47
58 return JZ_SUCCESS; 48 /* make sure there are enough bytes for the header */
59} 49 if(src_len < 18)
50 goto error;
60 51
61int jz_fiiom3k_patchboot(jz_context* jz, void* image_buf, size_t image_size, 52 /* avoid memcmp for reasons of code size */
62 const void* spl_buf, size_t spl_size, 53 for(size_t i = 0; i < sizeof(magic); ++i)
63 const void* boot_buf, size_t boot_size) 54 if(src[i] != magic[i])
64{ 55 goto error;
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 56
71 rc = jz_identify_x1000_spl(spl_buf, spl_size); 57 /* read the other header fields */
72 if(rc < 0) { 58 /* uint32_t flags = xread32(&src[8]); */
73 jz_log(jz, JZ_LOG_ERROR, "SPL image is invalid: %d", rc); 59 uint8_t method = src[12];
74 return JZ_ERR_BAD_FILE_FORMAT; 60 /* uint8_t level = src[13]; */
75 } 61 uint32_t block_size = xread32(&src[14]);
76 62
77 if(spl_size > SPL_SIZE) { 63 /* check supported compression method */
78 jz_log(jz, JZ_LOG_ERROR, "SPL is too big"); 64 if(method != 0x2e)
79 return JZ_ERR_BAD_FILE_FORMAT; 65 goto error;
80 }
81 66
82 rc = jz_identify_scramble_image(boot_buf, boot_size); 67 /* validate */
83 if(rc < 0) { 68 if(block_size < 1024 || block_size > 8*1024*1024)
84 jz_log(jz, JZ_LOG_ERROR, "Bootloader image is invalid: %d", rc); 69 goto error;
85 return JZ_ERR_BAD_FILE_FORMAT;
86 }
87 70
88 if(boot_size > BOOT_SIZE) { 71 src += 18;
89 jz_log(jz, JZ_LOG_ERROR, "Bootloader is too big"); 72 src_len -= 18;
90 return JZ_ERR_BAD_FILE_FORMAT; 73
91 } 74 /* Calculate amount of space that we might need & allocate a buffer:
75 * - subtract 4 to account for end of file marker
76 * - each block is block_size bytes + 8 bytes of header
77 * - add one to nr_blocks to account for case where file size < block size
78 * - total size = max uncompressed size of block * nr_blocks
79 */
80 uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1;
81 uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256);
82 buffer = jz_buffer_alloc(max_size, NULL);
83 if(!buffer)
84 goto error;
92 85
93 uint8_t* imgdat = (uint8_t*)image_buf; 86 /* perform the decompression */
94 memset(&imgdat[SPL_OFFSET], 0xff, SPL_SIZE); 87 uint32_t dst_ilen = buffer->size;
95 memcpy(&imgdat[SPL_OFFSET], spl_buf, spl_size); 88 uint8_t* dst = buffer->data;
96 memset(&imgdat[BOOT_OFFSET], 0xff, BOOT_SIZE); 89 while(1) {
97 memcpy(&imgdat[BOOT_OFFSET], boot_buf, boot_size); 90 if(src_len < 4)
98 return JZ_SUCCESS; 91 goto error;
99}
100 92
101#define IMGBUF 0 93 uint32_t out_len = xread32(src); src += 4, src_len -= 4;
102#define SPLBUF 1 94 if(out_len == 0)
103#define BOOTBUF 2 95 break;
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 96
121 if(state->dev) 97 if(src_len < 4)
122 jz_usb_close(state->dev); 98 goto error;
123}
124 99
125static int fiiom3k_action_loadbuf(jz_context* jz, jz_paramlist* pl, 100 uint32_t in_len = xread32(src); src += 4, src_len -= 4;
126 struct fiiom3k_workstate* state, int idx) 101 if(in_len > block_size || out_len > block_size ||
127{ 102 in_len == 0 || in_len > out_len)
128 const char* const paramnames[] = {IMGBUF_NAME, SPLBUF_NAME, BOOTBUF_NAME}; 103 goto error;
129 104
130 if(state->bufs[idx]) 105 if(src_len < in_len)
131 return JZ_SUCCESS; 106 goto error;
132 107
133 const char* filename = jz_paramlist_get(pl, paramnames[idx]); 108 if(in_len < out_len) {
134 if(!filename) { 109 uint32_t actual_out_len = dst_ilen;
135 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", paramnames[idx]); 110 int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL);
136 return JZ_ERR_OTHER; 111 if(rc != UCL_E_OK)
137 } 112 goto error;
113 if(actual_out_len != out_len)
114 goto error;
115 } else {
116 for(size_t i = 0; i < in_len; ++i)
117 dst[i] = src[i];
118 }
138 119
139 int rc = jz_buffer_load(&state->bufs[idx], filename); 120 src += in_len;
140 if(rc < 0) { 121 src_len -= in_len;
141 jz_log(jz, JZ_LOG_ERROR, "Error reading '%s' file (%d): %s", paramnames[idx], rc, filename); 122 dst += out_len;
142 return rc; 123 dst_ilen -= out_len;
143 } 124 }
144 125
145 return JZ_SUCCESS; 126 /* subtract leftover number of bytes to get size of compressed output */
127 *dst_len = buffer->size - dst_ilen;
128 return buffer;
129
130 error:
131 jz_buffer_free(buffer);
132 return NULL;
146} 133}
147 134
148static int fiiom3k_action_setup(jz_context* jz, jz_paramlist* pl, 135static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf)
149 struct fiiom3k_workstate* state)
150{ 136{
151 const jz_device_info* info = jz_get_device_info(JZ_DEVICE_FIIOM3K); 137 int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data);
152 if(!info) 138 if(rc < 0)
153 return JZ_ERR_OTHER; 139 return rc;
154 140
155 int rc = fiiom3k_action_loadbuf(jz, pl, state, SPLBUF); 141 return jz_usb_start1(dev, 0xf4001800);
142}
143
144static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf)
145{
146 int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data);
156 if(rc < 0) 147 if(rc < 0)
157 return rc; 148 return rc;
158 149
159 jz_log(jz, JZ_LOG_DETAIL, "Open USB device %04x:%04x", 150 rc = jz_usb_flush_caches(dev);
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) 151 if(rc < 0)
163 return rc; 152 return rc;
164 153
165 jz_log(jz, JZ_LOG_DETAIL, "Setup device for flash access"); 154 return jz_usb_start2(dev, 0x80004000);
166 jz_buffer* splbuf = state->bufs[SPLBUF];
167 return jz_x1000_setup(state->dev, splbuf->size, splbuf->data);
168} 155}
169 156
170int jz_fiiom3k_install(jz_context* jz, jz_paramlist* pl) 157static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file,
158 bool decompress, jz_buffer** buf)
171{ 159{
172 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; 160 jz_buffer* buffer = NULL;
161 mtar_header_t h;
173 int rc; 162 int rc;
174 163
175 rc = fiiom3k_action_loadbuf(jz, pl, &state, BOOTBUF); 164 rc = mtar_find(tar, file, &h);
176 if(rc < 0) 165 if(rc != MTAR_ESUCCESS) {
177 goto error; 166 jz_log(jz, JZ_LOG_ERROR, "can't find %s in boot file, tar error %d", file, rc);
167 return JZ_ERR_BAD_FILE_FORMAT;
168 }
178 169
179 rc = fiiom3k_action_setup(jz, pl, &state); 170 buffer = jz_buffer_alloc(h.size, NULL);
180 if(rc < 0) 171 if(!buffer)
181 goto error; 172 return JZ_ERR_OUT_OF_MEMORY;
182 173
183 jz_log(jz, JZ_LOG_DETAIL, "Reading boot image from device"); 174 rc = mtar_read_data(tar, buffer->data, buffer->size);
184 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); 175 if(rc != MTAR_ESUCCESS) {
185 if(rc < 0) 176 jz_buffer_free(buffer);
186 goto error; 177 jz_log(jz, JZ_LOG_ERROR, "can't read %s in boot file, tar error %d", file, rc);
178 return JZ_ERR_BAD_FILE_FORMAT;
179 }
187 180
188 jz_buffer* img_buf = state.bufs[IMGBUF]; 181 if(decompress) {
189 const char* backupfile = jz_paramlist_get(pl, "backup"); 182 uint32_t dst_len;
190 const char* without_backup = jz_paramlist_get(pl, "without-backup"); 183 jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len);
191 if(backupfile) { 184 jz_buffer_free(buffer);
192 jz_log(jz, JZ_LOG_DETAIL, "Backup original boot image to file: %s", backupfile); 185 if(!nbuf) {
193 rc = jz_buffer_save(img_buf, backupfile); 186 jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file);
194 if(rc < 0) { 187 return JZ_ERR_BAD_FILE_FORMAT;
195 jz_log(jz, JZ_LOG_ERROR, "Error saving backup image file (%d): %s", rc, backupfile);
196 goto error;
197 } 188 }
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 189
204 jz_log(jz, JZ_LOG_DETAIL, "Patching image with new SPL/bootloader"); 190 /* for simplicity just forget original size of buffer */
205 jz_buffer* boot_buf = state.bufs[BOOTBUF]; 191 nbuf->size = dst_len;
206 jz_buffer* spl_buf = state.bufs[SPLBUF]; 192 buffer = nbuf;
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 } 193 }
214 194
215 jz_log(jz, JZ_LOG_DETAIL, "Writing patched image to device"); 195 *buf = buffer;
216 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); 196 return JZ_SUCCESS;
217 if(rc < 0) 197}
218 goto error;
219 198
220 rc = JZ_SUCCESS; 199static int m3k_show_version(jz_context* jz, jz_buffer* info_file)
200{
201 /* Extract the version string and log it for informational purposes */
202 char* boot_version = (char*)info_file->data;
203 char* endpos = memchr(boot_version, '\n', info_file->size);
204 if(!endpos) {
205 jz_log(jz, JZ_LOG_ERROR, "invalid metadata in boot file");
206 return JZ_ERR_BAD_FILE_FORMAT;
207 }
221 208
222 error: 209 *endpos = 0;
223 fiiom3k_action_cleanup(&state); 210 jz_log(jz, JZ_LOG_NOTICE, "Rockbox bootloader version: %s", boot_version);
224 return rc; 211 return JZ_SUCCESS;
225} 212}
226 213
227int jz_fiiom3k_backup(jz_context* jz, jz_paramlist* pl) 214/** \brief Load the Rockbox bootloader on the FiiO M3K
215 * \param dev USB device freshly returned by jz_usb_open()
216 * \param filename Path to the "bootloader.m3k" file
217 * \return either JZ_SUCCESS or an error code
218 */
219int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename)
228{ 220{
229 struct fiiom3k_workstate state = FIIOM3K_INIT_WORKSTATE; 221 jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL;
222 mtar_t tar;
230 int rc; 223 int rc;
231 224
232 const char* outfile_path = jz_paramlist_get(pl, IMGBUF_NAME); 225 rc = mtar_open(&tar, filename, "r");
233 if(!outfile_path) { 226 if(rc != MTAR_ESUCCESS) {
234 jz_log(jz, JZ_LOG_ERROR, "Missing required parameter '%s'", IMGBUF_NAME); 227 jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc);
235 rc = JZ_ERR_OTHER; 228 return JZ_ERR_OPEN_FILE;
236 goto error;
237 } 229 }
238 230
239 rc = fiiom3k_action_setup(jz, pl, &state); 231 /* Extract all necessary files */
240 if(rc < 0) 232 rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl);
233 if(rc != JZ_SUCCESS)
241 goto error; 234 goto error;
242 235
243 rc = jz_fiiom3k_readboot(state.dev, &state.bufs[IMGBUF]); 236 rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader);
244 if(rc < 0) 237 if(rc != JZ_SUCCESS)
245 goto error; 238 goto error;
246 239
247 rc = jz_buffer_save(state.bufs[IMGBUF], outfile_path); 240 rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file);
248 if(rc < 0) { 241 if(rc != JZ_SUCCESS)
249 jz_log(jz, JZ_LOG_ERROR, "Error writing '%s' file (%d): %s", IMGBUF_NAME, rc, outfile_path);
250 goto error; 242 goto error;
251 }
252
253 rc = JZ_SUCCESS;
254 243
255 error: 244 /* Display the version string */
256 fiiom3k_action_cleanup(&state); 245 rc = m3k_show_version(dev->jz, info_file);
257 return rc; 246 if(rc != JZ_SUCCESS)
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; 247 goto error;
268 248
269 rc = fiiom3k_action_setup(jz, pl, &state); 249 /* Stage1 boot of SPL to set up hardware */
270 if(rc < 0) 250 rc = m3k_stage1(dev, spl);
251 if(rc != JZ_SUCCESS)
271 goto error; 252 goto error;
272 253
273 jz_buffer* img_buf = state.bufs[IMGBUF]; 254 /* Need a bit of time for SPL to handle init */
274 rc = jz_fiiom3k_writeboot(state.dev, img_buf->size, img_buf->data); 255 jz_sleepms(500);
275 if(rc < 0) 256
257 /* Stage2 boot into the bootloader's recovery menu
258 * User has to take manual action from there */
259 rc = m3k_stage2(dev, bootloader);
260 if(rc != JZ_SUCCESS)
276 goto error; 261 goto error;
277 262
278 rc = JZ_SUCCESS; 263 rc = JZ_SUCCESS;
279 264
280 error: 265 error:
281 fiiom3k_action_cleanup(&state); 266 if(spl)
267 jz_buffer_free(spl);
268 if(bootloader)
269 jz_buffer_free(bootloader);
270 if(info_file)
271 jz_buffer_free(info_file);
272 mtar_close(&tar);
282 return rc; 273 return rc;
283} 274}
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)
81 return crc; 81 return crc;
82} 82}
83 83
84/** \brief Identify a file as an SPL for X1000 CPUs
85 * \param data File data buffer
86 * \param len Length of file
87 * \return JZ_SUCCESS if file looks correct, or one of the following errors
88 * \retval JZ_IDERR_WRONG_SIZE file too small or size doesn't match header
89 * \retval JZ_IDERR_BAD_HEADER missing magic bytes from header
90 * \retval JZ_IDERR_BAD_CHECKSUM CRC7 mismatch
91 */
84int jz_identify_x1000_spl(const void* data, size_t len) 92int jz_identify_x1000_spl(const void* data, size_t len)
85{ 93{
86 /* Use <= check because a header-only file is not really valid, 94 /* Use <= check because a header-only file is not really valid,
@@ -115,6 +123,14 @@ static const struct scramble_model_info {
115 {NULL, 0}, 123 {NULL, 0},
116}; 124};
117 125
126/** \brief Identify a file as a Rockbox `scramble` image
127 * \param data File data buffer
128 * \param len Length of file
129 * \return JZ_SUCCESS if file looks correct, or one of the following errors
130 * \retval JZ_IDERR_WRONG_SIZE file too small to be valid
131 * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type
132 * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch
133 */
118int jz_identify_scramble_image(const void* data, size_t len) 134int jz_identify_scramble_image(const void* data, size_t len)
119{ 135{
120 /* 4 bytes checksum + 4 bytes player model */ 136 /* 4 bytes checksum + 4 bytes player model */
@@ -143,37 +159,3 @@ int jz_identify_scramble_image(const void* data, size_t len)
143 159
144 return JZ_SUCCESS; 160 return JZ_SUCCESS;
145} 161}
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/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 @@
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
index 7e4a5f3388..c101f2be77 100644
--- a/rbutil/jztool/src/usb.c
+++ b/rbutil/jztool/src/usb.c
@@ -30,6 +30,16 @@
30#define VR_PROGRAM_START1 4 30#define VR_PROGRAM_START1 4
31#define VR_PROGRAM_START2 5 31#define VR_PROGRAM_START2 5
32 32
33/** \brief Open a USB device
34 * \param jz Context
35 * \param devptr Returns pointer to the USB device upon success
36 * \param vend_id USB vendor ID
37 * \param prod_id USB product ID
38 * \return either JZ_SUCCESS if device was opened, or an error below
39 * \retval JZ_ERR_OUT_OF_MEMORY malloc failed
40 * \retval JZ_ERR_USB libusb error (details are logged)
41 * \retval JZ_ERR_NO_DEVICE can't unambiguously find the device
42 */
33int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id) 43int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t prod_id)
34{ 44{
35 int rc; 45 int rc;
@@ -80,7 +90,7 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
80 } 90 }
81 91
82 if(dev_index < 0) { 92 if(dev_index < 0) {
83 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%05x found", 93 jz_log(jz, JZ_LOG_ERROR, "No device with ID %04x:%04x found",
84 (unsigned int)vend_id, (unsigned int)prod_id); 94 (unsigned int)vend_id, (unsigned int)prod_id);
85 rc = JZ_ERR_NO_DEVICE; 95 rc = JZ_ERR_NO_DEVICE;
86 goto error; 96 goto error;
@@ -100,6 +110,8 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
100 goto error; 110 goto error;
101 } 111 }
102 112
113 jz_log(jz, JZ_LOG_DEBUG, "Opened device (%p, ID %04x:%04x)",
114 dev, (unsigned int)vend_id, (unsigned int)prod_id);
103 dev->jz = jz; 115 dev->jz = jz;
104 dev->handle = usb_handle; 116 dev->handle = usb_handle;
105 *devptr = dev; 117 *devptr = dev;
@@ -119,14 +131,20 @@ int jz_usb_open(jz_context* jz, jz_usbdev** devptr, uint16_t vend_id, uint16_t p
119 goto exit; 131 goto exit;
120} 132}
121 133
134/** \brief Close a USB device
135 * \param dev Device to close; memory will be freed automatically
136 */
122void jz_usb_close(jz_usbdev* dev) 137void jz_usb_close(jz_usbdev* dev)
123{ 138{
139 jz_log(dev->jz, JZ_LOG_DEBUG, "Closing device (%p)", dev);
124 libusb_release_interface(dev->handle, 0); 140 libusb_release_interface(dev->handle, 0);
125 libusb_close(dev->handle); 141 libusb_close(dev->handle);
126 jz_context_unref_libusb(dev->jz); 142 jz_context_unref_libusb(dev->jz);
127 free(dev); 143 free(dev);
128} 144}
129 145
146// Does an Ingenic-specific vendor request
147// Written with X1000 in mind but other Ingenic CPUs have the same commands
130static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) 148static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg)
131{ 149{
132 int rc = libusb_control_transfer(dev->handle, 150 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)
137 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); 155 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc));
138 rc = JZ_ERR_USB; 156 rc = JZ_ERR_USB;
139 } else { 157 } else {
158 static const char* req_names[] = {
159 "GET_CPU_INFO",
160 "SET_DATA_ADDRESS",
161 "SET_DATA_LENGTH",
162 "FLUSH_CACHES",
163 "PROGRAM_START1",
164 "PROGRAM_START2",
165 };
166
167 jz_log(dev->jz, JZ_LOG_DEBUG, "Issued %s %08lu",
168 req_names[req], (unsigned long)arg);
140 rc = JZ_SUCCESS; 169 rc = JZ_SUCCESS;
141 } 170 }
142 171
143 return rc; 172 return rc;
144} 173}
145 174
175// Bulk transfer wrapper
146static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf) 176static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
147{ 177{
148 int xfered = 0; 178 int xfered = 0;
@@ -156,12 +186,16 @@ static int jz_usb_transfer(jz_usbdev* dev, bool write, size_t len, void* buf)
156 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered"); 186 jz_log(dev->jz, JZ_LOG_ERROR, "libusb_bulk_transfer: incorrect amount of data transfered");
157 rc = JZ_ERR_USB; 187 rc = JZ_ERR_USB;
158 } else { 188 } else {
189 jz_log(dev->jz, JZ_LOG_DEBUG, "Transferred %zu bytes %s",
190 len, write ? "to device" : "from device");
159 rc = JZ_SUCCESS; 191 rc = JZ_SUCCESS;
160 } 192 }
161 193
162 return rc; 194 return rc;
163} 195}
164 196
197// Memory send/receive primitive, performs the necessary vendor requests
198// and then tranfers data using the bulk endpoint
165static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, 199static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
166 size_t len, void* data) 200 size_t len, void* data)
167{ 201{
@@ -177,26 +211,54 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr,
177 return jz_usb_transfer(dev, write, len, data); 211 return jz_usb_transfer(dev, write, len, data);
178} 212}
179 213
214/** \brief Write data to device memory
215 * \param dev USB device
216 * \param addr Address where data should be written
217 * \param len Length of the data, in bytes, should be positive
218 * \param data Data buffer
219 * \return either JZ_SUCCESS on success or a failure code
220 */
180int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data) 221int jz_usb_send(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
181{ 222{
182 return jz_usb_sendrecv(dev, true, addr, len, (void*)data); 223 return jz_usb_sendrecv(dev, true, addr, len, (void*)data);
183} 224}
184 225
226/** \brief Read data to device memory
227 * \param dev USB device
228 * \param addr Address to read from
229 * \param len Length of the data, in bytes, should be positive
230 * \param data Data buffer
231 * \return either JZ_SUCCESS on success or a failure code
232 */
185int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) 233int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
186{ 234{
187 return jz_usb_sendrecv(dev, false, addr, len, data); 235 return jz_usb_sendrecv(dev, false, addr, len, data);
188} 236}
189 237
238/** \brief Execute stage1 program jumping to the specified address
239 * \param dev USB device
240 * \param addr Address to begin execution at
241 * \return either JZ_SUCCESS on success or a failure code
242 */
190int jz_usb_start1(jz_usbdev* dev, uint32_t addr) 243int jz_usb_start1(jz_usbdev* dev, uint32_t addr)
191{ 244{
192 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr); 245 return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr);
193} 246}
194 247
248/** \brief Execute stage2 program jumping to the specified address
249 * \param dev USB device
250 * \param addr Address to begin execution at
251 * \return either JZ_SUCCESS on success or a failure code
252 */
195int jz_usb_start2(jz_usbdev* dev, uint32_t addr) 253int jz_usb_start2(jz_usbdev* dev, uint32_t addr)
196{ 254{
197 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr); 255 return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr);
198} 256}
199 257
258/** \brief Ask device to flush CPU caches
259 * \param dev USB device
260 * \return either JZ_SUCCESS on success or a failure code
261 */
200int jz_usb_flush_caches(jz_usbdev* dev) 262int jz_usb_flush_caches(jz_usbdev* dev)
201{ 263{
202 return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0); 264 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 @@
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 <string.h>
26
27static uint32_t to_le32(uint32_t x)
28{
29 union { uint32_t u; uint8_t p[4]; } f;
30 f.p[0] = x & 0xff;
31 f.p[1] = (x >> 8) & 0xff;
32 f.p[2] = (x >> 16) & 0xff;
33 f.p[3] = (x >> 24) & 0xff;
34 return f.u;
35}
36
37static uint32_t from_le32(uint32_t x)
38{
39 union { uint32_t u; uint8_t p[4]; } f;
40 f.u = x;
41 return f.p[0] | (f.p[1] << 8) | (f.p[2] << 16) | (f.p[3] << 24);
42}
43
44static const char* jz_x1000_nand_strerror(int rc)
45{
46 switch(rc) {
47 case NANDERR_CHIP_UNSUPPORTED:
48 return "Chip unsupported";
49 case NANDERR_WRITE_PROTECTED:
50 return "Operation forbidden by write-protect";
51 case NANDERR_UNALIGNED_ADDRESS:
52 return "Improperly aligned address";
53 case NANDERR_UNALIGNED_LENGTH:
54 return "Improperly aligned length";
55 case NANDERR_READ_FAILED:
56 return "Read operation failed";
57 case NANDERR_ECC_FAILED:
58 return "Uncorrectable ECC error on read";
59 case NANDERR_ERASE_FAILED:
60 return "Erase operation failed";
61 case NANDERR_PROGRAM_FAILED:
62 return "Program operation failed";
63 case NANDERR_COMMAND_FAILED:
64 return "NAND command failed";
65 default:
66 return "Unknown NAND error";
67 }
68}
69
70
71static int jz_x1000_send_args(jz_usbdev* dev, struct x1000_spl_arguments* args)
72{
73 args->command = to_le32(args->command);
74 args->param1 = to_le32(args->param1);
75 args->param2 = to_le32(args->param2);
76 args->flags = to_le32(args->flags);
77 return jz_usb_send(dev, SPL_ARGUMENTS_ADDRESS, sizeof(*args), args);
78}
79
80static int jz_x1000_recv_status(jz_usbdev* dev, struct x1000_spl_status* status)
81{
82 int rc = jz_usb_recv(dev, SPL_STATUS_ADDRESS, sizeof(*status), status);
83 if(rc < 0)
84 return rc;
85
86 status->err_code = from_le32(status->err_code);
87 status->reserved = from_le32(status->reserved);
88 return JZ_SUCCESS;
89}
90
91int jz_x1000_setup(jz_usbdev* dev, size_t spl_len, const void* spl_data)
92{
93 int rc = jz_identify_x1000_spl(spl_data, spl_len);
94 if(rc < 0)
95 return JZ_ERR_BAD_FILE_FORMAT;
96 if(spl_len > SPL_MAX_SIZE)
97 return JZ_ERR_BAD_FILE_FORMAT;
98
99 rc = jz_usb_send(dev, SPL_LOAD_ADDRESS, spl_len, spl_data);
100 if(rc < 0)
101 return rc;
102
103 struct x1000_spl_arguments args;
104 args.command = SPL_CMD_BOOT;
105 args.param1 = SPL_BOOTOPT_NONE;
106 args.param2 = 0;
107 args.flags = 0;
108 rc = jz_x1000_send_args(dev, &args);
109 if(rc < 0)
110 return rc;
111
112 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
113 if(rc < 0)
114 return rc;
115
116 jz_sleepms(100);
117
118 struct x1000_spl_status status;
119 rc = jz_x1000_recv_status(dev, &status);
120 if(rc < 0)
121 return rc;
122
123 if(status.err_code != 0) {
124 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 device init error: %d", status.err_code);
125 return JZ_ERR_OTHER;
126 }
127
128 return JZ_SUCCESS;
129}
130
131int jz_x1000_read_flash(jz_usbdev* dev, uint32_t addr, size_t len, void* data)
132{
133 struct x1000_spl_arguments args;
134 args.command = SPL_CMD_FLASH_READ;
135 args.param1 = addr;
136 args.param2 = len;
137 args.flags = SPL_FLAG_SKIP_INIT;
138 int rc = jz_x1000_send_args(dev, &args);
139 if(rc < 0)
140 return rc;
141
142 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
143 if(rc < 0)
144 return rc;
145
146 jz_sleepms(500);
147
148 struct x1000_spl_status status;
149 rc = jz_x1000_recv_status(dev, &status);
150 if(rc < 0)
151 return rc;
152
153 if(status.err_code != 0) {
154 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash read error: %s",
155 jz_x1000_nand_strerror(status.err_code));
156 return JZ_ERR_FLASH_ERROR;
157 }
158
159 return jz_usb_recv(dev, SPL_BUFFER_ADDRESS, len, data);
160}
161
162int jz_x1000_write_flash(jz_usbdev* dev, uint32_t addr, size_t len, const void* data)
163{
164 int rc = jz_usb_send(dev, SPL_BUFFER_ADDRESS, len, data);
165 if(rc < 0)
166 return rc;
167
168 struct x1000_spl_arguments args;
169 args.command = SPL_CMD_FLASH_WRITE;
170 args.param1 = addr;
171 args.param2 = len;
172 args.flags = SPL_FLAG_SKIP_INIT;
173 rc = jz_x1000_send_args(dev, &args);
174 if(rc < 0)
175 return rc;
176
177 rc = jz_usb_start1(dev, SPL_EXEC_ADDRESS);
178 if(rc < 0)
179 return rc;
180
181 jz_sleepms(500);
182
183 struct x1000_spl_status status;
184 rc = jz_x1000_recv_status(dev, &status);
185 if(rc < 0)
186 return rc;
187
188 if(status.err_code != 0) {
189 jz_log(dev->jz, JZ_LOG_ERROR, "X1000 flash write error: %s",
190 jz_x1000_nand_strerror(status.err_code));
191 return JZ_ERR_FLASH_ERROR;
192 }
193
194 return JZ_SUCCESS;
195}
196
197int jz_x1000_boot_rockbox(jz_usbdev* dev)
198{
199 struct x1000_spl_arguments args;
200 args.command = SPL_CMD_BOOT;
201 args.param1 = SPL_BOOTOPT_ROCKBOX;
202 args.param2 = 0;
203 args.flags = 0;
204 int rc = jz_x1000_send_args(dev, &args);
205 if(rc < 0)
206 return rc;
207
208 return jz_usb_start1(dev, SPL_EXEC_ADDRESS);
209}