diff options
Diffstat (limited to 'utils/jztool/jztool.c')
-rw-r--r-- | utils/jztool/jztool.c | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/utils/jztool/jztool.c b/utils/jztool/jztool.c new file mode 100644 index 0000000000..dcd78137b3 --- /dev/null +++ b/utils/jztool/jztool.c | |||
@@ -0,0 +1,212 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2021 Aidan MacDonald | ||
11 | * | ||
12 | * This program is free software; you can redistribute it and/or | ||
13 | * modify it under the terms of the GNU General Public License | ||
14 | * as published by the Free Software Foundation; either version 2 | ||
15 | * of the License, or (at your option) any later version. | ||
16 | * | ||
17 | * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY | ||
18 | * KIND, either express or implied. | ||
19 | * | ||
20 | ****************************************************************************/ | ||
21 | |||
22 | #include "jztool.h" | ||
23 | #include <stdio.h> | ||
24 | #include <stdlib.h> | ||
25 | #include <string.h> | ||
26 | #include <stdbool.h> | ||
27 | |||
28 | jz_context* jz = NULL; | ||
29 | jz_usbdev* usbdev = NULL; | ||
30 | const jz_device_info* dev_info = NULL; | ||
31 | const jz_cpu_info* cpu_info = NULL; | ||
32 | |||
33 | void usage_x1000(void) | ||
34 | { | ||
35 | printf( | ||
36 | "Usage:\n" | ||
37 | " jztool fiiom3k load <bootloader.m3k>\n" | ||
38 | " jztool shanlingq1 load <bootloader.q1>\n" | ||
39 | " jztool erosq load <bootloader.erosq>\n" | ||
40 | "\n" | ||
41 | "The 'load' command is used to boot the Rockbox bootloader in recovery\n" | ||
42 | "mode, which allows you to install the Rockbox bootloader and backup or\n" | ||
43 | "restore bootloader images. You need to connect your player in USB boot\n" | ||
44 | "mode in order to use this tool.\n" | ||
45 | "\n" | ||
46 | "To connect the player in USB boot mode, follow these steps:\n" | ||
47 | "\n" | ||
48 | "1. Ensure the player is fully powered off.\n" | ||
49 | "2. Plug one end of the USB cable into your player.\n" | ||
50 | "3. Hold down your player's USB boot key (see below).\n" | ||
51 | "4. Plug the other end of the USB cable into your computer.\n" | ||
52 | "5. Let go of the USB boot key.\n" | ||
53 | "\n" | ||
54 | "USB boot keys:\n" | ||
55 | "\n" | ||
56 | " FiiO M3K - Volume Down\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 | |||
71 | exit(4); | ||
72 | } | ||
73 | |||
74 | int cmdline_x1000(int argc, char** argv) | ||
75 | { | ||
76 | if(argc < 2 || strcmp(argv[0], "load")) { | ||
77 | usage_x1000(); | ||
78 | return 2; | ||
79 | } | ||
80 | |||
81 | int rc = jz_usb_open(jz, &usbdev, cpu_info->vendor_id, cpu_info->product_id); | ||
82 | if(rc < 0) { | ||
83 | jz_log(jz, JZ_LOG_ERROR, "Cannot open USB device: %d", rc); | ||
84 | return 1; | ||
85 | } | ||
86 | |||
87 | rc = jz_x1000_boot(usbdev, dev_info->device_type, argv[1]); | ||
88 | if(rc < 0) { | ||
89 | jz_log(jz, JZ_LOG_ERROR, "Boot failed: %d", rc); | ||
90 | return 1; | ||
91 | } | ||
92 | |||
93 | return 0; | ||
94 | } | ||
95 | |||
96 | void usage(void) | ||
97 | { | ||
98 | printf("Usage:\n" | ||
99 | " jztool [global options] <device> <command> [command arguments]\n" | ||
100 | "\n" | ||
101 | "Global options:\n" | ||
102 | " -h, --help Display this help\n" | ||
103 | " -q, --quiet Don't log anything except errors\n" | ||
104 | " -v, --verbose Display detailed logging output\n\n"); | ||
105 | |||
106 | printf("Supported devices:\n\n"); | ||
107 | for(int i = 0; i < JZ_NUM_DEVICES; ++i) { | ||
108 | const jz_device_info* info = jz_get_device_info_indexed(i); | ||
109 | printf(" %s - %s\n", info->name, info->description); | ||
110 | } | ||
111 | |||
112 | printf("\n" | ||
113 | "For device-specific help run 'jztool DEVICE' without arguments,\n" | ||
114 | "eg. 'jztool fiiom3k' will display help for the FiiO M3K.\n"); | ||
115 | |||
116 | exit(4); | ||
117 | } | ||
118 | |||
119 | void cleanup(void) | ||
120 | { | ||
121 | if(usbdev) | ||
122 | jz_usb_close(usbdev); | ||
123 | if(jz) | ||
124 | jz_context_destroy(jz); | ||
125 | } | ||
126 | |||
127 | int main(int argc, char** argv) | ||
128 | { | ||
129 | if(argc < 2) | ||
130 | usage(); | ||
131 | |||
132 | /* Library initialization */ | ||
133 | jz = jz_context_create(); | ||
134 | if(!jz) { | ||
135 | fprintf(stderr, "ERROR: Can't create context"); | ||
136 | return 1; | ||
137 | } | ||
138 | |||
139 | atexit(cleanup); | ||
140 | jz_context_set_log_cb(jz, jz_log_cb_stderr); | ||
141 | jz_context_set_log_level(jz, JZ_LOG_NOTICE); | ||
142 | |||
143 | /* Parse global options */ | ||
144 | --argc, ++argv; | ||
145 | while(argc > 0 && argv[0][0] == '-') { | ||
146 | if(!strcmp(*argv, "-h") || !strcmp(*argv, "--help")) | ||
147 | usage(); | ||
148 | else if(!strcmp(*argv, "-q") || !strcmp(*argv, "--quiet")) | ||
149 | jz_context_set_log_level(jz, JZ_LOG_ERROR); | ||
150 | else if(!strcmp(*argv, "-v") || !strcmp(*argv, "--verbose")) | ||
151 | jz_context_set_log_level(jz, JZ_LOG_DETAIL); | ||
152 | else if(!strcmp(*argv, "-l") || !strcmp(*argv, "--loglevel")) { | ||
153 | ++argv; | ||
154 | if(--argc == 0) { | ||
155 | jz_log(jz, JZ_LOG_ERROR, "Missing argument to option %s", *argv); | ||
156 | exit(2); | ||
157 | } | ||
158 | |||
159 | enum jz_log_level level; | ||
160 | if(!strcmp(*argv, "ignore")) | ||
161 | level = JZ_LOG_IGNORE; | ||
162 | else if(!strcmp(*argv, "error")) | ||
163 | level = JZ_LOG_ERROR; | ||
164 | else if(!strcmp(*argv, "warning")) | ||
165 | level = JZ_LOG_WARNING; | ||
166 | else if(!strcmp(*argv, "notice")) | ||
167 | level = JZ_LOG_NOTICE; | ||
168 | else if(!strcmp(*argv, "detail")) | ||
169 | level = JZ_LOG_DETAIL; | ||
170 | else if(!strcmp(*argv, "debug")) | ||
171 | level = JZ_LOG_DEBUG; | ||
172 | else { | ||
173 | jz_log(jz, JZ_LOG_ERROR, "Invalid log level '%s'", *argv); | ||
174 | exit(2); | ||
175 | } | ||
176 | |||
177 | jz_context_set_log_level(jz, level); | ||
178 | } else { | ||
179 | jz_log(jz, JZ_LOG_ERROR, "Invalid global option '%s'", *argv); | ||
180 | exit(2); | ||
181 | } | ||
182 | |||
183 | --argc, ++argv; | ||
184 | } | ||
185 | |||
186 | /* Read the device type */ | ||
187 | if(argc == 0) { | ||
188 | jz_log(jz, JZ_LOG_ERROR, "No device specified (try jztool --help)"); | ||
189 | exit(2); | ||
190 | } | ||
191 | |||
192 | dev_info = jz_get_device_info_named(*argv); | ||
193 | if(!dev_info) { | ||
194 | jz_log(jz, JZ_LOG_ERROR, "Unknown device '%s' (try jztool --help)", *argv); | ||
195 | exit(2); | ||
196 | } | ||
197 | |||
198 | cpu_info = jz_get_cpu_info(dev_info->cpu_type); | ||
199 | |||
200 | /* Dispatch to device handler */ | ||
201 | --argc, ++argv; | ||
202 | switch(dev_info->device_type) { | ||
203 | case JZ_DEVICE_FIIOM3K: | ||
204 | case JZ_DEVICE_SHANLINGQ1: | ||
205 | case JZ_DEVICE_EROSQ: | ||
206 | return cmdline_x1000(argc, argv); | ||
207 | |||
208 | default: | ||
209 | jz_log(jz, JZ_LOG_ERROR, "INTERNAL ERROR: unhandled device type"); | ||
210 | return 1; | ||
211 | } | ||
212 | } | ||