diff options
author | Amaury Pouly <amaury.pouly@gmail.com> | 2016-11-11 16:04:15 +0100 |
---|---|---|
committer | Amaury Pouly <amaury.pouly@gmail.com> | 2016-11-11 16:07:14 +0100 |
commit | 33856d9ceb6df817c4f40f42bfcf9ee9fa188848 (patch) | |
tree | b6b00a247fa13505400aaa886994c9b751e6f48d /utils/nwztools | |
parent | c95e30b75d75b674f0d645b7c41377bbd0511213 (diff) | |
download | rockbox-33856d9ceb6df817c4f40f42bfcf9ee9fa188848.tar.gz rockbox-33856d9ceb6df817c4f40f42bfcf9ee9fa188848.zip |
nwztool/scsitools: cleanup and add destination changer tool
Now that we have a nice database of player index, the scsitool becomes more
useful and supports a lot more players. I did some general cleanup of the code,
though eventually it would be nice to really split it into a library and a CLI.
The SCSI vendor command allow to read but also write most NVP nodes. Since there
seems to a demand to change destination and sound pressure settings on device,
I implement this feature in the tool. I do not plan to allow arbitrary NVP
writes because this could easily brick the device. Changing the destination
should be safe, but as usual, use at your own risks.
Change-Id: Iff4e8cc3ac97b965c1df849051c5fd373756cda5
Diffstat (limited to 'utils/nwztools')
-rw-r--r-- | utils/nwztools/scsitools/Makefile | 14 | ||||
-rw-r--r-- | utils/nwztools/scsitools/scsitool.c | 536 |
2 files changed, 367 insertions, 183 deletions
diff --git a/utils/nwztools/scsitools/Makefile b/utils/nwztools/scsitools/Makefile index f51537fe76..41ffce33b1 100644 --- a/utils/nwztools/scsitools/Makefile +++ b/utils/nwztools/scsitools/Makefile | |||
@@ -1,20 +1,16 @@ | |||
1 | DEFINES= | 1 | DEFINES= |
2 | CC=gcc | 2 | CC=gcc |
3 | LD=gcc | 3 | LD=gcc |
4 | CFLAGS=-g -std=c99 -W -Wall $(DEFINES) | 4 | NWZ_DB_DIR=../database |
5 | INCLUDES=-I$(NWZ_DB_DIR) | ||
6 | CFLAGS=-std=c99 -W -Wall $(DEFINES) $(INCLUDES) | ||
5 | LDFLAGS=-lsgutils2 | 7 | LDFLAGS=-lsgutils2 |
6 | BINS=scsitool | 8 | BINS=scsitool |
7 | 9 | ||
8 | all: $(BINS) | 10 | all: $(BINS) |
9 | 11 | ||
10 | %.o: %.c | 12 | scsitool: scsitool.c misc.c para_noise.c $(NWZ_DB_DIR)/nwz_db.c |
11 | $(CC) $(CFLAGS) -c -o $@ $< | 13 | $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) |
12 | |||
13 | scsitool: scsitool.o misc.o para_noise.o | ||
14 | $(LD) -o $@ $^ $(LDFLAGS) | ||
15 | 14 | ||
16 | clean: | 15 | clean: |
17 | rm -fr *.o | ||
18 | |||
19 | veryclean: | ||
20 | rm -rf $(BINS) | 16 | rm -rf $(BINS) |
diff --git a/utils/nwztools/scsitools/scsitool.c b/utils/nwztools/scsitools/scsitool.c index 7e2dcd9967..3bff9b90fd 100644 --- a/utils/nwztools/scsitools/scsitool.c +++ b/utils/nwztools/scsitools/scsitool.c | |||
@@ -38,6 +38,7 @@ | |||
38 | #include <scsi/sg_pt.h> | 38 | #include <scsi/sg_pt.h> |
39 | #include "misc.h" | 39 | #include "misc.h" |
40 | #include "para_noise.h" | 40 | #include "para_noise.h" |
41 | #include "nwz_db.h" | ||
41 | 42 | ||
42 | /* the windows port doesn't have scsi.h and GOOD */ | 43 | /* the windows port doesn't have scsi.h and GOOD */ |
43 | #ifndef GOOD | 44 | #ifndef GOOD |
@@ -152,7 +153,7 @@ int do_scsi(uint8_t *cdb, int cdb_size, unsigned flags, void *sense, int *sense_ | |||
152 | *sense_size = get_scsi_pt_sense_len(obj); | 153 | *sense_size = get_scsi_pt_sense_len(obj); |
153 | if(flags & (DO_WRITE | DO_READ)) | 154 | if(flags & (DO_WRITE | DO_READ)) |
154 | *buf_size -= get_scsi_pt_resid(obj); | 155 | *buf_size -= get_scsi_pt_resid(obj); |
155 | 156 | ||
156 | destruct_scsi_pt_obj(obj); | 157 | destruct_scsi_pt_obj(obj); |
157 | return ret; | 158 | return ret; |
158 | } | 159 | } |
@@ -171,20 +172,33 @@ int do_sense_analysis(int status, uint8_t *sense, int sense_size) | |||
171 | return status; | 172 | return status; |
172 | } | 173 | } |
173 | 174 | ||
174 | int do_dnk_cmd(uint32_t cmd, uint8_t sub_cmd, uint16_t arg, void *buffer, int *buffer_size) | 175 | /* |
176 | * SCSI commands | ||
177 | */ | ||
178 | #define CMD_A3 0xa3 /* start a complicated, authenticated, session to do things */ | ||
179 | #define CMD_A4 0xa4 /* start a complicated, authenticated, session to do things */ | ||
180 | #define CMD_EMPR_DPCC 0xd7 | ||
181 | #define CMD_DNK 0xdd | ||
182 | #define CMD_DPCC 0xfb | ||
183 | |||
184 | /* | ||
185 | * DNK: command is in cdb[10], subcommand in cdb[11], cdb[7] must be 0xbc | ||
186 | */ | ||
187 | |||
188 | int do_dnk_cmd(bool read, uint32_t cmd, uint8_t sub_cmd, uint16_t arg, void *buffer, int *buffer_size) | ||
175 | { | 189 | { |
176 | uint8_t cdb[12] = {0xdd, 0, 0, 0, 0, 0, 0, 0xbc, 0, 0, 0, 0}; | 190 | uint8_t cdb[12] = {CMD_DNK, 0, 0, 0, 0, 0, 0, 0xbc, 0, 0, 0, 0}; |
177 | cdb[10] = cmd; | 191 | cdb[10] = cmd; |
178 | cdb[11] = sub_cmd; | 192 | cdb[11] = sub_cmd; |
179 | cdb[8] = (*buffer_size) >> 8; | 193 | cdb[8] = (*buffer_size) >> 8; |
180 | cdb[9] = (*buffer_size) & 0xff; | 194 | cdb[9] = (*buffer_size) & 0xff; |
181 | cdb[4] = (arg >> 8) & 0xff; | 195 | cdb[4] = (arg >> 8) & 0xff; |
182 | cdb[5] = arg & 0xff; | 196 | cdb[5] = arg & 0xff; |
183 | 197 | ||
184 | uint8_t sense[32]; | 198 | uint8_t sense[32]; |
185 | int sense_size = 32; | 199 | int sense_size = 32; |
186 | 200 | ||
187 | int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, buffer_size); | 201 | int ret = do_scsi(cdb, 12, read ? DO_READ : DO_WRITE, sense, &sense_size, buffer, buffer_size); |
188 | if(ret < 0) | 202 | if(ret < 0) |
189 | return ret; | 203 | return ret; |
190 | ret = do_sense_analysis(ret, sense, sense_size); | 204 | ret = do_sense_analysis(ret, sense, sense_size); |
@@ -196,10 +210,12 @@ int do_dnk_cmd(uint32_t cmd, uint8_t sub_cmd, uint16_t arg, void *buffer, int *b | |||
196 | #define DNK_EXACT_LENGTH (1 << 0) | 210 | #define DNK_EXACT_LENGTH (1 << 0) |
197 | #define DNK_STRING (1 << 1) | 211 | #define DNK_STRING (1 << 1) |
198 | #define DNK_UINT32 (1 << 2) | 212 | #define DNK_UINT32 (1 << 2) |
213 | #define DNK_HEX (1 << 3) | ||
199 | 214 | ||
200 | struct dnk_prop_t | 215 | struct dnk_prop_t |
201 | { | 216 | { |
202 | char *name; | 217 | const char *name; |
218 | const char *desc; | ||
203 | uint8_t cmd; | 219 | uint8_t cmd; |
204 | uint8_t subcmd; | 220 | uint8_t subcmd; |
205 | int size; | 221 | int size; |
@@ -208,16 +224,71 @@ struct dnk_prop_t | |||
208 | 224 | ||
209 | struct dnk_prop_t dnk_prop_list[] = | 225 | struct dnk_prop_t dnk_prop_list[] = |
210 | { | 226 | { |
211 | { "serial_num", 0x23, 1, 8, DNK_STRING}, | 227 | { "serial_num", "Serial number", 0x23, 1, 8, DNK_STRING}, |
212 | { "model_id", 0x23, 4, 4, DNK_EXACT_LENGTH | DNK_UINT32}, | 228 | { "storage_size", "Storage size(GB)", 0x23, 4, 4, DNK_EXACT_LENGTH | DNK_UINT32}, |
213 | { "product_id", 0x23, 6, 12, DNK_STRING}, | 229 | { "product_id", "Product ID", 0x23, 6, 12, DNK_STRING}, |
214 | { "destination", 0x23, 8, 4, DNK_EXACT_LENGTH | DNK_UINT32}, | 230 | { "destination", "Destination", 0x23, 8, 4, DNK_EXACT_LENGTH | DNK_UINT32}, |
215 | { "model_id2", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32}, | 231 | { "model_id", "Model ID", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32 | DNK_HEX}, |
216 | { "dev_info", 0x12, 0, 64, DNK_STRING}, | 232 | { "model_name", "Model Name", 0x12, 0, 64, DNK_STRING}, |
233 | /* there are more obscure commands: | ||
234 | * - 0x11 returns a 10-byte packet containing a 8-byte "LeftIdl8", scrambled | ||
235 | * with para_noise (the 2-byte padding is random so that output is random | ||
236 | * until unscrambled) | ||
237 | * - 0x21 returns a 0x2b2 packet contaning a 0x2b0 "DNK", scrambled similarly | ||
238 | * - 0x22 can write the DNK (sending scrambled data again) | ||
239 | * - 0x23 has more subproperties: | ||
240 | * - 5 is "eDKS" | ||
241 | * - 7 is "ProductGroup" | ||
242 | * - 10 is nvp properties (see get_dnk_nvp) | ||
243 | * - 11 seems to read something from nvp and encrypt it with AES, not sure what | ||
244 | * - 0x24 can write the same properties read by 0x23 */ | ||
217 | }; | 245 | }; |
218 | 246 | ||
219 | #define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0])) | 247 | #define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0])) |
220 | 248 | ||
249 | uint16_t get_big_endian16(void *_buf) | ||
250 | { | ||
251 | uint8_t *buf = _buf; | ||
252 | return buf[0] << 16 | buf[1]; | ||
253 | } | ||
254 | |||
255 | uint32_t get_big_endian32(void *_buf) | ||
256 | { | ||
257 | uint8_t *buf = _buf; | ||
258 | return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; | ||
259 | } | ||
260 | |||
261 | void set_big_endian16(void *_buf, uint16_t val) | ||
262 | { | ||
263 | uint8_t *buf = _buf; | ||
264 | buf[1] = val & 0xff; | ||
265 | buf[0] = (val >> 8) & 0xff; | ||
266 | } | ||
267 | |||
268 | void set_big_endian32(void *_buf, uint32_t val) | ||
269 | { | ||
270 | uint8_t *buf = _buf; | ||
271 | buf[3] = val & 0xff; | ||
272 | buf[2] = (val >> 8) & 0xff; | ||
273 | buf[1] = (val >> 16) & 0xff; | ||
274 | buf[0] = (val >> 24) & 0xff; | ||
275 | } | ||
276 | |||
277 | uint32_t get_little_endian32(void *_buf) | ||
278 | { | ||
279 | uint8_t *buf = _buf; | ||
280 | return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0]; | ||
281 | } | ||
282 | |||
283 | void set_little_endian32(void *_buf, uint32_t val) | ||
284 | { | ||
285 | uint8_t *buf = _buf; | ||
286 | buf[0] = val & 0xff; | ||
287 | buf[1] = (val >> 8) & 0xff; | ||
288 | buf[2] = (val >> 16) & 0xff; | ||
289 | buf[3] = (val >> 24) & 0xff; | ||
290 | } | ||
291 | |||
221 | int get_dnk_prop(int argc, char **argv) | 292 | int get_dnk_prop(int argc, char **argv) |
222 | { | 293 | { |
223 | if(argc != 1 && argc != 4) | 294 | if(argc != 1 && argc != 4) |
@@ -247,6 +318,7 @@ int get_dnk_prop(int argc, char **argv) | |||
247 | } | 318 | } |
248 | else | 319 | else |
249 | { | 320 | { |
321 | prop.desc = "Property"; | ||
250 | prop.cmd = strtoul(argv[0], NULL, 0); | 322 | prop.cmd = strtoul(argv[0], NULL, 0); |
251 | prop.subcmd = strtoul(argv[1], NULL, 0); | 323 | prop.subcmd = strtoul(argv[1], NULL, 0); |
252 | prop.size = strtoul(argv[2], NULL, 0); | 324 | prop.size = strtoul(argv[2], NULL, 0); |
@@ -255,7 +327,7 @@ int get_dnk_prop(int argc, char **argv) | |||
255 | 327 | ||
256 | char *buffer = buffer_alloc(prop.size + 1); | 328 | char *buffer = buffer_alloc(prop.size + 1); |
257 | int buffer_size = prop.size; | 329 | int buffer_size = prop.size; |
258 | int ret = do_dnk_cmd(prop.cmd, prop.subcmd, 0, buffer, &buffer_size); | 330 | int ret = do_dnk_cmd(true, prop.cmd, prop.subcmd, 0, buffer, &buffer_size); |
259 | if(ret) | 331 | if(ret) |
260 | return ret; | 332 | return ret; |
261 | if(buffer_size == 0) | 333 | if(buffer_size == 0) |
@@ -269,197 +341,185 @@ int get_dnk_prop(int argc, char **argv) | |||
269 | return 2; | 341 | return 2; |
270 | } | 342 | } |
271 | buffer[buffer_size] = 0; | 343 | buffer[buffer_size] = 0; |
344 | cprintf(GREEN, "%s:", prop.desc); | ||
272 | if(prop.flags & DNK_STRING) | 345 | if(prop.flags & DNK_STRING) |
273 | cprintf_field("Property: ", "%s\n", buffer); | 346 | cprintf(YELLOW, " %s\n", buffer); |
274 | else if(prop.flags & DNK_UINT32) | 347 | else if(prop.flags & DNK_UINT32) |
275 | cprintf_field("Property: ", "0x%x\n", *(uint32_t *)buffer); | 348 | { |
349 | uint32_t val = get_big_endian32(buffer); | ||
350 | if(prop.flags & DNK_HEX) | ||
351 | cprintf(YELLOW, " 0x%x\n", val); | ||
352 | else | ||
353 | cprintf(YELLOW, " %u\n", val); | ||
354 | } | ||
276 | else | 355 | else |
277 | { | 356 | { |
278 | cprintf(GREEN, "Property:\n"); | 357 | printf(YELLOW, "\n"); |
279 | print_hex(buffer, buffer_size); | 358 | print_hex(buffer, buffer_size); |
280 | } | 359 | } |
281 | return 0; | 360 | return 0; |
282 | } | 361 | } |
283 | 362 | ||
284 | #define NVP_EXACT_LENGTH (1 << 0) | 363 | int get_model_and_series(int *model_index, int *series_index) |
285 | #define NVP_STRING (1 << 1) | ||
286 | #define NVP_UINT32 (1 << 2) | ||
287 | |||
288 | struct nvp_prop_t | ||
289 | { | 364 | { |
290 | const char *name; | 365 | /* we need to get the model ID: code stolen from get_dnk_prop */ |
291 | const char *desc; | 366 | uint8_t mid_buf[4]; |
292 | uint8_t node; | 367 | int mid_buf_size = sizeof(mid_buf); |
293 | int size; | 368 | int ret = do_dnk_cmd(true, 0x23, 9, 0, mid_buf, &mid_buf_size); |
294 | unsigned flags; | 369 | if(ret) |
295 | }; | ||
296 | |||
297 | #define NVP_ENTRY(node, name,desc,size,flag) { name, desc, node, size, flag } | ||
298 | |||
299 | struct nvp_prop_t nvp_prop_list[] = | ||
300 | { | ||
301 | NVP_ENTRY(1, "bti", "boot image", 262144, 0), | ||
302 | NVP_ENTRY(2, "hdi", "hold image", 262144, 0), | ||
303 | NVP_ENTRY(3, "cng", "aad key", 704, 0), | ||
304 | NVP_ENTRY(4, "ser", "serial number", 16, NVP_STRING), | ||
305 | NVP_ENTRY(5, "app", "application parameter", 4096, 0), | ||
306 | NVP_ENTRY(6, "eri", "update error image", 262144, 0), | ||
307 | NVP_ENTRY(7, "dcc", "secure clock", 20, 0), | ||
308 | NVP_ENTRY(8, "mdl", "middleware parameter", 8, 0), | ||
309 | NVP_ENTRY(9, "fup", "firmware update flag", 4, 0), | ||
310 | NVP_ENTRY(10, "bok", "beep ok flag", 4, 0), | ||
311 | NVP_ENTRY(11, "shp", "ship information", 32, 0), | ||
312 | NVP_ENTRY(12, "dba", "aad icv", 160, 0), | ||
313 | NVP_ENTRY(13, "dbv", "empr key", 520, 0), | ||
314 | NVP_ENTRY(14, "tr0", "EKB 0", 16384, 0), | ||
315 | NVP_ENTRY(15, "tr1", "EKB 1", 16384, 0), | ||
316 | NVP_ENTRY(16, "mid", "model id", 64, 0), | ||
317 | NVP_ENTRY(17, "tst", "test mode flag", 4, 0), | ||
318 | NVP_ENTRY(18, "gty", "getty mode flag", 4, 0), | ||
319 | NVP_ENTRY(19, "fui", "update image", 262144, 0), | ||
320 | NVP_ENTRY(20, "lbi", "low battery image", 262144, 0), | ||
321 | NVP_ENTRY(21, "dor", "key mode (debug/release)", 4, 0), | ||
322 | NVP_ENTRY(22, "edw", "quick shutdown flag", 4, 0), | ||
323 | NVP_ENTRY(23, "ubp", "u-boot password", 32, 0), | ||
324 | NVP_ENTRY(24, "syi", "system information", 4, 0), | ||
325 | NVP_ENTRY(25, "exm", "exception monitor mode", 4, 0), | ||
326 | NVP_ENTRY(26, "pcd", "product code", 5, 0), | ||
327 | NVP_ENTRY(27, "btc", "battery calibration", 4, 0), | ||
328 | NVP_ENTRY(28, "rnd", "wmt key", 64, 0), | ||
329 | NVP_ENTRY(29, "ufn", "update file name", 8, 0), | ||
330 | NVP_ENTRY(30, "sdp", "sound driver parameter", 64, 0), | ||
331 | NVP_ENTRY(31, "ncp", "noise cancel driver parameter", 64, 0), | ||
332 | NVP_ENTRY(32, "kas", "key and signature", 64, 0), | ||
333 | NVP_ENTRY(33, "sfi", "starfish id", 64, 0), | ||
334 | NVP_ENTRY(34, "rtc", "rtc alarm", 16, 0), | ||
335 | NVP_ENTRY(35, "bpr", "bluetooth address", 2048, 0), | ||
336 | NVP_ENTRY(36, "e00", "EMPR 0", 1024, 0), | ||
337 | NVP_ENTRY(37, "e01", "EMPR 1", 1024, 0), | ||
338 | NVP_ENTRY(38, "e02", "EMPR 2", 1024, 0), | ||
339 | NVP_ENTRY(39, "e03", "EMPR 3", 1024, 0), | ||
340 | NVP_ENTRY(40, "e04", "EMPR 4", 1024, 0), | ||
341 | NVP_ENTRY(41, "e05", "EMPR 5", 1024, 0), | ||
342 | NVP_ENTRY(42, "e06", "EMPR 6", 1024, 0), | ||
343 | NVP_ENTRY(43, "e07", "EMPR 7", 1024, 0), | ||
344 | NVP_ENTRY(44, "e08", "EMPR 8", 1024, 0), | ||
345 | NVP_ENTRY(45, "e09", "EMPR 9", 1024, 0), | ||
346 | NVP_ENTRY(46, "e10", "EMPR 10", 1024, 0), | ||
347 | NVP_ENTRY(47, "e11", "EMPR 11", 1024, 0), | ||
348 | NVP_ENTRY(48, "e12", "EMPR 12", 1024, 0), | ||
349 | NVP_ENTRY(49, "e13", "EMPR 13", 1024, 0), | ||
350 | NVP_ENTRY(50, "e14", "EMPR 14", 1024, 0), | ||
351 | NVP_ENTRY(51, "e15", "EMPR 15", 1024, 0), | ||
352 | NVP_ENTRY(52, "e16", "EMPR 16", 1024, 0), | ||
353 | NVP_ENTRY(53, "e17", "EMPR 17", 1024, 0), | ||
354 | NVP_ENTRY(54, "e18", "EMPR 18", 1024, 0), | ||
355 | NVP_ENTRY(55, "e19", "EMPR 19", 1024, 0), | ||
356 | NVP_ENTRY(56, "e20", "EMPR 20", 1024, 0), | ||
357 | NVP_ENTRY(57, "e21", "EMPR 21", 1024, 0), | ||
358 | NVP_ENTRY(58, "e22", "EMPR 22", 1024, 0), | ||
359 | NVP_ENTRY(59, "e23", "EMPR 23", 1024, 0), | ||
360 | NVP_ENTRY(60, "e24", "EMPR 24", 1024, 0), | ||
361 | NVP_ENTRY(61, "e25", "EMPR 25", 1024, 0), | ||
362 | NVP_ENTRY(62, "e26", "EMPR 26", 1024, 0), | ||
363 | NVP_ENTRY(63, "e27", "EMPR 27", 1024, 0), | ||
364 | NVP_ENTRY(64, "e28", "EMPR 28", 1024, 0), | ||
365 | NVP_ENTRY(65, "e29", "EMPR 29", 1024, 0), | ||
366 | NVP_ENTRY(66, "e30", "EMPR 30", 1024, 0), | ||
367 | NVP_ENTRY(67, "e31", "EMPR 31", 1024, 0), | ||
368 | NVP_ENTRY(68, "clv", "color variation", 4, 0), | ||
369 | NVP_ENTRY(69, "slp", "time out to sleep", 4, 0), | ||
370 | NVP_ENTRY(70, "ipt", "disable iptable flag", 4, 0), | ||
371 | NVP_ENTRY(71, "mtm", "marlin time", 64, 0), | ||
372 | NVP_ENTRY(72, "mcr", "marlin crl", 16384, 0), | ||
373 | NVP_ENTRY(73, "mdk", "marlin device key", 33024, 0), | ||
374 | NVP_ENTRY(74, "muk", "marlin user key", 24576, 0), | ||
375 | NVP_ENTRY(75, "pts", "wifi protected setup", 4, 0), | ||
376 | NVP_ENTRY(76, "skt", "slacker time", 16, 0), | ||
377 | NVP_ENTRY(77, "mac", "wifi mac address", 6, 0), | ||
378 | NVP_ENTRY(78, "apd", "application debug mode flag", 4, 0), | ||
379 | NVP_ENTRY(79, "blf", "browser log mode flag", 4, 0), | ||
380 | NVP_ENTRY(80, "hld", "hold mode", 4, 0), | ||
381 | NVP_ENTRY(81, "skd", "slacker id file", 8224, 0), | ||
382 | NVP_ENTRY(82, "fmp", "fm parameter", 16, 0), | ||
383 | NVP_ENTRY(83, "sps", "speaker ship info", 4, 0), | ||
384 | NVP_ENTRY(84, "msc", "mass storage class mode", 4, 0), | ||
385 | NVP_ENTRY(85, "vrt", "europe vol regulation flag", 4, 0), | ||
386 | NVP_ENTRY(86, "psk", "bluetooth pskey", 512, 0), | ||
387 | NVP_ENTRY(87, "bml", "btmw log mode flag", 4, 0), | ||
388 | NVP_ENTRY(88, "bfd", "btmw factory scdb", 512, 0), | ||
389 | NVP_ENTRY(89, "bfp", "btmw factory pair info", 512, 0), | ||
390 | }; | ||
391 | |||
392 | #define NR_NVP_PROPS (sizeof(nvp_prop_list) / sizeof(nvp_prop_list[0])) | ||
393 | |||
394 | int get_dnk_nvp(int argc, char **argv) | ||
395 | { | ||
396 | if(argc != 1 && argc != 3) | ||
397 | { | 370 | { |
398 | printf("You must specify a known nvp node or a full node specification:\n"); | 371 | printf("Cannot get model ID from device: %d\n", ret); |
399 | printf("Full usage: <node> <size> <flags>\n"); | 372 | return 2; |
400 | printf("Node usage: <node>\n"); | ||
401 | printf("Nodes:\n"); | ||
402 | for(unsigned i = 0; i < NR_NVP_PROPS; i++) | ||
403 | printf(" %s\t%s\n", nvp_prop_list[i].name, nvp_prop_list[i].desc); | ||
404 | return 1; | ||
405 | } | 373 | } |
406 | 374 | if(mid_buf_size != sizeof(mid_buf)) | |
407 | struct nvp_prop_t prop; | ||
408 | memset(&prop, 0, sizeof(prop)); | ||
409 | if(argc == 1) | ||
410 | { | 375 | { |
411 | for(unsigned i = 0; i < NR_NVP_PROPS; i++) | 376 | printf("Cannot get model ID from device: device didn't send the expected amount of data\n"); |
412 | if(strcmp(nvp_prop_list[i].name, argv[0]) == 0) | 377 | return 3; |
413 | prop = nvp_prop_list[i]; | ||
414 | if(prop.name == NULL) | ||
415 | { | ||
416 | cprintf(GREY, "Unknown node '%s'\n", argv[0]); | ||
417 | return 1; | ||
418 | } | ||
419 | } | 378 | } |
420 | else | 379 | unsigned long model_id = get_big_endian32(&mid_buf); |
380 | *model_index = -1; | ||
381 | for(int i = 0; i < NWZ_MODEL_COUNT; i++) | ||
382 | if(nwz_model[i].mid == model_id) | ||
383 | *model_index = i; | ||
384 | cprintf_field("Model: ", "%s\n", *model_index == -1 ? "Unknown" : nwz_model[*model_index].name); | ||
385 | if(*model_index == -1) | ||
386 | { | ||
387 | printf("Your device is not supported. Please contact developers.\n"); | ||
388 | return 3; | ||
389 | } | ||
390 | *series_index = -1; | ||
391 | for(int i = 0; i < NWZ_SERIES_COUNT; i++) | ||
392 | for(int j = 0; j < nwz_series[i].mid_count; j++) | ||
393 | if(nwz_series[i].mid[j] == model_id) | ||
394 | *series_index = i; | ||
395 | cprintf_field("Series: ", "%s\n", *series_index == -1 ? "Unknown" : nwz_series[*series_index].name); | ||
396 | if(*series_index == -1) | ||
421 | { | 397 | { |
422 | prop.node = strtoul(argv[0], NULL, 0); | 398 | printf("Your device is not supported. Please contact developers.\n"); |
423 | prop.size = strtoul(argv[1], NULL, 0); | 399 | return 3; |
424 | prop.flags = strtoul(argv[2], NULL, 0); | ||
425 | } | 400 | } |
401 | return 0; | ||
402 | } | ||
426 | 403 | ||
427 | int buffer_size = prop.size + 4; | 404 | /* read nvp node, retrun nonzero on error */ |
428 | uint8_t *buffer = buffer_alloc(buffer_size + 1); | 405 | int read_nvp_node(int series_index, enum nwz_nvp_node_t node, void *buffer, int size) |
429 | int ret = do_dnk_cmd(0x23, 10, prop.node, buffer, &buffer_size); | 406 | { |
407 | int node_index = NWZ_NVP_INVALID; | ||
408 | if(nwz_series[series_index].nvp_index) | ||
409 | node_index = (*nwz_series[series_index].nvp_index)[node]; | ||
410 | if(node_index == NWZ_NVP_INVALID) | ||
411 | { | ||
412 | printf("This device doesn't have node '%s'\n", nwz_nvp[node].name); | ||
413 | return 5; | ||
414 | } | ||
415 | /* the returned data has a 4 byte header: | ||
416 | * - byte 0/1 is the para_noise index, written as a 16bit big-endian number | ||
417 | * - byte 2/3 is the node index, written as a 16-bit big-endian number | ||
418 | * | ||
419 | * NOTE: byte 0 is always 0 because the OF always picks small para_noise | ||
420 | * indexes but I guess the actual encoding the one above */ | ||
421 | int xfer_size = size + 4; | ||
422 | uint8_t *xfer_buf = buffer_alloc(xfer_size); | ||
423 | int ret = do_dnk_cmd(true, 0x23, 10, node_index, xfer_buf, &xfer_size); | ||
430 | if(ret) | 424 | if(ret) |
431 | return ret; | 425 | return ret; |
432 | if(buffer_size <= 4) | 426 | if(xfer_size <= 4) |
433 | { | 427 | { |
428 | free(xfer_buf); | ||
434 | cprintf(GREY, "Device didn't send any data\n"); | 429 | cprintf(GREY, "Device didn't send any data\n"); |
435 | return 1; | 430 | return 6; |
436 | } | 431 | } |
437 | if(buffer[0] != 0 || buffer[2] != 0 || buffer[3] != prop.node) | 432 | if(get_big_endian16(xfer_buf + 2) != node_index) |
438 | { | 433 | { |
434 | free(xfer_buf); | ||
439 | cprintf(GREY, "Device responded with invalid data\n"); | 435 | cprintf(GREY, "Device responded with invalid data\n"); |
440 | return 1; | 436 | return 1; |
441 | } | 437 | } |
442 | 438 | if(xfer_size - 4 != (int)size) | |
443 | for(int i = 4, idx = buffer[1]; i < buffer_size; i++, idx++) | ||
444 | buffer[i] ^= para_noise[idx]; | ||
445 | buffer[buffer_size] = 0; | ||
446 | buffer += 4; | ||
447 | buffer_size -= 4; | ||
448 | if((prop.flags & DNK_EXACT_LENGTH) && buffer_size != prop.size) | ||
449 | { | 439 | { |
440 | free(xfer_buf); | ||
450 | cprintf(GREY, "Device didn't send the expected amount of data\n"); | 441 | cprintf(GREY, "Device didn't send the expected amount of data\n"); |
451 | return 2; | 442 | return 7; |
452 | } | 443 | } |
453 | 444 | /* unscramble and copy */ | |
454 | if(prop.flags & DNK_STRING) | 445 | for(int i = 4, idx = get_big_endian16(xfer_buf); i < xfer_size; i++, idx++) |
455 | cprintf_field("Node: ", "%s\n", buffer); | 446 | xfer_buf[i] ^= para_noise[idx % sizeof(para_noise)]; |
456 | else if(prop.flags & DNK_UINT32) | 447 | memcpy(buffer, xfer_buf + 4, size); |
457 | cprintf_field("Node: ", "0x%x\n", *(uint32_t *)buffer); | 448 | free(xfer_buf); |
458 | else | 449 | return 0; |
450 | } | ||
451 | |||
452 | /* read nvp node, retrun nonzero on error */ | ||
453 | int write_nvp_node(int series_index, enum nwz_nvp_node_t node, void *buffer, int size) | ||
454 | { | ||
455 | int node_index = NWZ_NVP_INVALID; | ||
456 | if(nwz_series[series_index].nvp_index) | ||
457 | node_index = (*nwz_series[series_index].nvp_index)[node]; | ||
458 | if(node_index == NWZ_NVP_INVALID) | ||
459 | { | 459 | { |
460 | cprintf(GREEN, "Node:\n"); | 460 | printf("This device doesn't have node '%s'\n", nwz_nvp[node].name); |
461 | print_hex(buffer, buffer_size); | 461 | return 5; |
462 | } | ||
463 | /* the data buffer is prepended with a 4 byte header: | ||
464 | * - byte 0/1 is the para_noise index, written as a 16bit big-endian number | ||
465 | * - byte 2/3 is the node index, written as a 16-bit big-endian number */ | ||
466 | int xfer_size = size + 4; | ||
467 | uint8_t *xfer_buf = buffer_alloc(xfer_size); | ||
468 | /* scramble, always use index 0 for para_noise */ | ||
469 | set_big_endian16(xfer_buf, 0); /* para_noise index */ | ||
470 | set_big_endian16(xfer_buf + 2, node_index); /* node index */ | ||
471 | memcpy(xfer_buf + 4, buffer, size); | ||
472 | for(int i = 4, idx = get_big_endian16(xfer_buf); i < xfer_size; i++, idx++) | ||
473 | xfer_buf[i] ^= para_noise[idx % sizeof(para_noise)]; | ||
474 | int ret = do_dnk_cmd(false, 0x24, 10, node_index, xfer_buf, &xfer_size); | ||
475 | if(ret) | ||
476 | return ret; | ||
477 | if(xfer_size - 4 != (int)size) | ||
478 | { | ||
479 | free(xfer_buf); | ||
480 | cprintf(GREY, "Wrong transger size\n"); | ||
481 | return 7; | ||
462 | } | 482 | } |
483 | free(xfer_buf); | ||
484 | return 0; | ||
485 | } | ||
486 | |||
487 | int get_dnk_nvp(int argc, char **argv) | ||
488 | { | ||
489 | if(argc != 1) | ||
490 | { | ||
491 | printf("You must specify a known nvp node or a full node specification:\n"); | ||
492 | printf("Node usage: <node>\n"); | ||
493 | printf("Nodes:\n"); | ||
494 | for(unsigned i = 0; i < NWZ_NVP_COUNT; i++) | ||
495 | printf(" %s\t%s\n", nwz_nvp[i].name, nwz_nvp[i].desc); | ||
496 | return 1; | ||
497 | } | ||
498 | int series_index, model_index; | ||
499 | int ret = get_model_and_series(&model_index, &series_index); | ||
500 | if(ret) | ||
501 | return ret; | ||
502 | /* find entry in NVP */ | ||
503 | enum nwz_nvp_node_t node = NWZ_NVP_COUNT; | ||
504 | for(int i = 0; i < NWZ_NVP_COUNT; i++) | ||
505 | if(strcmp(nwz_nvp[i].name, argv[0]) == 0) | ||
506 | node = i; | ||
507 | if(node== NWZ_NVP_COUNT) | ||
508 | { | ||
509 | printf("I don't know about node '%s'\n", argv[0]); | ||
510 | return 4; | ||
511 | } | ||
512 | uint8_t *buffer = malloc(nwz_nvp[node].size); | ||
513 | ret = read_nvp_node(series_index, node, buffer, nwz_nvp[node].size); | ||
514 | if(ret != 0) | ||
515 | { | ||
516 | free(buffer); | ||
517 | return ret; | ||
518 | } | ||
519 | cprintf(GREEN, "%s:\n", nwz_nvp[node].name); | ||
520 | print_hex(buffer, nwz_nvp[node].size); | ||
521 | |||
522 | free(buffer); | ||
463 | return 0; | 523 | return 0; |
464 | } | 524 | } |
465 | 525 | ||
@@ -474,6 +534,7 @@ struct dpcc_prop_t | |||
474 | struct dpcc_prop_t dpcc_prop_list[] = | 534 | struct dpcc_prop_t dpcc_prop_list[] = |
475 | { | 535 | { |
476 | { "dev_info", "DEVINFO", 0, 0x80 }, | 536 | { "dev_info", "DEVINFO", 0, 0x80 }, |
537 | /* there are more but they are very obscure */ | ||
477 | }; | 538 | }; |
478 | 539 | ||
479 | #define NR_DPCC_PROPS (sizeof(dpcc_prop_list) / sizeof(dpcc_prop_list[0])) | 540 | #define NR_DPCC_PROPS (sizeof(dpcc_prop_list) / sizeof(dpcc_prop_list[0])) |
@@ -606,6 +667,8 @@ int do_fw_upgrade(int argc, char **argv) | |||
606 | { | 667 | { |
607 | (void) argc; | 668 | (void) argc; |
608 | (void )argv; | 669 | (void )argv; |
670 | /* older devices may have used subcommand 3 instead of 4, but this is not | ||
671 | * supported by any device I have seen */ | ||
609 | uint8_t cdb[12] = {0xfc, 0, 0x04, 'd', 'b', 'm', 'n', 0, 0x80, 0, 0, 0}; | 672 | uint8_t cdb[12] = {0xfc, 0, 0x04, 'd', 'b', 'm', 'n', 0, 0x80, 0, 0, 0}; |
610 | 673 | ||
611 | char *buffer = buffer_alloc(0x81); | 674 | char *buffer = buffer_alloc(0x81); |
@@ -625,6 +688,130 @@ int do_fw_upgrade(int argc, char **argv) | |||
625 | return 0; | 688 | return 0; |
626 | } | 689 | } |
627 | 690 | ||
691 | static struct | ||
692 | { | ||
693 | unsigned long dest; | ||
694 | const char *name; | ||
695 | } g_dest_list[] = | ||
696 | { | ||
697 | { 0, "J" }, | ||
698 | { 1, "U" }, | ||
699 | { 0x101, "U2" }, | ||
700 | { 0x201, "U3" }, | ||
701 | { 0x301, "CA" }, | ||
702 | { 2, "CEV" }, | ||
703 | { 0x102, "CE7" }, | ||
704 | { 3, "CEW" }, | ||
705 | { 0x103, "CEW2" }, | ||
706 | { 4, "CN" }, | ||
707 | { 5, "KR" }, | ||
708 | { 6, "E" }, | ||
709 | { 0x106, "MX" }, | ||
710 | { 0x206, "E2" }, | ||
711 | { 0x306, "MX3" }, | ||
712 | { 7, "TW" }, | ||
713 | }; | ||
714 | |||
715 | #define DEST_COUNT (sizeof(g_dest_list) / sizeof(g_dest_list[0])) | ||
716 | |||
717 | int do_dest(int argc, char **argv) | ||
718 | { | ||
719 | /* it is possile to write any NVP node using the SCSI interface but only | ||
720 | * give the user the possibility to write destination, because that's the | ||
721 | * most useful one */ | ||
722 | if(argc != 1 && argc != 3) | ||
723 | { | ||
724 | printf("Usage: get\n"); | ||
725 | printf("Usage: set <dest> <sps>\n"); | ||
726 | printf("Destination (<dest>) can be either an integer or one of:\n"); | ||
727 | for(size_t i = 0; i < DEST_COUNT; i++) | ||
728 | printf(" %s\n", g_dest_list[i].name); | ||
729 | printf("Sound pressure (<sps>) can be be an integer, 'on' or 'off'\n"); | ||
730 | return 1; | ||
731 | } | ||
732 | /* get model/series */ | ||
733 | int model_index, series_index; | ||
734 | int ret = get_model_and_series(&model_index, &series_index); | ||
735 | /* in all cases, we need to read shp */ | ||
736 | uint8_t *shp = malloc(nwz_nvp[NWZ_NVP_SHP].size); | ||
737 | ret = read_nvp_node(series_index, NWZ_NVP_SHP, shp, nwz_nvp[NWZ_NVP_SHP].size); | ||
738 | if(ret != 0) | ||
739 | { | ||
740 | free(shp); | ||
741 | return ret; | ||
742 | } | ||
743 | /* get */ | ||
744 | if(strcmp(argv[0], "get") == 0) | ||
745 | { | ||
746 | if(argc != 1) | ||
747 | { | ||
748 | printf("Too many arguments for get\n"); | ||
749 | free(shp); | ||
750 | return 2; | ||
751 | } | ||
752 | const char *dst_name = "Unknown"; | ||
753 | unsigned long dst = get_little_endian32(shp); | ||
754 | for(size_t i = 0; i < DEST_COUNT; i++) | ||
755 | if(dst == g_dest_list[i].dest) | ||
756 | dst_name = g_dest_list[i].name; | ||
757 | printf("Destination: %s (%lx)\n", dst_name, dst); | ||
758 | unsigned long sps = get_little_endian32(shp + 4); | ||
759 | printf("Sound pressure: %lu (%s)\n", sps, sps == 0 ? "off" : "on"); | ||
760 | free(shp); | ||
761 | } | ||
762 | /* set */ | ||
763 | if(strcmp(argv[0], "set") == 0) | ||
764 | { | ||
765 | if(argc != 3) | ||
766 | { | ||
767 | printf("Not enough arguments for set\n"); | ||
768 | free(shp); | ||
769 | return 2; | ||
770 | } | ||
771 | /* try to parse dest as integer */ | ||
772 | char *end; | ||
773 | unsigned long dst = strtoul(argv[1], &end, 0); | ||
774 | if(*end) | ||
775 | { | ||
776 | /* assume string */ | ||
777 | int index = -1; | ||
778 | for(size_t i = 0; i < DEST_COUNT; i++) | ||
779 | if(strcmp(argv[1], g_dest_list[i].name) == 0) | ||
780 | index = i; | ||
781 | if(index == -1) | ||
782 | { | ||
783 | printf("Unknown destination '%s'\n", argv[1]); | ||
784 | free(shp); | ||
785 | return 3; | ||
786 | } | ||
787 | dst = g_dest_list[index].dest; | ||
788 | } | ||
789 | /* try to parse sps as integer */ | ||
790 | /* try to parse dest as integer */ | ||
791 | unsigned long sps = strtoul(argv[2], &end, 0); | ||
792 | if(*end) | ||
793 | { | ||
794 | /* assume string */ | ||
795 | if(strcmp(argv[2], "on") == 0) | ||
796 | sps = 1; | ||
797 | else if(strcmp(argv[2], "off") == 0) | ||
798 | sps = 0; | ||
799 | else | ||
800 | { | ||
801 | printf("Unknown sound pressure setting '%s'\n", argv[2]); | ||
802 | free(shp); | ||
803 | return 3; | ||
804 | } | ||
805 | } | ||
806 | set_little_endian32(shp, dst); | ||
807 | set_little_endian32(shp + 4, sps); | ||
808 | int ret = write_nvp_node(series_index, NWZ_NVP_SHP, shp, nwz_nvp[NWZ_NVP_SHP].size); | ||
809 | free(shp); | ||
810 | return ret; | ||
811 | } | ||
812 | return 0; | ||
813 | } | ||
814 | |||
628 | typedef int (*cmd_fn_t)(int argc, char **argv); | 815 | typedef int (*cmd_fn_t)(int argc, char **argv); |
629 | 816 | ||
630 | struct cmd_t | 817 | struct cmd_t |
@@ -642,6 +829,7 @@ struct cmd_t cmd_list[] = | |||
642 | { "get_user_time", "Get user time", get_user_time }, | 829 | { "get_user_time", "Get user time", get_user_time }, |
643 | { "get_dev_info", "Get device info", get_dev_info }, | 830 | { "get_dev_info", "Get device info", get_dev_info }, |
644 | { "do_fw_upgrade", "Do a firmware upgrade", do_fw_upgrade }, | 831 | { "do_fw_upgrade", "Do a firmware upgrade", do_fw_upgrade }, |
832 | { "dest_tool", "Get/Set destination and sound pressure regulation", do_dest }, | ||
645 | }; | 833 | }; |
646 | 834 | ||
647 | #define NR_CMDS (sizeof(cmd_list) / sizeof(cmd_list[0])) | 835 | #define NR_CMDS (sizeof(cmd_list) / sizeof(cmd_list[0])) |