diff options
-rw-r--r-- | rbutil/jztool/Makefile | 2 | ||||
-rw-r--r-- | rbutil/jztool/README.md | 85 | ||||
-rw-r--r-- | rbutil/jztool/include/jztool.h | 41 | ||||
-rw-r--r-- | rbutil/jztool/jztool.c | 79 | ||||
-rw-r--r-- | rbutil/jztool/src/device_info.c | 72 | ||||
-rw-r--r-- | rbutil/jztool/src/identify_file.c | 37 | ||||
-rw-r--r-- | rbutil/jztool/src/ucl_unpack.c | 128 | ||||
-rw-r--r-- | rbutil/jztool/src/usb.c | 40 | ||||
-rw-r--r-- | rbutil/jztool/src/x1000.c (renamed from rbutil/jztool/src/fiiom3k.c) | 149 |
9 files changed, 408 insertions, 225 deletions
diff --git a/rbutil/jztool/Makefile b/rbutil/jztool/Makefile index 5e397c59aa..d1bbae578c 100644 --- a/rbutil/jztool/Makefile +++ b/rbutil/jztool/Makefile | |||
@@ -15,7 +15,7 @@ CFLAGS += -O0 -ggdb | |||
15 | endif | 15 | endif |
16 | 16 | ||
17 | LIBSOURCES := src/buffer.c src/context.c src/device_info.c \ | 17 | LIBSOURCES := src/buffer.c src/context.c src/device_info.c \ |
18 | src/identify_file.c src/fiiom3k.c src/usb.c | 18 | src/identify_file.c src/ucl_unpack.c src/usb.c src/x1000.c |
19 | SOURCES := $(LIBSOURCES) jztool.c | 19 | SOURCES := $(LIBSOURCES) jztool.c |
20 | EXTRADEPS := libucl.a libmicrotar.a | 20 | EXTRADEPS := libucl.a libmicrotar.a |
21 | 21 | ||
diff --git a/rbutil/jztool/README.md b/rbutil/jztool/README.md index 2c4dd4992c..72c630c6c3 100644 --- a/rbutil/jztool/README.md +++ b/rbutil/jztool/README.md | |||
@@ -1,26 +1,45 @@ | |||
1 | # jztool -- Ingenic device utility & bootloader installer | 1 | # jztool -- Ingenic device utility & bootloader installer |
2 | 2 | ||
3 | The `jztool` utility can help install, backup, and restore the bootloader on | 3 | The `jztool` utility can help install, backup, and restore the bootloader on |
4 | Rockbox players based on a supported Ingenic SoC. | 4 | Rockbox players based on a supported Ingenic SoC (currently only the X1000). |
5 | 5 | ||
6 | ## FiiO M3K | 6 | ## Running jztool |
7 | 7 | ||
8 | First, get a copy of the `bootloader.m3k` file, either by downloading it | 8 | ### Getting a bootloader |
9 | from <https://rockbox.org>, or by compiling it yourself (choose 'B'ootloader | ||
10 | build when configuring your build). | ||
11 | 9 | ||
12 | The first time you install Rockbox, you need to load the Rockbox bootloader | 10 | To use `jztool` you need to compile or download a bootloader for your player. |
13 | over USB by entering USB boot mode. The easiest way to do this is by plugging | 11 | It's recommended to use only official released bootloaders, since bootloaders |
14 | in the microUSB cable to the M3K and holding the VOL- button while plugging | 12 | compiled from Git are not tested and might be buggy. |
15 | the USB into your computer. If you entered USB boot mode, the button light | ||
16 | will turn on but the LCD will remain black. | ||
17 | 13 | ||
18 | Copy the `bootloader.m3k` next to the `jztool` executable and follow the | 14 | You can download released bootloaders from <https://download.rockbox.org/>. |
19 | instructions below which are appropriate to your OS. | ||
20 | 15 | ||
21 | ### Running jztool | 16 | The bootloader file is named after the target: for example, the FiiO M3K |
17 | bootloader is called `bootloader.m3k`. The FiiO M3K is used as an example | ||
18 | here, but the instructions apply to all X1000-based players. | ||
22 | 19 | ||
23 | #### Linux/Mac | 20 | Use `jztool --help` to find out the model name of your player. |
21 | |||
22 | ### Entering USB boot mode | ||
23 | |||
24 | USB boot mode is a low-level mode provided by the CPU which allows a computer | ||
25 | to load firmware onto the device. You need to put your player into this mode | ||
26 | manually before using `jztool` (unfortunately, it can't be done automatically.) | ||
27 | |||
28 | To connect the player in USB boot mode, follow these steps: | ||
29 | |||
30 | 1. Ensure the player is fully powered off. | ||
31 | 2. Plug one end of the USB cable into your player. | ||
32 | 3. Hold down your player's USB boot key (see below). | ||
33 | 4. Plug the other end of the USB cable into your computer. | ||
34 | 5. Let go of the USB boot key. | ||
35 | |||
36 | The USB boot key depends on your player: | ||
37 | |||
38 | - FiiO M3K: Volume Down | ||
39 | - Shanling Q1: Play | ||
40 | - Eros Q: Menu | ||
41 | |||
42 | ### Linux/Mac | ||
24 | 43 | ||
25 | Run the following command in a terminal. Note that on Linux, you will need to | 44 | Run the following command in a terminal. Note that on Linux, you will need to |
26 | have root access to allow libusb to access the USB device. | 45 | have root access to allow libusb to access the USB device. |
@@ -32,9 +51,9 @@ have root access to allow libusb to access the USB device. | |||
32 | $ ./jztool fiiom3k load bootloader.m3k | 51 | $ ./jztool fiiom3k load bootloader.m3k |
33 | ``` | 52 | ``` |
34 | 53 | ||
35 | #### Windows | 54 | ### Windows |
36 | 55 | ||
37 | To allow `jztool` access to the M3K in USB boot mode, you need to install | 56 | To allow `jztool` access to your player in USB boot mode, you need to install |
38 | the WinUSB driver. The recommended way to install it is using Zadig, which | 57 | the WinUSB driver. The recommended way to install it is using Zadig, which |
39 | may be downloaded from its homepage <https://zadig.akeo.ie>. Please note | 58 | may be downloaded from its homepage <https://zadig.akeo.ie>. Please note |
40 | this is 3rd party software not maintained or supported by Rockbox developers. | 59 | this is 3rd party software not maintained or supported by Rockbox developers. |
@@ -42,10 +61,10 @@ this is 3rd party software not maintained or supported by Rockbox developers. | |||
42 | 61 | ||
43 | When running Zadig you must select the WinUSB driver; the other driver options | 62 | When running Zadig you must select the WinUSB driver; the other driver options |
44 | will not work properly with `jztool`. You will have to select the correct USB | 63 | will not work properly with `jztool`. You will have to select the correct USB |
45 | device in Zadig -- the name and USB IDs of the M3K in USB boot mode are listed | 64 | device in Zadig. All X1000-based players use the same USB ID while in USB boot |
46 | below. NOTE: the device name may show only as "X" and a hollow square in Zadig. | 65 | mode, listed below. NOTE: the device name may show only as "X" and a hollow |
47 | The IDs will not change, so those are the most reliable way to confirm you have | 66 | square in Zadig. The IDs will not change, so those are the most reliable way |
48 | selected the correct device. | 67 | to confirm you have selected the correct device. |
49 | 68 | ||
50 | ``` | 69 | ``` |
51 | Name: Ingenic Semiconductor Co.,Ltd X1000 | 70 | Name: Ingenic Semiconductor Co.,Ltd X1000 |
@@ -63,21 +82,27 @@ Type the following command to load the Rockbox bootloader: | |||
63 | $ jztool.exe fiiom3k load bootloader.m3k | 82 | $ jztool.exe fiiom3k load bootloader.m3k |
64 | ``` | 83 | ``` |
65 | 84 | ||
66 | ### Further instructions | 85 | ## Using the recovery menu |
67 | 86 | ||
68 | After running `jztool` successfully your M3K will display the recovery menu | 87 | If `jztool` runs successfully your player will display the Rockbox bootloader's |
69 | of the Rockbox bootloader. If you want to permanently install Rockbox to your | 88 | recovery menu. If you want to permanently install Rockbox to your device, copy |
70 | M3K, copy `bootloader.m3k` to the root of an SD card, insert it to your device, | 89 | the bootloader file you downloaded to the root of your SD card, insert the SD |
71 | then choose "Install/update bootloader" from the menu. | 90 | card to your player, and choose "Install/update bootloader" from the menu. |
72 | 91 | ||
73 | It is _highly_ recommended that you take a backup of your existing bootloader | 92 | It is _highly_ recommended that you take a backup of your existing bootloader |
74 | in case of any trouble -- choose "Backup bootloader" from the recovery menu. | 93 | in case of any trouble -- choose "Backup bootloader" from the recovery menu. |
75 | The backup file is called "fiiom3k-boot.bin" and will be saved to the root of | 94 | The backup file is called `PLAYER-boot.bin`, where `PLAYER` is the model name. |
76 | the SD card. If you need to restore it, simply place the file at the root of | 95 | (Example: `fiiom3k-boot.bin`.) |
77 | your SD card and select "Restore bootloader". | 96 | |
97 | You can restore the backup later by putting it on the root of your SD card and | ||
98 | selecting "Restor bootloader" in the recovery menu. | ||
99 | |||
100 | After installing the Rockbox bootloader, you can access the recovery menu by | ||
101 | holding a key while booting: | ||
78 | 102 | ||
79 | In the future if you want to backup, restore, or update the bootloader, you | 103 | - FiiO M3K: Volume Up |
80 | can access the Rockbox bootloader's recovery menu by holding VOL+ when booting. | 104 | - Shanling Q1: Next (button on the lower left) |
105 | - Eros Q: Volume Up | ||
81 | 106 | ||
82 | ### Known issues | 107 | ### Known issues |
83 | 108 | ||
diff --git a/rbutil/jztool/include/jztool.h b/rbutil/jztool/include/jztool.h index df51fe9f44..9d3c08c5bc 100644 --- a/rbutil/jztool/include/jztool.h +++ b/rbutil/jztool/include/jztool.h | |||
@@ -33,9 +33,12 @@ extern "C" { | |||
33 | * Types, enumerations, etc | 33 | * Types, enumerations, etc |
34 | */ | 34 | */ |
35 | 35 | ||
36 | #define JZ_CPUINFO_BUFLEN 9 | ||
37 | |||
36 | typedef struct jz_context jz_context; | 38 | typedef struct jz_context jz_context; |
37 | typedef struct jz_usbdev jz_usbdev; | 39 | typedef struct jz_usbdev jz_usbdev; |
38 | typedef struct jz_device_info jz_device_info; | 40 | typedef struct jz_device_info jz_device_info; |
41 | typedef struct jz_cpu_info jz_cpu_info; | ||
39 | typedef struct jz_buffer jz_buffer; | 42 | typedef struct jz_buffer jz_buffer; |
40 | 43 | ||
41 | typedef enum jz_error jz_error; | 44 | typedef enum jz_error jz_error; |
@@ -77,19 +80,46 @@ enum jz_log_level { | |||
77 | 80 | ||
78 | enum jz_device_type { | 81 | enum jz_device_type { |
79 | JZ_DEVICE_FIIOM3K, | 82 | JZ_DEVICE_FIIOM3K, |
83 | JZ_DEVICE_SHANLINGQ1, | ||
84 | JZ_DEVICE_EROSQ, | ||
85 | JZ_NUM_DEVICES, | ||
80 | }; | 86 | }; |
81 | 87 | ||
82 | enum jz_cpu_type { | 88 | enum jz_cpu_type { |
83 | JZ_CPU_X1000, | 89 | JZ_CPU_X1000, |
90 | JZ_NUM_CPUS, | ||
84 | }; | 91 | }; |
85 | 92 | ||
86 | struct jz_device_info { | 93 | struct jz_device_info { |
94 | /* internal device name and file extension */ | ||
87 | const char* name; | 95 | const char* name; |
96 | const char* file_ext; | ||
97 | |||
98 | /* human-readable name */ | ||
88 | const char* description; | 99 | const char* description; |
100 | |||
101 | /* device and CPU type */ | ||
89 | jz_device_type device_type; | 102 | jz_device_type device_type; |
90 | jz_cpu_type cpu_type; | 103 | jz_cpu_type cpu_type; |
104 | |||
105 | /* USB IDs of the device in mass storage mode */ | ||
106 | uint16_t vendor_id; | ||
107 | uint16_t product_id; | ||
108 | }; | ||
109 | |||
110 | struct jz_cpu_info { | ||
111 | /* CPU info string, as reported by the boot ROM */ | ||
112 | const char* info_str; | ||
113 | |||
114 | /* USB IDs of the boot ROM */ | ||
91 | uint16_t vendor_id; | 115 | uint16_t vendor_id; |
92 | uint16_t product_id; | 116 | uint16_t product_id; |
117 | |||
118 | /* default addresses for running binaries */ | ||
119 | uint32_t stage1_load_addr; | ||
120 | uint32_t stage1_exec_addr; | ||
121 | uint32_t stage2_load_addr; | ||
122 | uint32_t stage2_exec_addr; | ||
93 | }; | 123 | }; |
94 | 124 | ||
95 | struct jz_buffer { | 125 | struct jz_buffer { |
@@ -119,11 +149,13 @@ void jz_sleepms(int ms); | |||
119 | * Device and file info | 149 | * Device and file info |
120 | */ | 150 | */ |
121 | 151 | ||
122 | int jz_get_num_device_info(void); | ||
123 | const jz_device_info* jz_get_device_info(jz_device_type type); | 152 | const jz_device_info* jz_get_device_info(jz_device_type type); |
124 | const jz_device_info* jz_get_device_info_named(const char* name); | 153 | const jz_device_info* jz_get_device_info_named(const char* name); |
125 | const jz_device_info* jz_get_device_info_indexed(int index); | 154 | const jz_device_info* jz_get_device_info_indexed(int index); |
126 | 155 | ||
156 | const jz_cpu_info* jz_get_cpu_info(jz_cpu_type type); | ||
157 | const jz_cpu_info* jz_get_cpu_info_named(const char* info_str); | ||
158 | |||
127 | int jz_identify_x1000_spl(const void* data, size_t len); | 159 | int jz_identify_x1000_spl(const void* data, size_t len); |
128 | int jz_identify_scramble_image(const void* data, size_t len); | 160 | int jz_identify_scramble_image(const void* data, size_t len); |
129 | 161 | ||
@@ -139,15 +171,16 @@ int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data); | |||
139 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr); | 171 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr); |
140 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr); | 172 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr); |
141 | int jz_usb_flush_caches(jz_usbdev* dev); | 173 | int jz_usb_flush_caches(jz_usbdev* dev); |
174 | int jz_usb_get_cpu_info(jz_usbdev* dev, char* buffer, size_t buflen); | ||
142 | 175 | ||
143 | /****************************************************************************** | 176 | /****************************************************************************** |
144 | * Rockbox loader (all functions are model-specific, see docs) | 177 | * Rockbox loader (all functions are model-specific, see docs) |
145 | */ | 178 | */ |
146 | 179 | ||
147 | int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename); | 180 | int jz_x1000_boot(jz_usbdev* dev, jz_device_type type, const char* filename); |
148 | 181 | ||
149 | /****************************************************************************** | 182 | /****************************************************************************** |
150 | * Simple buffer API | 183 | * Buffer API and other functions |
151 | */ | 184 | */ |
152 | 185 | ||
153 | jz_buffer* jz_buffer_alloc(size_t size, const void* data); | 186 | jz_buffer* jz_buffer_alloc(size_t size, const void* data); |
@@ -156,6 +189,8 @@ void jz_buffer_free(jz_buffer* buf); | |||
156 | int jz_buffer_load(jz_buffer** buf, const char* filename); | 189 | int jz_buffer_load(jz_buffer** buf, const char* filename); |
157 | int jz_buffer_save(jz_buffer* buf, const char* filename); | 190 | int jz_buffer_save(jz_buffer* buf, const char* filename); |
158 | 191 | ||
192 | jz_buffer* jz_ucl_unpack(const uint8_t* src, uint32_t src_len, uint32_t* dst_len); | ||
193 | |||
159 | /****************************************************************************** | 194 | /****************************************************************************** |
160 | * END | 195 | * END |
161 | */ | 196 | */ |
diff --git a/rbutil/jztool/jztool.c b/rbutil/jztool/jztool.c index 5fb6dc173f..dcd78137b3 100644 --- a/rbutil/jztool/jztool.c +++ b/rbutil/jztool/jztool.c | |||
@@ -28,49 +28,63 @@ | |||
28 | jz_context* jz = NULL; | 28 | jz_context* jz = NULL; |
29 | jz_usbdev* usbdev = NULL; | 29 | jz_usbdev* usbdev = NULL; |
30 | const jz_device_info* dev_info = NULL; | 30 | const jz_device_info* dev_info = NULL; |
31 | const jz_cpu_info* cpu_info = NULL; | ||
31 | 32 | ||
32 | void usage_fiiom3k(void) | 33 | void usage_x1000(void) |
33 | { | 34 | { |
34 | printf("Usage:\n" | 35 | printf( |
35 | " jztool fiiom3k load <bootloader.m3k>\n" | 36 | "Usage:\n" |
36 | "\n" | 37 | " jztool fiiom3k load <bootloader.m3k>\n" |
37 | "The 'load' command is used to boot the Rockbox bootloader in\n" | 38 | " jztool shanlingq1 load <bootloader.q1>\n" |
38 | "recovery mode, which allows you to install the Rockbox bootloader\n" | 39 | " jztool erosq load <bootloader.erosq>\n" |
39 | "and backup or restore bootloader images. You need to connect the\n" | 40 | "\n" |
40 | "M3K in USB boot mode in order to use this tool.\n" | 41 | "The 'load' command is used to boot the Rockbox bootloader in recovery\n" |
41 | "\n" | 42 | "mode, which allows you to install the Rockbox bootloader and backup or\n" |
42 | "On Windows, you will need to install the WinUSB driver for the M3K\n" | 43 | "restore bootloader images. You need to connect your player in USB boot\n" |
43 | "using a 3rd-party tool such as Zadig <https://zadig.akeo.ie>. For\n" | 44 | "mode in order to use this tool.\n" |
44 | "more details check the jztool README.md file or the Rockbox wiki at\n" | 45 | "\n" |
45 | "<https://rockbox.org/wiki/FiioM3K>.\n" | 46 | "To connect the player in USB boot mode, follow these steps:\n" |
46 | "\n" | 47 | "\n" |
47 | "To connect the M3K in USB boot mode, plug the microUSB into the\n" | 48 | "1. Ensure the player is fully powered off.\n" |
48 | "M3K, and hold the VOL- button while plugging the USB into your\n" | 49 | "2. Plug one end of the USB cable into your player.\n" |
49 | "computer. If successful, the button light will turn on and the\n" | 50 | "3. Hold down your player's USB boot key (see below).\n" |
50 | "LCD will remain black. If you encounter any errors and need to\n" | 51 | "4. Plug the other end of the USB cable into your computer.\n" |
51 | "reconnect the device, you must force a power off by holding POWER\n" | 52 | "5. Let go of the USB boot key.\n" |
52 | "for more than 10 seconds.\n" | 53 | "\n" |
53 | "\n" | 54 | "USB boot keys:\n" |
54 | "Once the Rockbox bootloader is installed on your M3K, you can\n" | 55 | "\n" |
55 | "access the recovery menu by holding VOL+ while powering on the\n" | 56 | " FiiO M3K - Volume Down\n" |
56 | "device.\n"); | 57 | " Shanling Q1 - Play\n" |
58 | " Eros Q - Menu\n" | ||
59 | "\n" | ||
60 | "Not all players give a visible indication that they are in USB boot mode.\n" | ||
61 | "If you're having trouble connecting your player, try resetting it by\n" | ||
62 | "holding the power button for 10 seconds, and try the above steps again.\n" | ||
63 | "\n" | ||
64 | "Note for Windows users: you need to install the WinUSB driver using a\n" | ||
65 | "3rd-party tool such as Zadig <https://zadig.akeo.ie> before this tool\n" | ||
66 | "can access your player in USB boot mode. You need to run Zadig while the\n" | ||
67 | "player is plugged in and in USB boot mode. For more details check the\n" | ||
68 | "jztool README.md file or visit <https://rockbox.org/wiki/IngenicX1000>.\n" | ||
69 | "\n"); | ||
70 | |||
57 | exit(4); | 71 | exit(4); |
58 | } | 72 | } |
59 | 73 | ||
60 | int cmdline_fiiom3k(int argc, char** argv) | 74 | int cmdline_x1000(int argc, char** argv) |
61 | { | 75 | { |
62 | if(argc < 2 || strcmp(argv[0], "load")) { | 76 | if(argc < 2 || strcmp(argv[0], "load")) { |
63 | usage_fiiom3k(); | 77 | usage_x1000(); |
64 | return 2; | 78 | return 2; |
65 | } | 79 | } |
66 | 80 | ||
67 | int rc = jz_usb_open(jz, &usbdev, dev_info->vendor_id, dev_info->product_id); | 81 | int rc = jz_usb_open(jz, &usbdev, cpu_info->vendor_id, cpu_info->product_id); |
68 | if(rc < 0) { | 82 | if(rc < 0) { |
69 | jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc); | 83 | jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc); |
70 | return 1; | 84 | return 1; |
71 | } | 85 | } |
72 | 86 | ||
73 | rc = jz_fiiom3k_boot(usbdev, argv[1]); | 87 | rc = jz_x1000_boot(usbdev, dev_info->device_type, argv[1]); |
74 | if(rc < 0) { | 88 | if(rc < 0) { |
75 | jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc); | 89 | jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc); |
76 | return 1; | 90 | return 1; |
@@ -90,8 +104,7 @@ void usage(void) | |||
90 | " -v, --verbose Display detailed logging output\n\n"); | 104 | " -v, --verbose Display detailed logging output\n\n"); |
91 | 105 | ||
92 | printf("Supported devices:\n\n"); | 106 | printf("Supported devices:\n\n"); |
93 | int n = jz_get_num_device_info(); | 107 | for(int i = 0; i < JZ_NUM_DEVICES; ++i) { |
94 | for(int i = 0; i < n; ++i) { | ||
95 | const jz_device_info* info = jz_get_device_info_indexed(i); | 108 | const jz_device_info* info = jz_get_device_info_indexed(i); |
96 | printf(" %s - %s\n", info->name, info->description); | 109 | printf(" %s - %s\n", info->name, info->description); |
97 | } | 110 | } |
@@ -182,11 +195,15 @@ int main(int argc, char** argv) | |||
182 | exit(2); | 195 | exit(2); |
183 | } | 196 | } |
184 | 197 | ||
198 | cpu_info = jz_get_cpu_info(dev_info->cpu_type); | ||
199 | |||
185 | /* Dispatch to device handler */ | 200 | /* Dispatch to device handler */ |
186 | --argc, ++argv; | 201 | --argc, ++argv; |
187 | switch(dev_info->device_type) { | 202 | switch(dev_info->device_type) { |
188 | case JZ_DEVICE_FIIOM3K: | 203 | case JZ_DEVICE_FIIOM3K: |
189 | return cmdline_fiiom3k(argc, argv); | 204 | case JZ_DEVICE_SHANLINGQ1: |
205 | case JZ_DEVICE_EROSQ: | ||
206 | return cmdline_x1000(argc, argv); | ||
190 | 207 | ||
191 | default: | 208 | default: |
192 | jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type"); | 209 | jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type"); |
diff --git a/rbutil/jztool/src/device_info.c b/rbutil/jztool/src/device_info.c index 5ce3899262..cc431959ca 100644 --- a/rbutil/jztool/src/device_info.c +++ b/rbutil/jztool/src/device_info.c | |||
@@ -22,39 +22,58 @@ | |||
22 | #include "jztool.h" | 22 | #include "jztool.h" |
23 | #include <string.h> | 23 | #include <string.h> |
24 | 24 | ||
25 | static const jz_device_info infotable[] = { | 25 | static const jz_device_info infotable[JZ_NUM_DEVICES] = { |
26 | { | 26 | [JZ_DEVICE_FIIOM3K] = { |
27 | .name = "fiiom3k", | 27 | .name = "fiiom3k", |
28 | .file_ext = "m3k", | ||
28 | .description = "FiiO M3K", | 29 | .description = "FiiO M3K", |
29 | .device_type = JZ_DEVICE_FIIOM3K, | 30 | .device_type = JZ_DEVICE_FIIOM3K, |
30 | .cpu_type = JZ_CPU_X1000, | 31 | .cpu_type = JZ_CPU_X1000, |
32 | .vendor_id = 0x2972, | ||
33 | .product_id = 0x0003, | ||
34 | }, | ||
35 | [JZ_DEVICE_SHANLINGQ1] = { | ||
36 | .name = "shanlingq1", | ||
37 | .file_ext = "q1", | ||
38 | .description = "Shanling Q1", | ||
39 | .device_type = JZ_DEVICE_SHANLINGQ1, | ||
40 | .cpu_type = JZ_CPU_X1000, | ||
41 | .vendor_id = 0x0525, | ||
42 | .product_id = 0xa4a5, | ||
43 | }, | ||
44 | [JZ_DEVICE_EROSQ] = { | ||
45 | .name = "erosq", | ||
46 | .file_ext = "erosq", | ||
47 | .description = "AIGO Eros Q", | ||
48 | .device_type = JZ_DEVICE_EROSQ, | ||
49 | .cpu_type = JZ_CPU_X1000, | ||
50 | .vendor_id = 0xc502, | ||
51 | .product_id = 0x0023, | ||
52 | }, | ||
53 | }; | ||
54 | |||
55 | static const jz_cpu_info cputable[JZ_NUM_CPUS] = { | ||
56 | [JZ_CPU_X1000] = { | ||
57 | .info_str = "X1000_v1", | ||
31 | .vendor_id = 0xa108, | 58 | .vendor_id = 0xa108, |
32 | .product_id = 0x1000, | 59 | .product_id = 0x1000, |
60 | .stage1_load_addr = 0xf4001000, | ||
61 | .stage1_exec_addr = 0xf4001800, | ||
62 | .stage2_load_addr = 0x80004000, | ||
63 | .stage2_exec_addr = 0x80004000, | ||
33 | }, | 64 | }, |
34 | }; | 65 | }; |
35 | 66 | ||
36 | static const int infotable_size = sizeof(infotable)/sizeof(struct jz_device_info); | ||
37 | |||
38 | /** \brief Get the number of entries in the device info list */ | ||
39 | int jz_get_num_device_info(void) | ||
40 | { | ||
41 | return infotable_size; | ||
42 | } | ||
43 | |||
44 | /** \brief Lookup info for a device by type, returns NULL if not found. */ | 67 | /** \brief Lookup info for a device by type, returns NULL if not found. */ |
45 | const jz_device_info* jz_get_device_info(jz_device_type type) | 68 | const jz_device_info* jz_get_device_info(jz_device_type type) |
46 | { | 69 | { |
47 | for(int i = 0; i < infotable_size; ++i) | 70 | return jz_get_device_info_indexed(type); |
48 | if(infotable[i].device_type == type) | ||
49 | return &infotable[i]; | ||
50 | |||
51 | return NULL; | ||
52 | } | 71 | } |
53 | 72 | ||
54 | /** \brief Lookup info for a device by name, returns NULL if not found. */ | 73 | /** \brief Lookup info for a device by name, returns NULL if not found. */ |
55 | const jz_device_info* jz_get_device_info_named(const char* name) | 74 | const jz_device_info* jz_get_device_info_named(const char* name) |
56 | { | 75 | { |
57 | for(int i = 0; i < infotable_size; ++i) | 76 | for(int i = 0; i < JZ_NUM_DEVICES; ++i) |
58 | if(!strcmp(infotable[i].name, name)) | 77 | if(!strcmp(infotable[i].name, name)) |
59 | return &infotable[i]; | 78 | return &infotable[i]; |
60 | 79 | ||
@@ -64,8 +83,27 @@ const jz_device_info* jz_get_device_info_named(const char* name) | |||
64 | /** \brief Get a device info entry by index, returns NULL if out of range. */ | 83 | /** \brief Get a device info entry by index, returns NULL if out of range. */ |
65 | const jz_device_info* jz_get_device_info_indexed(int index) | 84 | const jz_device_info* jz_get_device_info_indexed(int index) |
66 | { | 85 | { |
67 | if(index < infotable_size) | 86 | if(index < JZ_NUM_DEVICES) |
68 | return &infotable[index]; | 87 | return &infotable[index]; |
69 | else | 88 | else |
70 | return NULL; | 89 | return NULL; |
71 | } | 90 | } |
91 | |||
92 | /** \brief Lookup info for a CPU, returns NULL if not found. */ | ||
93 | const jz_cpu_info* jz_get_cpu_info(jz_cpu_type type) | ||
94 | { | ||
95 | if(type < JZ_NUM_CPUS) | ||
96 | return &cputable[type]; | ||
97 | else | ||
98 | return NULL; | ||
99 | } | ||
100 | |||
101 | /** \brief Lookup info for a CPU by info string, returns NULL if not found. */ | ||
102 | const jz_cpu_info* jz_get_cpu_info_named(const char* info_str) | ||
103 | { | ||
104 | for(int i = 0; i < JZ_NUM_CPUS; ++i) | ||
105 | if(!strcmp(cputable[i].info_str, info_str)) | ||
106 | return &cputable[i]; | ||
107 | |||
108 | return NULL; | ||
109 | } | ||
diff --git a/rbutil/jztool/src/identify_file.c b/rbutil/jztool/src/identify_file.c index e735075687..e475d98a3b 100644 --- a/rbutil/jztool/src/identify_file.c +++ b/rbutil/jztool/src/identify_file.c | |||
@@ -118,43 +118,52 @@ int jz_identify_x1000_spl(const void* data, size_t len) | |||
118 | static const struct scramble_model_info { | 118 | static const struct scramble_model_info { |
119 | const char* name; | 119 | const char* name; |
120 | int model_num; | 120 | int model_num; |
121 | size_t offset_crc; | ||
122 | size_t offset_name; | ||
123 | size_t offset_data; | ||
121 | } scramble_models[] = { | 124 | } scramble_models[] = { |
122 | {"fiio", 114}, | 125 | {"fiio", 114, 0, 4, 8}, |
123 | {NULL, 0}, | 126 | {"shq1", 115, 0, 4, 8}, |
127 | {"eros", 116, 0, 4, 8}, | ||
128 | {NULL, 0, 0, 0, 0}, | ||
124 | }; | 129 | }; |
125 | 130 | ||
126 | /** \brief Identify a file as a Rockbox `scramble` image | 131 | /** \brief Identify a file as a Rockbox `scramble` image |
127 | * \param data File data buffer | 132 | * \param data File data buffer |
128 | * \param len Length of file | 133 | * \param len Length of file |
129 | * \return JZ_SUCCESS if file looks correct, or one of the following errors | 134 | * \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 | 135 | * \retval JZ_IDERR_UNRECOGNIZED_MODEL unsupported/unknown model type |
132 | * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch | 136 | * \retval JZ_IDERR_BAD_CHECKSUM checksum mismatch |
133 | */ | 137 | */ |
134 | int jz_identify_scramble_image(const void* data, size_t len) | 138 | int jz_identify_scramble_image(const void* data, size_t len) |
135 | { | 139 | { |
136 | /* 4 bytes checksum + 4 bytes player model */ | 140 | const uint8_t* dat; |
137 | if(len < 8) | 141 | const struct scramble_model_info* model_info; |
138 | return JZ_IDERR_WRONG_SIZE; | 142 | uint32_t sum, file_sum; |
143 | |||
144 | dat = (const uint8_t*)data; | ||
145 | model_info = &scramble_models[0]; | ||
139 | 146 | ||
140 | /* Look up the model number */ | 147 | /* Look up the model number */ |
141 | const uint8_t* dat = (const uint8_t*)data; | 148 | for(; model_info->name != NULL; ++model_info) { |
142 | const struct scramble_model_info* model_info = &scramble_models[0]; | 149 | if(model_info->offset_name + 4 > len) |
143 | for(; model_info->name != NULL; ++model_info) | 150 | continue; |
144 | if(!memcmp(&dat[4], model_info->name, 4)) | 151 | if(!memcmp(&dat[model_info->offset_name], model_info->name, 4)) |
145 | break; | 152 | break; |
153 | } | ||
146 | 154 | ||
147 | if(model_info->name == NULL) | 155 | if(model_info->name == NULL) |
148 | return JZ_IDERR_UNRECOGNIZED_MODEL; | 156 | return JZ_IDERR_UNRECOGNIZED_MODEL; |
149 | 157 | ||
150 | /* Compute the checksum */ | 158 | /* Compute the checksum */ |
151 | uint32_t sum = model_info->model_num; | 159 | sum = model_info->model_num; |
152 | for(size_t i = 8; i < len; ++i) | 160 | for(size_t i = model_info->offset_data; i < len; ++i) |
153 | sum += dat[i]; | 161 | sum += dat[i]; |
154 | 162 | ||
155 | /* Compare with file's checksum, it's stored in big-endian form */ | 163 | /* Compare with file's checksum, it's stored in big-endian form */ |
156 | uint32_t fsum = (dat[0] << 24) | (dat[1] << 16) | (dat[2] << 8) | dat[3]; | 164 | dat += model_info->offset_crc; |
157 | if(sum != fsum) | 165 | file_sum = (dat[0] << 24) | (dat[1] << 16) | (dat[2] << 8) | dat[3]; |
166 | if(sum != file_sum) | ||
158 | return JZ_IDERR_BAD_CHECKSUM; | 167 | return JZ_IDERR_BAD_CHECKSUM; |
159 | 168 | ||
160 | return JZ_SUCCESS; | 169 | return JZ_SUCCESS; |
diff --git a/rbutil/jztool/src/ucl_unpack.c b/rbutil/jztool/src/ucl_unpack.c new file mode 100644 index 0000000000..3b199c7008 --- /dev/null +++ b/rbutil/jztool/src/ucl_unpack.c | |||
@@ -0,0 +1,128 @@ | |||
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 "ucl/ucl.h" | ||
24 | |||
25 | static uint32_t xread32(const uint8_t* d) | ||
26 | { | ||
27 | uint32_t r = 0; | ||
28 | r |= d[0] << 24; | ||
29 | r |= d[1] << 16; | ||
30 | r |= d[2] << 8; | ||
31 | r |= d[3] << 0; | ||
32 | return r; | ||
33 | } | ||
34 | |||
35 | /* adapted from firmware/common/ucl_decompress.c */ | ||
36 | jz_buffer* jz_ucl_unpack(const uint8_t* src, uint32_t src_len, uint32_t* dst_len) | ||
37 | { | ||
38 | static const uint8_t magic[8] = | ||
39 | {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a}; | ||
40 | |||
41 | jz_buffer* buffer = NULL; | ||
42 | |||
43 | /* make sure there are enough bytes for the header */ | ||
44 | if(src_len < 18) | ||
45 | goto error; | ||
46 | |||
47 | /* avoid memcmp for reasons of code size */ | ||
48 | for(size_t i = 0; i < sizeof(magic); ++i) | ||
49 | if(src[i] != magic[i]) | ||
50 | goto error; | ||
51 | |||
52 | /* read the other header fields */ | ||
53 | /* uint32_t flags = xread32(&src[8]); */ | ||
54 | uint8_t method = src[12]; | ||
55 | /* uint8_t level = src[13]; */ | ||
56 | uint32_t block_size = xread32(&src[14]); | ||
57 | |||
58 | /* check supported compression method */ | ||
59 | if(method != 0x2e) | ||
60 | goto error; | ||
61 | |||
62 | /* validate */ | ||
63 | if(block_size < 1024 || block_size > 8*1024*1024) | ||
64 | goto error; | ||
65 | |||
66 | src += 18; | ||
67 | src_len -= 18; | ||
68 | |||
69 | /* Calculate amount of space that we might need & allocate a buffer: | ||
70 | * - subtract 4 to account for end of file marker | ||
71 | * - each block is block_size bytes + 8 bytes of header | ||
72 | * - add one to nr_blocks to account for case where file size < block size | ||
73 | * - total size = max uncompressed size of block * nr_blocks | ||
74 | */ | ||
75 | uint32_t nr_blocks = (src_len - 4) / (8 + block_size) + 1; | ||
76 | uint32_t max_size = nr_blocks * (block_size + block_size/8 + 256); | ||
77 | buffer = jz_buffer_alloc(max_size, NULL); | ||
78 | if(!buffer) | ||
79 | goto error; | ||
80 | |||
81 | /* perform the decompression */ | ||
82 | uint32_t dst_ilen = buffer->size; | ||
83 | uint8_t* dst = buffer->data; | ||
84 | while(1) { | ||
85 | if(src_len < 4) | ||
86 | goto error; | ||
87 | |||
88 | uint32_t out_len = xread32(src); src += 4, src_len -= 4; | ||
89 | if(out_len == 0) | ||
90 | break; | ||
91 | |||
92 | if(src_len < 4) | ||
93 | goto error; | ||
94 | |||
95 | uint32_t in_len = xread32(src); src += 4, src_len -= 4; | ||
96 | if(in_len > block_size || out_len > block_size || | ||
97 | in_len == 0 || in_len > out_len) | ||
98 | goto error; | ||
99 | |||
100 | if(src_len < in_len) | ||
101 | goto error; | ||
102 | |||
103 | if(in_len < out_len) { | ||
104 | uint32_t actual_out_len = dst_ilen; | ||
105 | int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL); | ||
106 | if(rc != UCL_E_OK) | ||
107 | goto error; | ||
108 | if(actual_out_len != out_len) | ||
109 | goto error; | ||
110 | } else { | ||
111 | for(size_t i = 0; i < in_len; ++i) | ||
112 | dst[i] = src[i]; | ||
113 | } | ||
114 | |||
115 | src += in_len; | ||
116 | src_len -= in_len; | ||
117 | dst += out_len; | ||
118 | dst_ilen -= out_len; | ||
119 | } | ||
120 | |||
121 | /* subtract leftover number of bytes to get size of compressed output */ | ||
122 | *dst_len = buffer->size - dst_ilen; | ||
123 | return buffer; | ||
124 | |||
125 | error: | ||
126 | jz_buffer_free(buffer); | ||
127 | return NULL; | ||
128 | } | ||
diff --git a/rbutil/jztool/src/usb.c b/rbutil/jztool/src/usb.c index c101f2be77..cfc3ba60cb 100644 --- a/rbutil/jztool/src/usb.c +++ b/rbutil/jztool/src/usb.c | |||
@@ -22,6 +22,7 @@ | |||
22 | #include "jztool_private.h" | 22 | #include "jztool_private.h" |
23 | #include <stdlib.h> | 23 | #include <stdlib.h> |
24 | #include <stdbool.h> | 24 | #include <stdbool.h> |
25 | #include <string.h> | ||
25 | 26 | ||
26 | #define VR_GET_CPU_INFO 0 | 27 | #define VR_GET_CPU_INFO 0 |
27 | #define VR_SET_DATA_ADDRESS 1 | 28 | #define VR_SET_DATA_ADDRESS 1 |
@@ -145,11 +146,12 @@ void jz_usb_close(jz_usbdev* dev) | |||
145 | 146 | ||
146 | // Does an Ingenic-specific vendor request | 147 | // Does an Ingenic-specific vendor request |
147 | // Written with X1000 in mind but other Ingenic CPUs have the same commands | 148 | // Written with X1000 in mind but other Ingenic CPUs have the same commands |
148 | static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg) | 149 | static int jz_usb_vendor_req(jz_usbdev* dev, int req, uint32_t arg, |
150 | void* buffer, int buflen) | ||
149 | { | 151 | { |
150 | int rc = libusb_control_transfer(dev->handle, | 152 | int rc = libusb_control_transfer(dev->handle, |
151 | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, | 153 | LIBUSB_ENDPOINT_OUT | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, |
152 | req, arg >> 16, arg & 0xffff, NULL, 0, 1000); | 154 | req, arg >> 16, arg & 0xffff, buffer, buflen, 1000); |
153 | 155 | ||
154 | if(rc < 0) { | 156 | if(rc < 0) { |
155 | jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); | 157 | jz_log(dev->jz, JZ_LOG_ERROR, "libusb_control_transfer: %s", libusb_strerror(rc)); |
@@ -200,11 +202,11 @@ static int jz_usb_sendrecv(jz_usbdev* dev, bool write, uint32_t addr, | |||
200 | size_t len, void* data) | 202 | size_t len, void* data) |
201 | { | 203 | { |
202 | int rc; | 204 | int rc; |
203 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr); | 205 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_ADDRESS, addr, NULL, 0); |
204 | if(rc < 0) | 206 | if(rc < 0) |
205 | return rc; | 207 | return rc; |
206 | 208 | ||
207 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len); | 209 | rc = jz_usb_vendor_req(dev, VR_SET_DATA_LENGTH, len, NULL, 0); |
208 | if(rc < 0) | 210 | if(rc < 0) |
209 | return rc; | 211 | return rc; |
210 | 212 | ||
@@ -242,7 +244,7 @@ int jz_usb_recv(jz_usbdev* dev, uint32_t addr, size_t len, void* data) | |||
242 | */ | 244 | */ |
243 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr) | 245 | int jz_usb_start1(jz_usbdev* dev, uint32_t addr) |
244 | { | 246 | { |
245 | return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr); | 247 | return jz_usb_vendor_req(dev, VR_PROGRAM_START1, addr, NULL, 0); |
246 | } | 248 | } |
247 | 249 | ||
248 | /** \brief Execute stage2 program jumping to the specified address | 250 | /** \brief Execute stage2 program jumping to the specified address |
@@ -252,7 +254,7 @@ int jz_usb_start1(jz_usbdev* dev, uint32_t addr) | |||
252 | */ | 254 | */ |
253 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr) | 255 | int jz_usb_start2(jz_usbdev* dev, uint32_t addr) |
254 | { | 256 | { |
255 | return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr); | 257 | return jz_usb_vendor_req(dev, VR_PROGRAM_START2, addr, NULL, 0); |
256 | } | 258 | } |
257 | 259 | ||
258 | /** \brief Ask device to flush CPU caches | 260 | /** \brief Ask device to flush CPU caches |
@@ -261,5 +263,29 @@ int jz_usb_start2(jz_usbdev* dev, uint32_t addr) | |||
261 | */ | 263 | */ |
262 | int jz_usb_flush_caches(jz_usbdev* dev) | 264 | int jz_usb_flush_caches(jz_usbdev* dev) |
263 | { | 265 | { |
264 | return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0); | 266 | return jz_usb_vendor_req(dev, VR_FLUSH_CACHES, 0, NULL, 0); |
267 | } | ||
268 | |||
269 | /** \brief Ask device for CPU info string | ||
270 | * \param dev USB device | ||
271 | * \param buffer Buffer to hold the info string | ||
272 | * \param buflen Size of the buffer, in bytes | ||
273 | * \return either JZ_SUCCESS on success or a failure code | ||
274 | * | ||
275 | * The buffer will always be null terminated, but to ensure the info string is | ||
276 | * not truncated the buffer needs to be at least `JZ_CPUINFO_BUFLEN` byes long. | ||
277 | */ | ||
278 | int jz_usb_get_cpu_info(jz_usbdev* dev, char* buffer, size_t buflen) | ||
279 | { | ||
280 | char tmpbuf[JZ_CPUINFO_BUFLEN]; | ||
281 | int rc = jz_usb_vendor_req(dev, VR_GET_CPU_INFO, 0, tmpbuf, 8); | ||
282 | if(rc != JZ_SUCCESS) | ||
283 | return rc; | ||
284 | |||
285 | if(buflen > 0) { | ||
286 | strncpy(buffer, tmpbuf, buflen); | ||
287 | buffer[buflen - 1] = 0; | ||
288 | } | ||
289 | |||
290 | return JZ_SUCCESS; | ||
265 | } | 291 | } |
diff --git a/rbutil/jztool/src/fiiom3k.c b/rbutil/jztool/src/x1000.c index 72e25a1220..aacad0ef01 100644 --- a/rbutil/jztool/src/fiiom3k.c +++ b/rbutil/jztool/src/x1000.c | |||
@@ -22,117 +22,11 @@ | |||
22 | #include "jztool.h" | 22 | #include "jztool.h" |
23 | #include "jztool_private.h" | 23 | #include "jztool_private.h" |
24 | #include "microtar.h" | 24 | #include "microtar.h" |
25 | #include "ucl/ucl.h" | ||
26 | #include <stdbool.h> | 25 | #include <stdbool.h> |
27 | #include <string.h> | 26 | #include <string.h> |
28 | 27 | ||
29 | static uint32_t xread32(const uint8_t* d) | 28 | /* TODO: these functions could be refactored to be CPU-agnostic */ |
30 | { | 29 | static int run_stage1(jz_usbdev* dev, jz_buffer* buf) |
31 | uint32_t r = 0; | ||
32 | r |= d[0] << 24; | ||
33 | r |= d[1] << 16; | ||
34 | r |= d[2] << 8; | ||
35 | r |= d[3] << 0; | ||
36 | return r; | ||
37 | } | ||
38 | |||
39 | /* adapted from firmware/common/ucl_decompress.c */ | ||
40 | static jz_buffer* ucl_unpack(const uint8_t* src, uint32_t src_len, | ||
41 | uint32_t* dst_len) | ||
42 | { | ||
43 | static const uint8_t magic[8] = | ||
44 | {0x00, 0xe9, 0x55, 0x43, 0x4c, 0xff, 0x01, 0x1a}; | ||
45 | |||
46 | jz_buffer* buffer = NULL; | ||
47 | |||
48 | /* make sure there are enough bytes for the header */ | ||
49 | if(src_len < 18) | ||
50 | goto error; | ||
51 | |||
52 | /* avoid memcmp for reasons of code size */ | ||
53 | for(size_t i = 0; i < sizeof(magic); ++i) | ||
54 | if(src[i] != magic[i]) | ||
55 | goto error; | ||
56 | |||
57 | /* read the other header fields */ | ||
58 | /* uint32_t flags = xread32(&src[8]); */ | ||
59 | uint8_t method = src[12]; | ||
60 | /* uint8_t level = src[13]; */ | ||
61 | uint32_t block_size = xread32(&src[14]); | ||
62 | |||
63 | /* check supported compression method */ | ||
64 | if(method != 0x2e) | ||
65 | goto error; | ||
66 | |||
67 | /* validate */ | ||
68 | if(block_size < 1024 || block_size > 8*1024*1024) | ||
69 | goto error; | ||
70 | |||
71 | src += 18; | ||
72 | src_len -= 18; | ||
73 | |||
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; | ||
85 | |||
86 | /* perform the decompression */ | ||
87 | uint32_t dst_ilen = buffer->size; | ||
88 | uint8_t* dst = buffer->data; | ||
89 | while(1) { | ||
90 | if(src_len < 4) | ||
91 | goto error; | ||
92 | |||
93 | uint32_t out_len = xread32(src); src += 4, src_len -= 4; | ||
94 | if(out_len == 0) | ||
95 | break; | ||
96 | |||
97 | if(src_len < 4) | ||
98 | goto error; | ||
99 | |||
100 | uint32_t in_len = xread32(src); src += 4, src_len -= 4; | ||
101 | if(in_len > block_size || out_len > block_size || | ||
102 | in_len == 0 || in_len > out_len) | ||
103 | goto error; | ||
104 | |||
105 | if(src_len < in_len) | ||
106 | goto error; | ||
107 | |||
108 | if(in_len < out_len) { | ||
109 | uint32_t actual_out_len = dst_ilen; | ||
110 | int rc = ucl_nrv2e_decompress_safe_8(src, in_len, dst, &actual_out_len, NULL); | ||
111 | if(rc != UCL_E_OK) | ||
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 | } | ||
119 | |||
120 | src += in_len; | ||
121 | src_len -= in_len; | ||
122 | dst += out_len; | ||
123 | dst_ilen -= out_len; | ||
124 | } | ||
125 | |||
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; | ||
133 | } | ||
134 | |||
135 | static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf) | ||
136 | { | 30 | { |
137 | int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data); | 31 | int rc = jz_usb_send(dev, 0xf4001000, buf->size, buf->data); |
138 | if(rc < 0) | 32 | if(rc < 0) |
@@ -141,7 +35,7 @@ static int m3k_stage1(jz_usbdev* dev, jz_buffer* buf) | |||
141 | return jz_usb_start1(dev, 0xf4001800); | 35 | return jz_usb_start1(dev, 0xf4001800); |
142 | } | 36 | } |
143 | 37 | ||
144 | static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf) | 38 | static int run_stage2(jz_usbdev* dev, jz_buffer* buf) |
145 | { | 39 | { |
146 | int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data); | 40 | int rc = jz_usb_send(dev, 0x80004000, buf->size, buf->data); |
147 | if(rc < 0) | 41 | if(rc < 0) |
@@ -154,8 +48,8 @@ static int m3k_stage2(jz_usbdev* dev, jz_buffer* buf) | |||
154 | return jz_usb_start2(dev, 0x80004000); | 48 | return jz_usb_start2(dev, 0x80004000); |
155 | } | 49 | } |
156 | 50 | ||
157 | static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | 51 | static int get_file(jz_context* jz, mtar_t* tar, const char* file, |
158 | bool decompress, jz_buffer** buf) | 52 | bool decompress, jz_buffer** buf) |
159 | { | 53 | { |
160 | jz_buffer* buffer = NULL; | 54 | jz_buffer* buffer = NULL; |
161 | mtar_header_t h; | 55 | mtar_header_t h; |
@@ -180,7 +74,7 @@ static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | |||
180 | 74 | ||
181 | if(decompress) { | 75 | if(decompress) { |
182 | uint32_t dst_len; | 76 | uint32_t dst_len; |
183 | jz_buffer* nbuf = ucl_unpack(buffer->data, buffer->size, &dst_len); | 77 | jz_buffer* nbuf = jz_ucl_unpack(buffer->data, buffer->size, &dst_len); |
184 | jz_buffer_free(buffer); | 78 | jz_buffer_free(buffer); |
185 | if(!nbuf) { | 79 | if(!nbuf) { |
186 | jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file); | 80 | jz_log(jz, JZ_LOG_ERROR, "error decompressing %s in boot file", file); |
@@ -196,7 +90,7 @@ static int m3k_get_file(jz_context* jz, mtar_t* tar, const char* file, | |||
196 | return JZ_SUCCESS; | 90 | return JZ_SUCCESS; |
197 | } | 91 | } |
198 | 92 | ||
199 | static int m3k_show_version(jz_context* jz, jz_buffer* info_file) | 93 | static int show_version(jz_context* jz, jz_buffer* info_file) |
200 | { | 94 | { |
201 | /* Extract the version string and log it for informational purposes */ | 95 | /* Extract the version string and log it for informational purposes */ |
202 | char* boot_version = (char*)info_file->data; | 96 | char* boot_version = (char*)info_file->data; |
@@ -211,17 +105,28 @@ static int m3k_show_version(jz_context* jz, jz_buffer* info_file) | |||
211 | return JZ_SUCCESS; | 105 | return JZ_SUCCESS; |
212 | } | 106 | } |
213 | 107 | ||
214 | /** \brief Load the Rockbox bootloader on the FiiO M3K | 108 | /** \brief Load the Rockbox bootloader on an X1000 device |
215 | * \param dev USB device freshly returned by jz_usb_open() | 109 | * \param dev USB device freshly returned by jz_usb_open() |
216 | * \param filename Path to the "bootloader.m3k" file | 110 | * \param filename Path to the "bootloader.target" file |
217 | * \return either JZ_SUCCESS or an error code | 111 | * \return either JZ_SUCCESS or an error code |
218 | */ | 112 | */ |
219 | int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | 113 | int jz_x1000_boot(jz_usbdev* dev, jz_device_type type, const char* filename) |
220 | { | 114 | { |
115 | const jz_device_info* dev_info; | ||
116 | char spl_filename[32]; | ||
221 | jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL; | 117 | jz_buffer* spl = NULL, *bootloader = NULL, *info_file = NULL; |
222 | mtar_t tar; | 118 | mtar_t tar; |
223 | int rc; | 119 | int rc; |
224 | 120 | ||
121 | /* In retrospect using a model-dependent archive format was not a good | ||
122 | * idea, but it's not worth fixing just yet... */ | ||
123 | dev_info = jz_get_device_info(type); | ||
124 | if(!dev_info) | ||
125 | return JZ_ERR_OTHER; | ||
126 | /* use of sprintf is safe since file_ext is short */ | ||
127 | sprintf(spl_filename, "spl.%s", dev_info->file_ext); | ||
128 | |||
129 | /* Now open the archive */ | ||
225 | rc = mtar_open(&tar, filename, "r"); | 130 | rc = mtar_open(&tar, filename, "r"); |
226 | if(rc != MTAR_ESUCCESS) { | 131 | if(rc != MTAR_ESUCCESS) { |
227 | jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc); | 132 | jz_log(dev->jz, JZ_LOG_ERROR, "cannot open file %s (tar error: %d)", filename, rc); |
@@ -229,25 +134,25 @@ int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | |||
229 | } | 134 | } |
230 | 135 | ||
231 | /* Extract all necessary files */ | 136 | /* Extract all necessary files */ |
232 | rc = m3k_get_file(dev->jz, &tar, "spl.m3k", false, &spl); | 137 | rc = get_file(dev->jz, &tar, spl_filename, false, &spl); |
233 | if(rc != JZ_SUCCESS) | 138 | if(rc != JZ_SUCCESS) |
234 | goto error; | 139 | goto error; |
235 | 140 | ||
236 | rc = m3k_get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader); | 141 | rc = get_file(dev->jz, &tar, "bootloader.ucl", true, &bootloader); |
237 | if(rc != JZ_SUCCESS) | 142 | if(rc != JZ_SUCCESS) |
238 | goto error; | 143 | goto error; |
239 | 144 | ||
240 | rc = m3k_get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file); | 145 | rc = get_file(dev->jz, &tar, "bootloader-info.txt", false, &info_file); |
241 | if(rc != JZ_SUCCESS) | 146 | if(rc != JZ_SUCCESS) |
242 | goto error; | 147 | goto error; |
243 | 148 | ||
244 | /* Display the version string */ | 149 | /* Display the version string */ |
245 | rc = m3k_show_version(dev->jz, info_file); | 150 | rc = show_version(dev->jz, info_file); |
246 | if(rc != JZ_SUCCESS) | 151 | if(rc != JZ_SUCCESS) |
247 | goto error; | 152 | goto error; |
248 | 153 | ||
249 | /* Stage1 boot of SPL to set up hardware */ | 154 | /* Stage1 boot of SPL to set up hardware */ |
250 | rc = m3k_stage1(dev, spl); | 155 | rc = run_stage1(dev, spl); |
251 | if(rc != JZ_SUCCESS) | 156 | if(rc != JZ_SUCCESS) |
252 | goto error; | 157 | goto error; |
253 | 158 | ||
@@ -256,7 +161,7 @@ int jz_fiiom3k_boot(jz_usbdev* dev, const char* filename) | |||
256 | 161 | ||
257 | /* Stage2 boot into the bootloader's recovery menu | 162 | /* Stage2 boot into the bootloader's recovery menu |
258 | * User has to take manual action from there */ | 163 | * User has to take manual action from there */ |
259 | rc = m3k_stage2(dev, bootloader); | 164 | rc = run_stage2(dev, bootloader); |
260 | if(rc != JZ_SUCCESS) | 165 | if(rc != JZ_SUCCESS) |
261 | goto error; | 166 | goto error; |
262 | 167 | ||