summaryrefslogtreecommitdiff
path: root/rbutil/jztool/src
diff options
context:
space:
mode:
Diffstat (limited to 'rbutil/jztool/src')
-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
9 files changed, 1436 insertions, 0 deletions
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}