diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2014-04-07 22:25:06 +0200 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2014-04-07 22:25:06 +0200 |
commit | 399acc038750943c1a0ee7178a18714334871472 (patch) | |
tree | 343079cb893fe9b27d66455d0459536764b0865a /utils | |
parent | 68370b6eea8b5bed54e82a07086ad7c81cc966dd (diff) | |
download | rockbox-399acc038750943c1a0ee7178a18714334871472.tar.gz rockbox-399acc038750943c1a0ee7178a18714334871472.zip |
Add new tool to upload code on e200/c200/view in manufacturer mode
This is actually the trivial part of e200tool from MrH: it simply writes the
code on the bulk endpoint. Code was mostly copied from imxtools/sbloader.
Change-Id: I6c208840d23553aaf3bd8b9374e6b0337e54f3b0
Diffstat (limited to 'utils')
-rw-r--r-- | utils/e200tool/Makefile | 21 | ||||
-rw-r--r-- | utils/e200tool/e200load.c | 310 |
2 files changed, 331 insertions, 0 deletions
diff --git a/utils/e200tool/Makefile b/utils/e200tool/Makefile new file mode 100644 index 0000000000..63f82b9cdd --- /dev/null +++ b/utils/e200tool/Makefile | |||
@@ -0,0 +1,21 @@ | |||
1 | DEFINES= | ||
2 | CC=gcc | ||
3 | LD=gcc | ||
4 | CFLAGS=-O2 -g -std=c99 -W -Wall `pkg-config --cflags libusb-1.0` $(DEFINES) | ||
5 | LDFLAGS=`pkg-config --libs libusb-1.0` | ||
6 | BINS=e200load | ||
7 | |||
8 | all: $(BINS) | ||
9 | |||
10 | %.o: %.c | ||
11 | $(CC) $(CFLAGS) -c -o $@ $< | ||
12 | |||
13 | e200load: e200load.o | ||
14 | $(LD) -o $@ $^ $(LDFLAGS) | ||
15 | |||
16 | clean: | ||
17 | rm -fr *.o | ||
18 | |||
19 | veryclean: | ||
20 | rm -rf $(BINS) | ||
21 | |||
diff --git a/utils/e200tool/e200load.c b/utils/e200tool/e200load.c new file mode 100644 index 0000000000..8e5005b1f8 --- /dev/null +++ b/utils/e200tool/e200load.c | |||
@@ -0,0 +1,310 @@ | |||
1 | /*************************************************************************** | ||
2 | * __________ __ ___. | ||
3 | * Open \______ \ ____ ____ | | _\_ |__ _______ ___ | ||
4 | * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / | ||
5 | * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < | ||
6 | * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ | ||
7 | * \/ \/ \/ \/ \/ | ||
8 | * $Id$ | ||
9 | * | ||
10 | * Copyright (C) 2014 Amaury Pouly | ||
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 | #include <stdio.h> | ||
22 | #include <stdlib.h> | ||
23 | #include <string.h> | ||
24 | #include <libusb.h> | ||
25 | #include <stdint.h> | ||
26 | #include <stdbool.h> | ||
27 | #include <getopt.h> | ||
28 | |||
29 | bool g_debug = false; | ||
30 | |||
31 | struct dev_info_t | ||
32 | { | ||
33 | uint16_t vendor_id; | ||
34 | uint16_t product_id; | ||
35 | unsigned xfer_size; | ||
36 | }; | ||
37 | |||
38 | struct dev_info_t g_dev_info[] = | ||
39 | { | ||
40 | {0x0781, 0x0720, 64}, /* Sandisk E200/C200/View */ | ||
41 | {0x0b70, 0x0003, 64}, /* Portal Player */ | ||
42 | }; | ||
43 | |||
44 | #ifndef MIN | ||
45 | #define MIN(a,b) ((a) < (b) ? (a) : (b)) | ||
46 | #endif | ||
47 | |||
48 | #ifndef MAX | ||
49 | #define MAX(a,b) ((a) > (b) ? (a) : (b)) | ||
50 | #endif | ||
51 | |||
52 | static void put32le(uint8_t *buf, uint32_t i) | ||
53 | { | ||
54 | *buf++ = i & 0xff; | ||
55 | *buf++ = (i >> 8) & 0xff; | ||
56 | *buf++ = (i >> 16) & 0xff; | ||
57 | *buf++ = (i >> 24) & 0xff; | ||
58 | } | ||
59 | |||
60 | static int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size) | ||
61 | { | ||
62 | // there should be no kernel driver attached but in doubt... | ||
63 | libusb_detach_kernel_driver(dev, 0); | ||
64 | libusb_claim_interface(dev, 0); | ||
65 | |||
66 | uint8_t size_buffer[4]; | ||
67 | put32le(size_buffer, size); | ||
68 | int xfered; | ||
69 | int ret = libusb_bulk_transfer(dev, 1, size_buffer, sizeof(size_buffer), &xfered, 1000); | ||
70 | if(ret < 0 || xfered != sizeof(size_buffer)) | ||
71 | { | ||
72 | printf("transfer error at init step: %d", ret); | ||
73 | return 1; | ||
74 | } | ||
75 | |||
76 | int sent = 0; | ||
77 | while(sent < size) | ||
78 | { | ||
79 | int xfered; | ||
80 | int len = MIN(size - sent, xfer_size); | ||
81 | int ret = libusb_bulk_transfer(dev, 1, data + sent, len, &xfered, 1000); | ||
82 | if(ret < 0 || xfered != len) | ||
83 | { | ||
84 | printf("transfer error at send offset %d: %d\n", sent, ret); | ||
85 | return 1; | ||
86 | } | ||
87 | sent += xfered; | ||
88 | } | ||
89 | return 0; | ||
90 | } | ||
91 | |||
92 | static void usage(void) | ||
93 | { | ||
94 | printf("sbloader [options] file\n"); | ||
95 | printf("options:\n"); | ||
96 | printf(" -h/-?/--help Display this help\n"); | ||
97 | printf(" -d/--debug Enable debug output\n"); | ||
98 | printf(" -x <size> Force transfer size\n"); | ||
99 | printf(" -u <vid>:<pid> Force USB PID and VID\n"); | ||
100 | printf(" -b <bus>:<dev> Force USB bus and device\n"); | ||
101 | printf("The following devices are known to this tool:\n"); | ||
102 | for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) | ||
103 | { | ||
104 | printf(" %04x:%04x (%d bytes/xfer)\n", g_dev_info[i].vendor_id, | ||
105 | g_dev_info[i].product_id, g_dev_info[i].xfer_size); | ||
106 | } | ||
107 | printf("You can select a particular device by USB PID and VID.\n"); | ||
108 | printf("In case this is ambiguous, use bus and device number.\n"); | ||
109 | printf("Protocol is infered if possible and unspecified.\n"); | ||
110 | printf("Transfer size is infered if possible.\n"); | ||
111 | exit(1); | ||
112 | } | ||
113 | |||
114 | static bool dev_match(libusb_device *dev, struct dev_info_t *arg_di, | ||
115 | int usb_bus, int usb_dev, int *db_idx) | ||
116 | { | ||
117 | // match bus/dev | ||
118 | if(usb_bus != -1) | ||
119 | return libusb_get_bus_number(dev) == usb_bus && libusb_get_device_address(dev) == usb_dev; | ||
120 | // get device descriptor | ||
121 | struct libusb_device_descriptor desc; | ||
122 | if(libusb_get_device_descriptor(dev, &desc)) | ||
123 | return false; | ||
124 | // match command line vid/pid if specified | ||
125 | if(arg_di->vendor_id != 0) | ||
126 | return desc.idVendor == arg_di->vendor_id && desc.idProduct == arg_di->product_id; | ||
127 | // match known vid/pid | ||
128 | for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) | ||
129 | if(desc.idVendor == g_dev_info[i].vendor_id && desc.idProduct == g_dev_info[i].product_id) | ||
130 | { | ||
131 | if(db_idx) | ||
132 | *db_idx = i; | ||
133 | return true; | ||
134 | } | ||
135 | return false; | ||
136 | } | ||
137 | |||
138 | static void print_match(libusb_device *dev) | ||
139 | { | ||
140 | struct libusb_device_descriptor desc; | ||
141 | if(libusb_get_device_descriptor(dev, &desc)) | ||
142 | printf("????:????"); | ||
143 | else | ||
144 | printf("%04x:%04x", desc.idVendor, desc.idProduct); | ||
145 | printf(" @ %d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); | ||
146 | } | ||
147 | |||
148 | int main(int argc, char **argv) | ||
149 | { | ||
150 | if(argc <= 1) | ||
151 | usage(); | ||
152 | struct dev_info_t di = {.vendor_id = 0, .product_id = 0, .xfer_size = 64}; | ||
153 | int usb_bus = -1; | ||
154 | int usb_dev = -1; | ||
155 | int force_xfer_size = 0; | ||
156 | /* parse command line */ | ||
157 | while(1) | ||
158 | { | ||
159 | static struct option long_options[] = | ||
160 | { | ||
161 | {"help", no_argument, 0, '?'}, | ||
162 | {"debug", no_argument, 0, 'd'}, | ||
163 | {0, 0, 0, 0} | ||
164 | }; | ||
165 | |||
166 | int c = getopt_long(argc, argv, "?dx:u:b:p:", long_options, NULL); | ||
167 | if(c == -1) | ||
168 | break; | ||
169 | switch(c) | ||
170 | { | ||
171 | case -1: | ||
172 | break; | ||
173 | case 'd': | ||
174 | g_debug = true; | ||
175 | break; | ||
176 | case '?': | ||
177 | usage(); | ||
178 | break; | ||
179 | case 'x': | ||
180 | { | ||
181 | char *end; | ||
182 | force_xfer_size = strtoul(optarg, &end, 0); | ||
183 | if(*end) | ||
184 | { | ||
185 | printf("Invalid transfer size!\n"); | ||
186 | exit(2); | ||
187 | } | ||
188 | break; | ||
189 | } | ||
190 | case 'u': | ||
191 | { | ||
192 | char *end; | ||
193 | di.vendor_id = strtoul(optarg, &end, 16); | ||
194 | if(*end != ':') | ||
195 | { | ||
196 | printf("Invalid USB PID!\n"); | ||
197 | exit(3); | ||
198 | } | ||
199 | di.product_id = strtoul(end + 1, &end, 16); | ||
200 | if(*end) | ||
201 | { | ||
202 | printf("Invalid USB VID!\n"); | ||
203 | exit(4); | ||
204 | } | ||
205 | break; | ||
206 | } | ||
207 | case 'b': | ||
208 | { | ||
209 | char *end; | ||
210 | usb_bus = strtol(optarg, &end, 0); | ||
211 | if(*end != ':') | ||
212 | { | ||
213 | printf("Invalid USB bus!\n"); | ||
214 | exit(5); | ||
215 | } | ||
216 | usb_dev = strtol(end, &end, 0); | ||
217 | if(*end) | ||
218 | { | ||
219 | printf("Invalid USB device!\n"); | ||
220 | exit(6); | ||
221 | } | ||
222 | break; | ||
223 | } | ||
224 | default: | ||
225 | printf("Internal error: unknown option '%c'\n", c); | ||
226 | abort(); | ||
227 | } | ||
228 | } | ||
229 | |||
230 | if(optind + 1 != argc) | ||
231 | usage(); | ||
232 | const char *filename = argv[optind]; | ||
233 | /* lookup device */ | ||
234 | libusb_init(NULL); | ||
235 | libusb_set_debug(NULL, 3); | ||
236 | libusb_device **list; | ||
237 | ssize_t list_size = libusb_get_device_list(NULL, &list); | ||
238 | libusb_device_handle *dev = NULL; | ||
239 | int db_idx = -1; | ||
240 | |||
241 | { | ||
242 | libusb_device *mdev = NULL; | ||
243 | int nr_matches = 0; | ||
244 | for(int i = 0; i < list_size; i++) | ||
245 | { | ||
246 | // match bus/dev if specified | ||
247 | if(dev_match(list[i], &di, usb_bus, usb_dev, &db_idx)) | ||
248 | { | ||
249 | mdev = list[i]; | ||
250 | nr_matches++; | ||
251 | } | ||
252 | } | ||
253 | if(nr_matches == 0) | ||
254 | { | ||
255 | printf("No device found\n"); | ||
256 | exit(8); | ||
257 | } | ||
258 | if(nr_matches > 1) | ||
259 | { | ||
260 | printf("Several devices match the specified parameters:\n"); | ||
261 | for(int i = 0; i < list_size; i++) | ||
262 | { | ||
263 | // match bus/dev if specified | ||
264 | if(dev_match(list[i], &di, usb_bus, usb_dev, NULL)) | ||
265 | { | ||
266 | printf(" "); | ||
267 | print_match(list[i]); | ||
268 | } | ||
269 | } | ||
270 | } | ||
271 | printf("Device: "); | ||
272 | print_match(mdev); | ||
273 | libusb_open(mdev, &dev); | ||
274 | } | ||
275 | if(dev == NULL) | ||
276 | { | ||
277 | printf("Cannot open device\n"); | ||
278 | return 1; | ||
279 | } | ||
280 | /* get protocol */ | ||
281 | int xfer_size = di.xfer_size; | ||
282 | if(db_idx >= 0) | ||
283 | xfer_size = g_dev_info[db_idx].xfer_size; | ||
284 | if(force_xfer_size > 0) | ||
285 | xfer_size = force_xfer_size; | ||
286 | /* open file */ | ||
287 | FILE *f = fopen(filename, "r"); | ||
288 | if(f == NULL) | ||
289 | { | ||
290 | perror("Cannot open file"); | ||
291 | return 1; | ||
292 | } | ||
293 | fseek(f, 0, SEEK_END); | ||
294 | size_t size = ftell(f); | ||
295 | fseek(f, 0, SEEK_SET); | ||
296 | |||
297 | printf("Transfer size: %d\n", xfer_size); | ||
298 | uint8_t *file_buf = malloc(size); | ||
299 | if(fread(file_buf, size, 1, f) != 1) | ||
300 | { | ||
301 | perror("read error"); | ||
302 | fclose(f); | ||
303 | return 1; | ||
304 | } | ||
305 | fclose(f); | ||
306 | /* send file */ | ||
307 | return send_recovery(dev, xfer_size, file_buf, size); | ||
308 | } | ||
309 | |||
310 | |||