Introduce scsitool for sony nwz players
[maemo-rb.git] / utils / nwztools / scsitools / scsitool.c
blob6848fb90de9a7d4dd541391d1454db78e7593bc3
1 /***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
10 * Copyright (C) 2012 Amaury Pouly
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.
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
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>
34 #include <scsi/scsi.h>
35 #include <scsi/sg_lib.h>
36 #include <scsi/sg_pt.h>
37 #include "misc.h"
39 bool g_debug = false;
40 bool g_force = false;
41 char *g_out_prefix = NULL;
42 int g_dev_fd = 0;
44 #define let_the_force_flow(x) do { if(!g_force) return x; } while(0)
45 #define continue_the_force(x) if(x) let_the_force_flow(x)
47 #define check_field(v_exp, v_have, str_ok, str_bad) \
48 if((v_exp) != (v_have)) \
49 { cprintf(RED, str_bad); let_the_force_flow(__LINE__); } \
50 else { cprintf(RED, str_ok); }
52 #define errorf(...) do { cprintf(GREY, __VA_ARGS__); return __LINE__; } while(0)
54 #if 0
55 void *buffer_alloc(int sz)
57 #ifdef SG_LIB_MINGW
58 unsigned psz = getpagesize();
59 #else
60 unsigned psz = sysconf(_SC_PAGESIZE); /* was getpagesize() */
61 #endif
62 void *buffer = malloc(sz + psz);
63 return (void *)(((ptrdiff_t)(buffer + psz - 1)) & ~(psz - 1));
65 #else
66 void *buffer_alloc(int sz)
68 return malloc(sz);
70 #endif
72 static void print_hex(void *_buffer, int buffer_size)
74 uint8_t *buffer = _buffer;
75 for(int i = 0; i < buffer_size; i += 16)
77 for(int j = 0; j < 16; j++)
79 if(i + j < buffer_size)
80 cprintf(YELLOW, " %02x", buffer[i + j]);
81 else
82 cprintf(YELLOW, " ");
84 printf(" ");
85 for(int j = 0; j < 16; j++)
87 if(i + j < buffer_size)
88 cprintf(RED, "%c", isprint(buffer[i + j]) ? buffer[i + j] : '.');
89 else
90 cprintf(RED, " ");
92 printf("\n");
96 /* Do read */
97 #define DO_READ (1 << 1)
98 /* Do write */
99 #define DO_WRITE (1 << 2)
101 /* returns <0 on error and status otherwise */
102 int do_scsi(uint8_t *cdb, int cdb_size, unsigned flags, void *sense, int *sense_size, void *buffer, int *buf_size)
104 char error[256];
105 struct sg_pt_base *obj = construct_scsi_pt_obj();
106 if(obj == NULL)
108 cprintf(GREY, "construct_scsi_pt_obj failed\n");
109 return 1;
111 set_scsi_pt_cdb(obj, cdb, cdb_size);
112 if(sense)
113 set_scsi_pt_sense(obj, sense, *sense_size);
114 if(flags & DO_READ)
115 set_scsi_pt_data_in(obj, buffer, *buf_size);
116 if(flags & DO_WRITE)
117 set_scsi_pt_data_out(obj, buffer, *buf_size);
118 int ret = do_scsi_pt(obj, g_dev_fd, 1, 0);
119 switch(get_scsi_pt_result_category(obj))
121 case SCSI_PT_RESULT_SENSE:
122 case SCSI_PT_RESULT_GOOD:
123 ret = get_scsi_pt_status_response(obj);
124 break;
125 case SCSI_PT_RESULT_STATUS:
126 cprintf(GREY, "Status error: %d (", get_scsi_pt_status_response(obj));
127 sg_print_scsi_status(get_scsi_pt_status_response(obj));
128 printf(")\n");
129 break;
130 case SCSI_PT_RESULT_TRANSPORT_ERR:
131 cprintf(GREY, "Transport error: %s\n", get_scsi_pt_transport_err_str(obj, 256, error));
132 ret = -2;
133 break;
134 case SCSI_PT_RESULT_OS_ERR:
135 cprintf(GREY, "OS error: %s\n", get_scsi_pt_os_err_str(obj, 256, error));
136 ret = -3;
137 break;
138 default:
139 cprintf(GREY, "Unknown error\n");
140 break;
143 if(sense)
144 *sense_size = get_scsi_pt_sense_len(obj);
145 if(flags & (DO_WRITE | DO_READ))
146 *buf_size -= get_scsi_pt_resid(obj);
148 destruct_scsi_pt_obj(obj);
149 return ret;
152 int do_sense_analysis(int status, uint8_t *sense, int sense_size)
154 cprintf_field("Status:", " "); fflush(stdout);
155 sg_print_scsi_status(status);
156 cprintf_field("\nSense:", " "); fflush(stdout);
157 sg_print_sense(NULL, sense, sense_size, 0);
158 if(status == GOOD)
159 return 0;
160 return status;
163 int do_dnk_cmd(uint32_t cmd, uint8_t sub_cmd, void *buffer, int *buffer_size)
165 uint8_t cdb[12] = {0xdd, 0, 0, 0, 0, 0, 0, 0xbc, 0, 0, 0, 0};
166 cdb[10] = cmd;
167 cdb[11] = sub_cmd;
168 cdb[8] = (*buffer_size) >> 8;
169 cdb[9] = (*buffer_size) & 0xff;
171 uint8_t sense[32];
172 int sense_size = 32;
174 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, buffer_size);
175 if(ret < 0)
176 return ret;
177 ret = do_sense_analysis(ret, sense, sense_size);
178 if(ret)
179 return ret;
180 return 0;
183 #define DNK_EXACT_LENGTH (1 << 0)
184 #define DNK_STRING (1 << 1)
185 #define DNK_UINT32 (1 << 2)
187 struct dnk_prop_t
189 char *name;
190 uint8_t cmd;
191 uint8_t subcmd;
192 int size;
193 unsigned flags;
196 struct dnk_prop_t dnk_prop_list[] =
198 { "serial_num", 0x23, 1, 8, DNK_STRING},
199 { "model_id", 0x23, 4, 4, DNK_EXACT_LENGTH | DNK_UINT32},
200 { "product_id", 0x23, 6, 12, DNK_STRING},
201 { "destination", 0x23, 8, 4, DNK_EXACT_LENGTH | DNK_UINT32},
202 { "model_id2", 0x23, 9, 4, DNK_EXACT_LENGTH | DNK_UINT32},
203 { "dev_info", 0x12, 0, 64, DNK_STRING},
206 #define NR_DNK_PROPS (sizeof(dnk_prop_list) / sizeof(dnk_prop_list[0]))
208 int get_dnk_prop(int argc, char **argv)
210 if(argc != 1 && argc != 4)
212 printf("You must specify a known property name or a full property specification:\n");
213 printf("Full usage: <cmd> <subcmd> <size> <flags>\n");
214 printf("Property usage: <prop>\n");
215 printf("Properties:");
216 for(unsigned i = 0; i < NR_DNK_PROPS; i++)
217 printf(" %s", dnk_prop_list[i].name);
218 printf("\n");
219 return 1;
222 struct dnk_prop_t prop;
223 memset(&prop, 0, sizeof(prop));
224 if(argc == 1)
226 for(unsigned i = 0; i < NR_DNK_PROPS; i++)
227 if(strcmp(dnk_prop_list[i].name, argv[0]) == 0)
228 prop = dnk_prop_list[i];
229 if(prop.name == NULL)
231 cprintf(GREY, "Unknown property '%s'\n", argv[0]);
232 return 1;
235 else
237 prop.cmd = strtoul(argv[0], NULL, 0);
238 prop.subcmd = strtoul(argv[1], NULL, 0);
239 prop.size = strtoul(argv[2], NULL, 0);
240 prop.flags = strtoul(argv[3], NULL, 0);
243 char *buffer = buffer_alloc(prop.size + 1);
244 int buffer_size = prop.size;
245 int ret = do_dnk_cmd(prop.cmd, prop.subcmd, buffer, &buffer_size);
246 if(ret)
247 return ret;
248 if(buffer_size == 0)
250 cprintf(GREY, "Device didn't send any data\n");
251 return 1;
253 if((prop.flags & DNK_EXACT_LENGTH) && buffer_size != prop.size)
255 cprintf(GREY, "Device didn't send the expected amount of data\n");
256 return 2;
258 buffer[buffer_size] = 0;
259 if(prop.flags & DNK_STRING)
260 cprintf_field("Property: ", "%s\n", buffer);
261 else if(prop.flags & DNK_UINT32)
262 cprintf_field("Property: ", "0x%x\n", *(uint32_t *)buffer);
263 else
265 cprintf(GREEN, "Property:\n");
266 print_hex(buffer, buffer_size);
268 return 0;
271 struct dpcc_prop_t
273 char *user_name;
274 char name[7];
275 uint8_t cdb1;
276 int size;
279 struct dpcc_prop_t dpcc_prop_list[] =
281 { "dev_info", "DEVINFO", 0, 0x80 },
284 #define NR_DPCC_PROPS (sizeof(dpcc_prop_list) / sizeof(dpcc_prop_list[0]))
286 int do_dpcc_cmd(uint32_t cmd, struct dpcc_prop_t *prop, void *buffer, int *buffer_size)
288 uint8_t cdb[12] = {0xfb, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
289 cdb[2] = cmd;
290 if(cmd == 0)
292 strncpy((char *)(cdb + 3), prop->name, 7); // warning: erase cdb[10] !
293 cdb[1] = prop->cdb1;
294 if(prop->cdb1 & 1)
295 cdb[10] = (*buffer_size + 15) / 16;
296 else
297 cdb[10] = *buffer_size;
300 uint8_t sense[32];
301 int sense_size = 32;
303 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, buffer_size);
304 if(ret < 0)
305 return ret;
306 ret = do_sense_analysis(ret, sense, sense_size);
307 if(ret)
308 return ret;
309 return 0;
312 int get_dpcc_prop(int argc, char **argv)
314 if(argc != 1 && argc != 3)
316 printf("You must specify a known property name or a full property specification:\n");
317 printf("Full usage: <prop code> <large> <size>\n");
318 printf("Property usage: <prop>\n");
319 printf("Properties:");
320 for(unsigned i = 0; i < NR_DPCC_PROPS; i++)
321 printf(" %s", dpcc_prop_list[i].user_name);
322 printf("\n");
323 return 1;
326 struct dpcc_prop_t prop;
327 memset(&prop, 0, sizeof(prop));
328 if(argc == 1)
330 for(unsigned i = 0; i < NR_DPCC_PROPS; i++)
331 if(strcmp(dpcc_prop_list[i].user_name, argv[0]) == 0)
332 prop = dpcc_prop_list[i];
333 if(prop.user_name[0] == 0)
335 cprintf(GREY, "Unknown property '%s'\n", argv[0]);
336 return 1;
339 else
341 strncpy(prop.name, argv[0], 7);
342 prop.cdb1 = strtoul(argv[1], NULL, 0);
343 prop.size = strtoul(argv[2], NULL, 0);
346 char *buffer = buffer_alloc(prop.size);
347 int buffer_size = prop.size;
348 int ret = do_dpcc_cmd(0, &prop, buffer, &buffer_size);
349 if(ret)
350 return ret;
351 if(buffer_size < prop.size)
352 buffer[buffer_size] = 0;
353 cprintf_field("Property: ", "%s\n", buffer);
354 return 0;
357 struct user_timer_t
359 uint16_t magic;
360 uint8_t res[6];
361 uint8_t year[2]; // bcd
362 uint8_t month; // bcd
363 uint8_t day; // bcd
364 uint8_t hour; // bcd
365 uint8_t min; // bcd
366 uint8_t sec; // bcd
367 uint8_t res2[17];
368 } __attribute__((packed));
370 int get_user_time(int argc, char **argv)
372 (void) argc;
373 (void )argv;
375 void *buffer = buffer_alloc(32);
376 int buffer_size = 32;
377 int ret = do_dpcc_cmd(1, NULL, buffer, &buffer_size);
378 if(ret)
379 return ret;
380 struct user_timer_t *time = buffer;
381 cprintf_field("User Time: ", "%02x/%02x/%02x%02x %02x:%02x:%02x\n",
382 time->day, time->month, time->year[0], time->year[1], time->hour,
383 time->min, time->sec);
384 return 0;
387 int get_dev_info(int argc, char **argv)
389 (void) argc;
390 (void )argv;
391 uint8_t cdb[12] = {0xfc, 0, 0x20, 'd', 'b', 'm', 'n', 0, 0x80, 0, 0, 0};
393 char *buffer = buffer_alloc(0x81);
394 int buffer_size = 0x80;
395 uint8_t sense[32];
396 int sense_size = 32;
398 int ret = do_scsi(cdb, 12, DO_READ, sense, &sense_size, buffer, &buffer_size);
399 if(ret < 0)
400 return ret;
401 ret = do_sense_analysis(ret, sense, sense_size);
402 if(ret)
403 return ret;
404 buffer[buffer_size] = 0;
405 cprintf_field("Device Info:", "\n");
406 print_hex(buffer, buffer_size);
407 return 0;
410 typedef int (*cmd_fn_t)(int argc, char **argv);
412 struct cmd_t
414 const char *name;
415 const char *desc;
416 cmd_fn_t fn;
419 struct cmd_t cmd_list[] =
421 { "get_dnk_prop", "Get DNK property", get_dnk_prop },
422 { "get_dpcc_prop", "Get DPCC property", get_dpcc_prop },
423 { "get_user_time", "Get user time", get_user_time },
424 { "get_dev_info", "Get device info", get_dev_info },
427 #define NR_CMDS (sizeof(cmd_list) / sizeof(cmd_list[0]))
429 int process_cmd(const char *cmd, int argc, char **argv)
431 for(unsigned i = 0; i < NR_CMDS; i++)
432 if(strcmp(cmd_list[i].name, cmd) == 0)
433 return cmd_list[i].fn(argc, argv);
434 cprintf(GREY, "Unknown command '%s'\n", cmd);
435 return 1;
438 static void usage(void)
440 printf("Usage: emmctool [options] <dev> <command> [arguments]\n");
441 printf("Options:\n");
442 printf(" -o <prefix>\tSet output prefix\n");
443 printf(" -f/--force\tForce to continue on errors\n");
444 printf(" -?/--help\tDisplay this message\n");
445 printf(" -d/--debug\tDisplay debug messages\n");
446 printf(" -c/--no-color\tDisable color output\n");
447 printf("Commands:\n");
448 for(unsigned i = 0; i < NR_CMDS; i++)
449 printf(" %s\t%s\n", cmd_list[i].name, cmd_list[i].desc);
450 exit(1);
453 int main(int argc, char **argv)
455 while(1)
457 static struct option long_options[] =
459 {"help", no_argument, 0, '?'},
460 {"debug", no_argument, 0, 'd'},
461 {"no-color", no_argument, 0, 'c'},
462 {"force", no_argument, 0, 'f'},
463 {0, 0, 0, 0}
466 int c = getopt_long(argc, argv, "?dcfo:", long_options, NULL);
467 if(c == -1)
468 break;
469 switch(c)
471 case -1:
472 break;
473 case 'c':
474 enable_color(false);
475 break;
476 case 'd':
477 g_debug = true;
478 break;
479 case 'f':
480 g_force = true;
481 break;
482 case '?':
483 usage();
484 break;
485 case 'o':
486 g_out_prefix = optarg;
487 break;
488 default:
489 abort();
493 if(argc - optind < 2)
495 usage();
496 return 1;
499 int ret = 0;
500 g_dev_fd = scsi_pt_open_device(argv[optind], false, true);
501 if(g_dev_fd < 0)
503 cprintf(GREY, "Cannot open device: %m\n");
504 ret = 1;
505 goto Lend;
508 ret = process_cmd(argv[optind + 1], argc - optind - 2, argv + optind + 2);
510 scsi_pt_close_device(g_dev_fd);
511 Lend:
512 color(OFF);
514 return ret;