blob: e6051067b741114984404ee835f16c5212abfe63 [file] [log] [blame]
Amaury Pouly3e1c4922012-11-03 02:29:00 +01001/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2012 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 <stdint.h>
23#include <stdbool.h>
24#include <stdlib.h>
25#include <stddef.h>
26#include <string.h>
27#include <getopt.h>
28#include <stdarg.h>
29#include <ctype.h>
30#include <sys/types.h>
31#include <sys/stat.h>
32#include <fcntl.h>
33#include <unistd.h>
Amaury Pouly76c73c72017-01-07 16:31:28 +010034#include "rbscsi.h"
Amaury Pouly3e1c4922012-11-03 02:29:00 +010035#include "misc.h"
Amaury Pouly6d04ed32012-11-03 04:21:54 +010036#include "para_noise.h"
Amaury Pouly33856d92016-11-11 16:04:15 +010037#include "nwz_db.h"
Amaury Pouly3e1c4922012-11-03 02:29:00 +010038
39bool g_debug = false;
Amaury Poulyad2297d2017-01-04 17:10:48 +010040const char *g_force_series = NULL;
Amaury Pouly3e1c4922012-11-03 02:29:00 +010041char *g_out_prefix = NULL;
Amaury Pouly76c73c72017-01-07 16:31:28 +010042rb_scsi_device_t g_dev;
Amaury Pouly3e1c4922012-11-03 02:29:00 +010043
44static void print_hex(void *_buffer, int buffer_size)
45{
46 uint8_t *buffer = _buffer;
47 for(int i = 0; i < buffer_size; i += 16)
48 {
49 for(int j = 0; j < 16; j++)
50 {
51 if(i + j < buffer_size)
52 cprintf(YELLOW, " %02x", buffer[i + j]);
53 else
54 cprintf(YELLOW, " ");
55 }
56 printf(" ");
57 for(int j = 0; j < 16; j++)
58 {
59 if(i + j < buffer_size)
60 cprintf(RED, "%c", isprint(buffer[i + j]) ? buffer[i + j] : '.');
61 else
62 cprintf(RED, " ");
63 }
64 printf("\n");
65 }
66}
67
68/* Do read */
69#define DO_READ (1 << 1)
70/* Do write */
71#define DO_WRITE (1 << 2)
72
73/* returns <0 on error and status otherwise */
74int do_scsi(uint8_t *cdb, int cdb_size, unsigned flags, void *sense, int *sense_size, void *buffer, int *buf_size)
75{
Amaury Pouly76c73c72017-01-07 16:31:28 +010076 struct rb_scsi_raw_cmd_t raw;
77 raw.dir = RB_SCSI_NONE;
Amaury Pouly3e1c4922012-11-03 02:29:00 +010078 if(flags & DO_READ)
Amaury Pouly76c73c72017-01-07 16:31:28 +010079 raw.dir = RB_SCSI_READ;
Amaury Pouly3e1c4922012-11-03 02:29:00 +010080 if(flags & DO_WRITE)
Amaury Pouly76c73c72017-01-07 16:31:28 +010081 raw.dir = RB_SCSI_WRITE;
82 raw.cdb_len = cdb_size;
83 raw.cdb = cdb;
84 raw.buf = buffer;
85 raw.buf_len = *buf_size;
86 raw.sense_len = *sense_size;
87 raw.sense = sense;
88 raw.tmo = 5;
89 int ret = rb_scsi_raw_xfer(g_dev, &raw);
90 *sense_size = raw.sense_len;
91 *buf_size = raw.buf_len;
92 return ret == RB_SCSI_OK || ret == RB_SCSI_SENSE ? raw.status : -ret;
Amaury Pouly3e1c4922012-11-03 02:29:00 +010093}
94
95int do_sense_analysis(int status, uint8_t *sense, int sense_size)
96{
Amaury Pouly76c73c72017-01-07 16:31:28 +010097 if(status != 0 && g_debug)
Amaury Pouly6d04ed32012-11-03 04:21:54 +010098 {
Amaury Pouly76c73c72017-01-07 16:31:28 +010099 cprintf(GREY, "Status: %d\n", status);
100 cprintf(GREY, "Sense:");
101 for(int i = 0; i < sense_size; i++)
102 cprintf(GREY, " %02x", sense[i]);
103 cprintf(GREY, "\n");
104 rb_scsi_decode_sense(g_dev, sense, sense_size);
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100105 }
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100106 return status;
107}
108
Amaury Pouly33856d92016-11-11 16:04:15 +0100109/*
110 * SCSI commands
111 */
112#define CMD_A3 0xa3 /* start a complicated, authenticated, session to do things */
113#define CMD_A4 0xa4 /* start a complicated, authenticated, session to do things */
114#define CMD_EMPR_DPCC 0xd7
115#define CMD_DNK 0xdd
116#define CMD_DPCC 0xfb
117
118/*
119 * DNK: command is in cdb[10], subcommand in cdb[11], cdb[7] must be 0xbc
120 */
121
122int do_dnk_cmd(bool read, uint32_t cmd, uint8_t sub_cmd, uint16_t arg, void *buffer, int *buffer_size)
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100123{
Amaury Pouly33856d92016-11-11 16:04:15 +0100124 uint8_t cdb[12] = {CMD_DNK, 0, 0, 0, 0, 0, 0, 0xbc, 0, 0, 0, 0};
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100125 cdb[10] = cmd;
126 cdb[11] = sub_cmd;
127 cdb[8] = (*buffer_size) >> 8;
128 cdb[9] = (*buffer_size) & 0xff;
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100129 cdb[4] = (arg >> 8) & 0xff;
130 cdb[5] = arg & 0xff;
Amaury Pouly33856d92016-11-11 16:04:15 +0100131
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100132 uint8_t sense[32];
133 int sense_size = 32;
Amaury Pouly33856d92016-11-11 16:04:15 +0100134
135 int ret = do_scsi(cdb, 12, read ? DO_READ : DO_WRITE, sense, &sense_size, buffer, buffer_size);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100136 if(ret < 0)
137 return ret;
138 ret = do_sense_analysis(ret, sense, sense_size);
139 if(ret)
140 return ret;
141 return 0;
142}
143
144#define DNK_EXACT_LENGTH (1 << 0)
145#define DNK_STRING (1 << 1)
146#define DNK_UINT32 (1 << 2)
Amaury Pouly33856d92016-11-11 16:04:15 +0100147#define DNK_HEX (1 << 3)
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100148
149struct dnk_prop_t
150{
Amaury Pouly33856d92016-11-11 16:04:15 +0100151 const char *name;
152 const char *desc;
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100153 uint8_t cmd;
154 uint8_t subcmd;
155 int size;
156 unsigned flags;
157};
158
159struct dnk_prop_t dnk_prop_list[] =
160{
Amaury Pouly33856d92016-11-11 16:04:15 +0100161 { "serial_num", "Serial number", 0x23, 1, 8, DNK_STRING},
162 { "storage_size", "Storage size(GB)", 0x23, 4, 4, DNK_EXACT_LENGTH | DNK_UINT32},
163 { "product_id", "Product ID", 0x23, 6, 12, DNK_STRING},
164 { "destination", "Destination", 0x23, 8, 4, DNK_EXACT_LENGTH | DNK_UINT32},
165 { "model_id", "Model ID", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32 | DNK_HEX},
166 { "model_name", "Model Name", 0x12, 0, 64, DNK_STRING},
167 /* there are more obscure commands:
168 * - 0x11 returns a 10-byte packet containing a 8-byte "LeftIdl8", scrambled
169 * with para_noise (the 2-byte padding is random so that output is random
170 * until unscrambled)
171 * - 0x21 returns a 0x2b2 packet contaning a 0x2b0 "DNK", scrambled similarly
172 * - 0x22 can write the DNK (sending scrambled data again)
173 * - 0x23 has more subproperties:
174 * - 5 is "eDKS"
175 * - 7 is "ProductGroup"
176 * - 10 is nvp properties (see get_dnk_nvp)
177 * - 11 seems to read something from nvp and encrypt it with AES, not sure what
178 * - 0x24 can write the same properties read by 0x23 */
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100179};
180
181#define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0]))
182
Amaury Pouly33856d92016-11-11 16:04:15 +0100183uint16_t get_big_endian16(void *_buf)
184{
185 uint8_t *buf = _buf;
186 return buf[0] << 16 | buf[1];
187}
188
189uint32_t get_big_endian32(void *_buf)
190{
191 uint8_t *buf = _buf;
192 return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
193}
194
195void set_big_endian16(void *_buf, uint16_t val)
196{
197 uint8_t *buf = _buf;
198 buf[1] = val & 0xff;
199 buf[0] = (val >> 8) & 0xff;
200}
201
202void set_big_endian32(void *_buf, uint32_t val)
203{
204 uint8_t *buf = _buf;
205 buf[3] = val & 0xff;
206 buf[2] = (val >> 8) & 0xff;
207 buf[1] = (val >> 16) & 0xff;
208 buf[0] = (val >> 24) & 0xff;
209}
210
211uint32_t get_little_endian32(void *_buf)
212{
213 uint8_t *buf = _buf;
214 return buf[3] << 24 | buf[2] << 16 | buf[1] << 8 | buf[0];
215}
216
217void set_little_endian32(void *_buf, uint32_t val)
218{
219 uint8_t *buf = _buf;
220 buf[0] = val & 0xff;
221 buf[1] = (val >> 8) & 0xff;
222 buf[2] = (val >> 16) & 0xff;
223 buf[3] = (val >> 24) & 0xff;
224}
225
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100226int get_dnk_prop(int argc, char **argv)
227{
228 if(argc != 1 && argc != 4)
229 {
230 printf("You must specify a known property name or a full property specification:\n");
231 printf("Full usage: <cmd> <subcmd> <size> <flags>\n");
232 printf("Property usage: <prop>\n");
233 printf("Properties:");
234 for(unsigned i = 0; i < NR_DNK_PROPS; i++)
235 printf(" %s", dnk_prop_list[i].name);
236 printf("\n");
237 return 1;
238 }
239
240 struct dnk_prop_t prop;
241 memset(&prop, 0, sizeof(prop));
242 if(argc == 1)
243 {
244 for(unsigned i = 0; i < NR_DNK_PROPS; i++)
245 if(strcmp(dnk_prop_list[i].name, argv[0]) == 0)
246 prop = dnk_prop_list[i];
247 if(prop.name == NULL)
248 {
249 cprintf(GREY, "Unknown property '%s'\n", argv[0]);
250 return 1;
251 }
252 }
253 else
254 {
Amaury Pouly33856d92016-11-11 16:04:15 +0100255 prop.desc = "Property";
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100256 prop.cmd = strtoul(argv[0], NULL, 0);
257 prop.subcmd = strtoul(argv[1], NULL, 0);
258 prop.size = strtoul(argv[2], NULL, 0);
259 prop.flags = strtoul(argv[3], NULL, 0);
260 }
261
Amaury Pouly76c73c72017-01-07 16:31:28 +0100262 char *buffer = malloc(prop.size + 1);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100263 int buffer_size = prop.size;
Amaury Pouly33856d92016-11-11 16:04:15 +0100264 int ret = do_dnk_cmd(true, prop.cmd, prop.subcmd, 0, buffer, &buffer_size);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100265 if(ret)
266 return ret;
267 if(buffer_size == 0)
268 {
269 cprintf(GREY, "Device didn't send any data\n");
270 return 1;
271 }
272 if((prop.flags & DNK_EXACT_LENGTH) && buffer_size != prop.size)
273 {
274 cprintf(GREY, "Device didn't send the expected amount of data\n");
275 return 2;
276 }
277 buffer[buffer_size] = 0;
Amaury Pouly33856d92016-11-11 16:04:15 +0100278 cprintf(GREEN, "%s:", prop.desc);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100279 if(prop.flags & DNK_STRING)
Amaury Pouly33856d92016-11-11 16:04:15 +0100280 cprintf(YELLOW, " %s\n", buffer);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100281 else if(prop.flags & DNK_UINT32)
Amaury Pouly33856d92016-11-11 16:04:15 +0100282 {
283 uint32_t val = get_big_endian32(buffer);
284 if(prop.flags & DNK_HEX)
285 cprintf(YELLOW, " 0x%x\n", val);
286 else
287 cprintf(YELLOW, " %u\n", val);
288 }
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100289 else
290 {
Amaury Pouly33856d92016-11-11 16:04:15 +0100291 printf(YELLOW, "\n");
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100292 print_hex(buffer, buffer_size);
293 }
294 return 0;
295}
296
Amaury Pouly33856d92016-11-11 16:04:15 +0100297int get_model_and_series(int *model_index, int *series_index)
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100298{
Amaury Poulyad2297d2017-01-04 17:10:48 +0100299 /* if the user forced the series, simply match by name, special for '?' which
300 * prompts the list */
301 if(g_force_series)
Amaury Pouly33856d92016-11-11 16:04:15 +0100302 {
Amaury Poulyad2297d2017-01-04 17:10:48 +0100303 cprintf(RED, "User forced series, auto-detection disabled\n");
304 *series_index = -1;
305 *model_index = -1;
306 for(int i = 0; i < NWZ_SERIES_COUNT; i++)
307 if(strcmp(nwz_series[i].codename, g_force_series) == 0)
308 *series_index = i;
309 /* display list on error */
310 if(*series_index == -1)
311 {
312 if(strcmp(g_force_series, "?") != 0)
313 cprintf(GREY, "Unrecognized series '%s'\n", g_force_series);
314 cprintf(OFF, "Series list:\n");
315 for(int i = 0; i < NWZ_SERIES_COUNT; i++)
316 printf(" %-10s %s\n", nwz_series[i].codename, nwz_series[i].name);
317 return -1;
318 }
Amaury Pouly33856d92016-11-11 16:04:15 +0100319 }
Amaury Poulyad2297d2017-01-04 17:10:48 +0100320 else
Amaury Pouly33856d92016-11-11 16:04:15 +0100321 {
Amaury Poulyad2297d2017-01-04 17:10:48 +0100322 /* we need to get the model ID: code stolen from get_dnk_prop */
323 uint8_t mid_buf[4];
324 int mid_buf_size = sizeof(mid_buf);
325 int ret = do_dnk_cmd(true, 0x23, 9, 0, mid_buf, &mid_buf_size);
326 if(ret)
327 {
328 cprintf(RED, "Cannot get model ID from device: %d\n", ret);
329 return 2;
330 }
331 if(mid_buf_size != sizeof(mid_buf))
332 {
333 cprintf(RED, "Cannot get model ID from device: device didn't send the expected amount of data\n");
334 return 3;
335 }
336 unsigned long model_id = get_big_endian32(&mid_buf);
337 *model_index = -1;
338 for(int i = 0; i < NWZ_MODEL_COUNT; i++)
339 if(nwz_model[i].mid == model_id)
340 *model_index = i;
341 if(*model_index == -1)
342 {
343 cprintf(RED, "Your device is not supported. Please contact developers.\n");
344 return 3;
345 }
346 *series_index = -1;
347 for(int i = 0; i < NWZ_SERIES_COUNT; i++)
348 for(int j = 0; j < nwz_series[i].mid_count; j++)
349 if(nwz_series[i].mid[j] == model_id)
350 *series_index = i;
351 if(*series_index == -1)
352 {
353 printf("Your device is not supported. Please contact developers.\n");
354 return 3;
355 }
Amaury Pouly33856d92016-11-11 16:04:15 +0100356 }
Amaury Pouly33856d92016-11-11 16:04:15 +0100357 cprintf_field("Model: ", "%s\n", *model_index == -1 ? "Unknown" : nwz_model[*model_index].name);
Amaury Pouly33856d92016-11-11 16:04:15 +0100358 cprintf_field("Series: ", "%s\n", *series_index == -1 ? "Unknown" : nwz_series[*series_index].name);
Amaury Pouly33856d92016-11-11 16:04:15 +0100359 return 0;
360}
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100361
Amaury Pouly1895af82017-01-08 16:07:18 +0100362/* Read nvp node, retrun nonzero on error, update size to actual length. The
363 * index is the raw node number sent to the device */
364int read_nvp_node(int node_index, void *buffer, size_t *size)
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100365{
Amaury Pouly33856d92016-11-11 16:04:15 +0100366 /* the returned data has a 4 byte header:
367 * - byte 0/1 is the para_noise index, written as a 16bit big-endian number
368 * - byte 2/3 is the node index, written as a 16-bit big-endian number
369 *
370 * NOTE: byte 0 is always 0 because the OF always picks small para_noise
371 * indexes but I guess the actual encoding the one above */
Amaury Pouly86e745a2017-01-05 12:06:12 +0100372 int xfer_size = *size + 4;
Amaury Pouly76c73c72017-01-07 16:31:28 +0100373 uint8_t *xfer_buf = malloc(xfer_size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100374 int ret = do_dnk_cmd(true, 0x23, 10, node_index, xfer_buf, &xfer_size);
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100375 if(ret)
376 return ret;
Amaury Pouly33856d92016-11-11 16:04:15 +0100377 if(xfer_size <= 4)
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100378 {
Amaury Pouly33856d92016-11-11 16:04:15 +0100379 free(xfer_buf);
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100380 cprintf(GREY, "Device didn't send any data\n");
Amaury Pouly33856d92016-11-11 16:04:15 +0100381 return 6;
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100382 }
Amaury Pouly33856d92016-11-11 16:04:15 +0100383 if(get_big_endian16(xfer_buf + 2) != node_index)
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100384 {
Amaury Pouly33856d92016-11-11 16:04:15 +0100385 free(xfer_buf);
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100386 cprintf(GREY, "Device responded with invalid data\n");
387 return 1;
388 }
Amaury Pouly86e745a2017-01-05 12:06:12 +0100389 *size = xfer_size - 4;
Amaury Pouly33856d92016-11-11 16:04:15 +0100390 /* unscramble and copy */
391 for(int i = 4, idx = get_big_endian16(xfer_buf); i < xfer_size; i++, idx++)
392 xfer_buf[i] ^= para_noise[idx % sizeof(para_noise)];
Amaury Pouly86e745a2017-01-05 12:06:12 +0100393 memcpy(buffer, xfer_buf + 4, *size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100394 free(xfer_buf);
395 return 0;
396}
397
398/* read nvp node, retrun nonzero on error */
Amaury Pouly1895af82017-01-08 16:07:18 +0100399int write_nvp_node(int node_index, void *buffer, int size)
Amaury Pouly33856d92016-11-11 16:04:15 +0100400{
Amaury Pouly33856d92016-11-11 16:04:15 +0100401 /* the data buffer is prepended with a 4 byte header:
402 * - byte 0/1 is the para_noise index, written as a 16bit big-endian number
403 * - byte 2/3 is the node index, written as a 16-bit big-endian number */
404 int xfer_size = size + 4;
Amaury Pouly76c73c72017-01-07 16:31:28 +0100405 uint8_t *xfer_buf = malloc(xfer_size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100406 /* scramble, always use index 0 for para_noise */
407 set_big_endian16(xfer_buf, 0); /* para_noise index */
408 set_big_endian16(xfer_buf + 2, node_index); /* node index */
409 memcpy(xfer_buf + 4, buffer, size);
410 for(int i = 4, idx = get_big_endian16(xfer_buf); i < xfer_size; i++, idx++)
411 xfer_buf[i] ^= para_noise[idx % sizeof(para_noise)];
412 int ret = do_dnk_cmd(false, 0x24, 10, node_index, xfer_buf, &xfer_size);
413 if(ret)
414 return ret;
415 if(xfer_size - 4 != (int)size)
416 {
417 free(xfer_buf);
418 cprintf(GREY, "Wrong transger size\n");
419 return 7;
420 }
421 free(xfer_buf);
422 return 0;
423}
424
425int get_dnk_nvp(int argc, char **argv)
426{
Amaury Pouly1895af82017-01-08 16:07:18 +0100427 if(argc != 1 && argc != 2)
Amaury Pouly33856d92016-11-11 16:04:15 +0100428 {
429 printf("You must specify a known nvp node or a full node specification:\n");
430 printf("Node usage: <node>\n");
Amaury Pouly1895af82017-01-08 16:07:18 +0100431 printf("Node usage: <node> <size>\n");
Amaury Pouly33856d92016-11-11 16:04:15 +0100432 printf("Nodes:\n");
433 for(unsigned i = 0; i < NWZ_NVP_COUNT; i++)
Amaury Pouly86e745a2017-01-05 12:06:12 +0100434 printf(" %-6s%s\n", nwz_nvp[i].name, nwz_nvp[i].desc);
Amaury Pouly1895af82017-01-08 16:07:18 +0100435 printf("You can also specify a decimal or hexadecimal value directly\n");
Amaury Pouly33856d92016-11-11 16:04:15 +0100436 return 1;
437 }
438 int series_index, model_index;
439 int ret = get_model_and_series(&model_index, &series_index);
440 if(ret)
441 return ret;
Amaury Pouly1895af82017-01-08 16:07:18 +0100442 size_t size = 0;
443 /* maybe user specified an explicit size */
444 if(argc == 2)
Amaury Pouly33856d92016-11-11 16:04:15 +0100445 {
Amaury Pouly1895af82017-01-08 16:07:18 +0100446 char *end;
447 size = strtoul(argv[1], &end, 0);
448 if(*end)
449 {
450 printf("Invalid user-specified size '%s'\n", argv[1]);
451 return 5;
452 }
453 }
454 /* find entry in NVP */
455 const char *node_name = argv[0];
456 const char *node_desc = NULL;
457 int node_index = NWZ_NVP_INVALID;
458 for(int i = 0; i < NWZ_NVP_COUNT; i++)
459 if(strcmp(nwz_nvp[i].name, node_name) == 0)
460 {
461 if(nwz_series[series_index].nvp_index)
462 node_index = (*nwz_series[series_index].nvp_index)[i];
463 if(node_index == NWZ_NVP_INVALID)
464 {
465 printf("This device doesn't have node '%s'\n", node_name);
466 return 5;
467 }
468 node_desc = nwz_nvp[i].desc;
469 /* if not overriden, try to get size from database */
470 if(size == 0)
471 size = nwz_nvp[i].size;
472 }
473 /* if we can't find it, maybe check if it's a number */
474 if(node_index == NWZ_NVP_INVALID)
475 {
476 char *end;
477 node_index = strtol(node_name, &end, 0);
478 if(*end)
479 node_index = NWZ_NVP_INVALID; /* string is not a number */
480 }
481 if(node_index == NWZ_NVP_INVALID)
482 {
483 printf("I don't know about node '%s'\n", node_name);
Amaury Pouly33856d92016-11-11 16:04:15 +0100484 return 4;
485 }
Amaury Pouly1895af82017-01-08 16:07:18 +0100486 /* if we don't have a size, take a big size to be sure */
487 if(size == 0)
488 {
Amaury Pouly86e745a2017-01-05 12:06:12 +0100489 size = 4096;
Amaury Pouly1895af82017-01-08 16:07:18 +0100490 printf("Note: node size unknown, trying to read %u bytes\n", (unsigned)size);
491 }
492 if(g_debug)
493 printf("Asking device for %u bytes\n", (unsigned)size);
494 /* take the size in the database as a hint of the size, but the device could
495 * return less data */
Amaury Pouly86e745a2017-01-05 12:06:12 +0100496 uint8_t *buffer = malloc(size);
Amaury Pouly1895af82017-01-08 16:07:18 +0100497 ret = read_nvp_node(node_index, buffer, &size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100498 if(ret != 0)
499 {
500 free(buffer);
501 return ret;
502 }
Amaury Pouly1895af82017-01-08 16:07:18 +0100503 cprintf(GREEN, "%s (node %d%s%s):\n", node_name, node_index,
504 node_desc ? "," : "", node_desc ? node_desc : "");
Amaury Pouly86e745a2017-01-05 12:06:12 +0100505 print_hex(buffer, size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100506
507 free(buffer);
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100508 return 0;
509}
510
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100511struct dpcc_prop_t
512{
513 char *user_name;
514 char name[7];
515 uint8_t cdb1;
516 int size;
517};
518
519struct dpcc_prop_t dpcc_prop_list[] =
520{
521 { "dev_info", "DEVINFO", 0, 0x80 },
Amaury Pouly33856d92016-11-11 16:04:15 +0100522 /* there are more but they are very obscure */
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100523};
524
525#define NR_DPCC_PROPS (sizeof(dpcc_prop_list) / sizeof(dpcc_prop_list[0]))
526
527int do_dpcc_cmd(uint32_t cmd, struct dpcc_prop_t *prop, void *buffer, int *buffer_size)
528{
529 uint8_t cdb[12] = {0xfb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
530 cdb[2] = cmd;
531 if(cmd == 0)
532 {
533 strncpy((char *)(cdb + 3), prop->name, 7); // warning: erase cdb[10] !
534 cdb[1] = prop->cdb1;
535 if(prop->cdb1 & 1)
536 cdb[10] = (*buffer_size + 15) / 16;
537 else
538 cdb[10] = *buffer_size;
539 }
540
541 uint8_t sense[32];
542 int sense_size = 32;
543
544 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, buffer_size);
545 if(ret < 0)
546 return ret;
547 ret = do_sense_analysis(ret, sense, sense_size);
548 if(ret)
549 return ret;
550 return 0;
551}
552
553int get_dpcc_prop(int argc, char **argv)
554{
555 if(argc != 1 && argc != 3)
556 {
557 printf("You must specify a known property name or a full property specification:\n");
558 printf("Full usage: <prop code> <large> <size>\n");
559 printf("Property usage: <prop>\n");
560 printf("Properties:");
561 for(unsigned i = 0; i < NR_DPCC_PROPS; i++)
562 printf(" %s", dpcc_prop_list[i].user_name);
563 printf("\n");
564 return 1;
565 }
566
567 struct dpcc_prop_t prop;
568 memset(&prop, 0, sizeof(prop));
569 if(argc == 1)
570 {
571 for(unsigned i = 0; i < NR_DPCC_PROPS; i++)
572 if(strcmp(dpcc_prop_list[i].user_name, argv[0]) == 0)
573 prop = dpcc_prop_list[i];
574 if(prop.user_name[0] == 0)
575 {
576 cprintf(GREY, "Unknown property '%s'\n", argv[0]);
577 return 1;
578 }
579 }
580 else
581 {
582 strncpy(prop.name, argv[0], 7);
583 prop.cdb1 = strtoul(argv[1], NULL, 0);
584 prop.size = strtoul(argv[2], NULL, 0);
585 }
586
Amaury Pouly76c73c72017-01-07 16:31:28 +0100587 char *buffer = malloc(prop.size);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100588 int buffer_size = prop.size;
589 int ret = do_dpcc_cmd(0, &prop, buffer, &buffer_size);
590 if(ret)
591 return ret;
592 if(buffer_size < prop.size)
593 buffer[buffer_size] = 0;
594 cprintf_field("Property: ", "%s\n", buffer);
595 return 0;
596}
597
598struct user_timer_t
599{
600 uint16_t magic;
601 uint8_t res[6];
602 uint8_t year[2]; // bcd
603 uint8_t month; // bcd
604 uint8_t day; // bcd
605 uint8_t hour; // bcd
606 uint8_t min; // bcd
607 uint8_t sec; // bcd
608 uint8_t res2[17];
609} __attribute__((packed));
610
611int get_user_time(int argc, char **argv)
612{
613 (void) argc;
614 (void )argv;
615
Amaury Pouly76c73c72017-01-07 16:31:28 +0100616 void *buffer = malloc(32);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100617 int buffer_size = 32;
618 int ret = do_dpcc_cmd(1, NULL, buffer, &buffer_size);
619 if(ret)
620 return ret;
621 struct user_timer_t *time = buffer;
622 cprintf_field("User Time: ", "%02x/%02x/%02x%02x %02x:%02x:%02x\n",
623 time->day, time->month, time->year[0], time->year[1], time->hour,
624 time->min, time->sec);
625 return 0;
626}
627
628int get_dev_info(int argc, char **argv)
629{
630 (void) argc;
631 (void )argv;
632 uint8_t cdb[12] = {0xfc, 0, 0x20, 'd', 'b', 'm', 'n', 0, 0x80, 0, 0, 0};
633
Amaury Pouly76c73c72017-01-07 16:31:28 +0100634 char *buffer = malloc(0x81);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100635 int buffer_size = 0x80;
636 uint8_t sense[32];
637 int sense_size = 32;
638
639 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, &buffer_size);
640 if(ret < 0)
641 return ret;
642 ret = do_sense_analysis(ret, sense, sense_size);
643 if(ret)
644 return ret;
645 buffer[buffer_size] = 0;
646 cprintf_field("Device Info:", "\n");
647 print_hex(buffer, buffer_size);
648 return 0;
649}
650
Amaury Pouly02f67222012-11-12 18:34:22 +0100651int do_fw_upgrade(int argc, char **argv)
652{
653 (void) argc;
654 (void )argv;
Amaury Pouly33856d92016-11-11 16:04:15 +0100655 /* older devices may have used subcommand 3 instead of 4, but this is not
656 * supported by any device I have seen */
Amaury Pouly02f67222012-11-12 18:34:22 +0100657 uint8_t cdb[12] = {0xfc, 0, 0x04, 'd', 'b', 'm', 'n', 0, 0x80, 0, 0, 0};
658
Amaury Pouly76c73c72017-01-07 16:31:28 +0100659 char *buffer = malloc(0x81);
Amaury Pouly02f67222012-11-12 18:34:22 +0100660 int buffer_size = 0x80;
661 uint8_t sense[32];
662 int sense_size = 32;
663
664 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, &buffer_size);
665 if(ret < 0)
666 return ret;
667 ret = do_sense_analysis(ret, sense, sense_size);
668 if(ret)
669 return ret;
670 buffer[buffer_size] = 0;
671 cprintf_field("Result:", "\n");
672 print_hex(buffer, buffer_size);
673 return 0;
674}
675
Amaury Pouly33856d92016-11-11 16:04:15 +0100676static struct
677{
678 unsigned long dest;
679 const char *name;
680} g_dest_list[] =
681{
682 { 0, "J" },
683 { 1, "U" },
684 { 0x101, "U2" },
685 { 0x201, "U3" },
686 { 0x301, "CA" },
687 { 2, "CEV" },
688 { 0x102, "CE7" },
689 { 3, "CEW" },
690 { 0x103, "CEW2" },
691 { 4, "CN" },
692 { 5, "KR" },
693 { 6, "E" },
694 { 0x106, "MX" },
695 { 0x206, "E2" },
696 { 0x306, "MX3" },
697 { 7, "TW" },
698};
699
700#define DEST_COUNT (sizeof(g_dest_list) / sizeof(g_dest_list[0]))
701
702int do_dest(int argc, char **argv)
703{
704 /* it is possile to write any NVP node using the SCSI interface but only
705 * give the user the possibility to write destination, because that's the
706 * most useful one */
707 if(argc != 1 && argc != 3)
708 {
709 printf("Usage: get\n");
710 printf("Usage: set <dest> <sps>\n");
711 printf("Destination (<dest>) can be either an integer or one of:\n");
712 for(size_t i = 0; i < DEST_COUNT; i++)
713 printf(" %s\n", g_dest_list[i].name);
714 printf("Sound pressure (<sps>) can be be an integer, 'on' or 'off'\n");
715 return 1;
716 }
717 /* get model/series */
718 int model_index, series_index;
719 int ret = get_model_and_series(&model_index, &series_index);
Amaury Pouly1895af82017-01-08 16:07:18 +0100720 int shp_index = NWZ_NVP_INVALID;
721 if(nwz_series[series_index].nvp_index)
722 shp_index = (*nwz_series[series_index].nvp_index)[NWZ_NVP_SHP];
723 if(shp_index == NWZ_NVP_INVALID)
724 {
725 printf("This device doesn't have node 'shp'\n");
726 return 5;
727 }
Amaury Pouly33856d92016-11-11 16:04:15 +0100728 /* in all cases, we need to read shp */
Amaury Pouly86e745a2017-01-05 12:06:12 +0100729 size_t size = nwz_nvp[NWZ_NVP_SHP].size;
730 uint8_t *shp = malloc(size);
Amaury Pouly1895af82017-01-08 16:07:18 +0100731 ret = read_nvp_node(shp_index, shp, &size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100732 if(ret != 0)
733 {
734 free(shp);
735 return ret;
736 }
737 /* get */
738 if(strcmp(argv[0], "get") == 0)
739 {
740 if(argc != 1)
741 {
742 printf("Too many arguments for get\n");
743 free(shp);
744 return 2;
745 }
746 const char *dst_name = "Unknown";
747 unsigned long dst = get_little_endian32(shp);
748 for(size_t i = 0; i < DEST_COUNT; i++)
749 if(dst == g_dest_list[i].dest)
750 dst_name = g_dest_list[i].name;
751 printf("Destination: %s (%lx)\n", dst_name, dst);
752 unsigned long sps = get_little_endian32(shp + 4);
753 printf("Sound pressure: %lu (%s)\n", sps, sps == 0 ? "off" : "on");
754 free(shp);
755 }
756 /* set */
757 if(strcmp(argv[0], "set") == 0)
758 {
759 if(argc != 3)
760 {
761 printf("Not enough arguments for set\n");
762 free(shp);
763 return 2;
764 }
765 /* try to parse dest as integer */
766 char *end;
767 unsigned long dst = strtoul(argv[1], &end, 0);
768 if(*end)
769 {
770 /* assume string */
771 int index = -1;
772 for(size_t i = 0; i < DEST_COUNT; i++)
773 if(strcmp(argv[1], g_dest_list[i].name) == 0)
774 index = i;
775 if(index == -1)
776 {
777 printf("Unknown destination '%s'\n", argv[1]);
778 free(shp);
779 return 3;
780 }
781 dst = g_dest_list[index].dest;
782 }
783 /* try to parse sps as integer */
784 /* try to parse dest as integer */
785 unsigned long sps = strtoul(argv[2], &end, 0);
786 if(*end)
787 {
788 /* assume string */
789 if(strcmp(argv[2], "on") == 0)
790 sps = 1;
791 else if(strcmp(argv[2], "off") == 0)
792 sps = 0;
793 else
794 {
795 printf("Unknown sound pressure setting '%s'\n", argv[2]);
796 free(shp);
797 return 3;
798 }
799 }
800 set_little_endian32(shp, dst);
801 set_little_endian32(shp + 4, sps);
Amaury Pouly1895af82017-01-08 16:07:18 +0100802 int ret = write_nvp_node(shp_index, shp, size);
Amaury Pouly33856d92016-11-11 16:04:15 +0100803 free(shp);
Amaury Pouly1d7f6042017-01-09 22:15:53 +0100804 if(ret != 0)
805 printf("An error occured when writing node: %d\n", ret);
806 else
807 printf("Destination successfully changed.\nPlease RESET ALL SETTINGS on your device!\n");
Amaury Pouly33856d92016-11-11 16:04:15 +0100808 return ret;
809 }
810 return 0;
811}
812
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100813typedef int (*cmd_fn_t)(int argc, char **argv);
814
815struct cmd_t
816{
817 const char *name;
818 const char *desc;
819 cmd_fn_t fn;
820};
821
822struct cmd_t cmd_list[] =
823{
824 { "get_dnk_prop", "Get DNK property", get_dnk_prop },
Amaury Pouly6d04ed32012-11-03 04:21:54 +0100825 { "get_dnk_nvp", "Get DNK NVP content", get_dnk_nvp },
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100826 { "get_dpcc_prop", "Get DPCC property", get_dpcc_prop },
827 { "get_user_time", "Get user time", get_user_time },
828 { "get_dev_info", "Get device info", get_dev_info },
Amaury Pouly02f67222012-11-12 18:34:22 +0100829 { "do_fw_upgrade", "Do a firmware upgrade", do_fw_upgrade },
Amaury Pouly33856d92016-11-11 16:04:15 +0100830 { "dest_tool", "Get/Set destination and sound pressure regulation", do_dest },
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100831};
832
833#define NR_CMDS (sizeof(cmd_list) / sizeof(cmd_list[0]))
834
835int process_cmd(const char *cmd, int argc, char **argv)
836{
837 for(unsigned i = 0; i < NR_CMDS; i++)
838 if(strcmp(cmd_list[i].name, cmd) == 0)
839 return cmd_list[i].fn(argc, argv);
840 cprintf(GREY, "Unknown command '%s'\n", cmd);
841 return 1;
842}
843
844static void usage(void)
845{
Amaury Poulyfb05b3e2012-12-11 20:15:25 +0100846 printf("Usage: scsitool [options] <dev> <command> [arguments]\n");
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100847 printf("Options:\n");
Amaury Poulyad2297d2017-01-04 17:10:48 +0100848 printf(" -o <prefix> Set output prefix\n");
849 printf(" -?/--help Display this message\n");
850 printf(" -d/--debug Display debug messages\n");
851 printf(" -c/--no-color Disable color output\n");
852 printf(" -s/--series <name> Force series (disable auto-detection, use '?' for the list)\n");
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100853 printf("Commands:\n");
854 for(unsigned i = 0; i < NR_CMDS; i++)
855 printf(" %s\t%s\n", cmd_list[i].name, cmd_list[i].desc);
856 exit(1);
857}
858
859int main(int argc, char **argv)
860{
861 while(1)
862 {
863 static struct option long_options[] =
864 {
865 {"help", no_argument, 0, '?'},
866 {"debug", no_argument, 0, 'd'},
867 {"no-color", no_argument, 0, 'c'},
Amaury Poulyad2297d2017-01-04 17:10:48 +0100868 {"series", required_argument, 0, 's'},
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100869 {0, 0, 0, 0}
870 };
871
Amaury Pouly1895af82017-01-08 16:07:18 +0100872 int c = getopt_long(argc, argv, "?dcfo:s:", long_options, NULL);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100873 if(c == -1)
874 break;
875 switch(c)
876 {
877 case -1:
878 break;
879 case 'c':
880 enable_color(false);
881 break;
882 case 'd':
883 g_debug = true;
884 break;
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100885 case '?':
886 usage();
887 break;
888 case 'o':
889 g_out_prefix = optarg;
890 break;
Amaury Poulyad2297d2017-01-04 17:10:48 +0100891 case 's':
892 g_force_series = optarg;
893 break;
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100894 default:
895 abort();
896 }
897 }
898
899 if(argc - optind < 2)
900 {
901 usage();
902 return 1;
903 }
904
905 int ret = 0;
Amaury Pouly76c73c72017-01-07 16:31:28 +0100906 int flags = 0;
907 if(g_debug)
908 flags |= RB_SCSI_DEBUG;
909 g_dev = rb_scsi_open(argv[optind], flags, NULL, NULL);
910 if(g_dev == 0)
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100911 {
Amaury Pouly76c73c72017-01-07 16:31:28 +0100912 cprintf(GREY, "Cannot open device\n");
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100913 ret = 1;
914 goto Lend;
915 }
916
917 ret = process_cmd(argv[optind + 1], argc - optind - 2, argv + optind + 2);
918
Amaury Pouly76c73c72017-01-07 16:31:28 +0100919 rb_scsi_close(g_dev);
Amaury Pouly3e1c4922012-11-03 02:29:00 +0100920Lend:
921 color(OFF);
922
923 return ret;
924}
925