summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmaury Pouly <amaury.pouly@gmail.com>2016-11-11 16:04:15 +0100
committerAmaury Pouly <amaury.pouly@gmail.com>2016-11-11 16:07:14 +0100
commit33856d9ceb6df817c4f40f42bfcf9ee9fa188848 (patch)
treeb6b00a247fa13505400aaa886994c9b751e6f48d
parentc95e30b75d75b674f0d645b7c41377bbd0511213 (diff)
downloadrockbox-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
-rw-r--r--utils/nwztools/scsitools/Makefile14
-rw-r--r--utils/nwztools/scsitools/scsitool.c536
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 @@
1DEFINES= 1DEFINES=
2CC=gcc 2CC=gcc
3LD=gcc 3LD=gcc
4CFLAGS=-g -std=c99 -W -Wall $(DEFINES) 4NWZ_DB_DIR=../database
5INCLUDES=-I$(NWZ_DB_DIR)
6CFLAGS=-std=c99 -W -Wall $(DEFINES) $(INCLUDES)
5LDFLAGS=-lsgutils2 7LDFLAGS=-lsgutils2
6BINS=scsitool 8BINS=scsitool
7 9
8all: $(BINS) 10all: $(BINS)
9 11
10%.o: %.c 12scsitool: scsitool.c misc.c para_noise.c $(NWZ_DB_DIR)/nwz_db.c
11 $(CC) $(CFLAGS) -c -o $@ $< 13 $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS)
12
13scsitool: scsitool.o misc.o para_noise.o
14 $(LD) -o $@ $^ $(LDFLAGS)
15 14
16clean: 15clean:
17 rm -fr *.o
18
19veryclean:
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
174int 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
188int 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
200struct dnk_prop_t 215struct 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
209struct dnk_prop_t dnk_prop_list[] = 225struct 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
249uint16_t get_big_endian16(void *_buf)
250{
251 uint8_t *buf = _buf;
252 return buf[0] << 16 | buf[1];
253}
254
255uint32_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
261void 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
268void 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
277uint32_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
283void 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
221int get_dnk_prop(int argc, char **argv) 292int 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) 363int get_model_and_series(int *model_index, int *series_index)
285#define NVP_STRING (1 << 1)
286#define NVP_UINT32 (1 << 2)
287
288struct 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
299struct 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
394int 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); 405int 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 */
453int 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
487int 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
474struct dpcc_prop_t dpcc_prop_list[] = 534struct 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
691static 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
717int 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
628typedef int (*cmd_fn_t)(int argc, char **argv); 815typedef int (*cmd_fn_t)(int argc, char **argv);
629 816
630struct cmd_t 817struct 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]))