1 /***************************************************************************
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
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 ****************************************************************************/
30 #include <sys/types.h>
34 #include <scsi/scsi.h>
35 #include <scsi/sg_lib.h>
36 #include <scsi/sg_pt.h>
41 char *g_out_prefix
= NULL
;
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)
55 void *buffer_alloc(int sz
)
58 unsigned psz
= getpagesize();
60 unsigned psz
= sysconf(_SC_PAGESIZE
); /* was getpagesize() */
62 void *buffer
= malloc(sz
+ psz
);
63 return (void *)(((ptrdiff_t)(buffer
+ psz
- 1)) & ~(psz
- 1));
66 void *buffer_alloc(int sz
)
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
]);
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
] : '.');
97 #define DO_READ (1 << 1)
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
)
105 struct sg_pt_base
*obj
= construct_scsi_pt_obj();
108 cprintf(GREY
, "construct_scsi_pt_obj failed\n");
111 set_scsi_pt_cdb(obj
, cdb
, cdb_size
);
113 set_scsi_pt_sense(obj
, sense
, *sense_size
);
115 set_scsi_pt_data_in(obj
, buffer
, *buf_size
);
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
);
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
));
130 case SCSI_PT_RESULT_TRANSPORT_ERR
:
131 cprintf(GREY
, "Transport error: %s\n", get_scsi_pt_transport_err_str(obj
, 256, error
));
134 case SCSI_PT_RESULT_OS_ERR
:
135 cprintf(GREY
, "OS error: %s\n", get_scsi_pt_os_err_str(obj
, 256, error
));
139 cprintf(GREY
, "Unknown error\n");
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
);
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);
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};
168 cdb
[8] = (*buffer_size
) >> 8;
169 cdb
[9] = (*buffer_size
) & 0xff;
174 int ret
= do_scsi(cdb
, 12, DO_READ
, sense
, &sense_size
, buffer
, buffer_size
);
177 ret
= do_sense_analysis(ret
, sense
, sense_size
);
183 #define DNK_EXACT_LENGTH (1 << 0)
184 #define DNK_STRING (1 << 1)
185 #define DNK_UINT32 (1 << 2)
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
);
222 struct dnk_prop_t prop
;
223 memset(&prop
, 0, sizeof(prop
));
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]);
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
);
250 cprintf(GREY
, "Device didn't send any data\n");
253 if((prop
.flags
& DNK_EXACT_LENGTH
) && buffer_size
!= prop
.size
)
255 cprintf(GREY
, "Device didn't send the expected amount of data\n");
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
);
265 cprintf(GREEN
, "Property:\n");
266 print_hex(buffer
, buffer_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};
292 strncpy((char *)(cdb
+ 3), prop
->name
, 7); // warning: erase cdb[10] !
295 cdb
[10] = (*buffer_size
+ 15) / 16;
297 cdb
[10] = *buffer_size
;
303 int ret
= do_scsi(cdb
, 12, DO_READ
, sense
, &sense_size
, buffer
, buffer_size
);
306 ret
= do_sense_analysis(ret
, sense
, sense_size
);
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
);
326 struct dpcc_prop_t prop
;
327 memset(&prop
, 0, sizeof(prop
));
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]);
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
);
351 if(buffer_size
< prop
.size
)
352 buffer
[buffer_size
] = 0;
353 cprintf_field("Property: ", "%s\n", buffer
);
361 uint8_t year
[2]; // bcd
362 uint8_t month
; // bcd
368 } __attribute__((packed
));
370 int get_user_time(int argc
, char **argv
)
375 void *buffer
= buffer_alloc(32);
376 int buffer_size
= 32;
377 int ret
= do_dpcc_cmd(1, NULL
, buffer
, &buffer_size
);
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
);
387 int get_dev_info(int argc
, char **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;
398 int ret
= do_scsi(cdb
, 12, DO_READ
, sense
, &sense_size
, buffer
, &buffer_size
);
401 ret
= do_sense_analysis(ret
, sense
, sense_size
);
404 buffer
[buffer_size
] = 0;
405 cprintf_field("Device Info:", "\n");
406 print_hex(buffer
, buffer_size
);
410 typedef int (*cmd_fn_t
)(int argc
, char **argv
);
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
);
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
);
453 int main(int argc
, char **argv
)
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'},
466 int c
= getopt_long(argc
, argv
, "?dcfo:", long_options
, NULL
);
486 g_out_prefix
= optarg
;
493 if(argc
- optind
< 2)
500 g_dev_fd
= scsi_pt_open_device(argv
[optind
], false, true);
503 cprintf(GREY
, "Cannot open device: %m\n");
508 ret
= process_cmd(argv
[optind
+ 1], argc
- optind
- 2, argv
+ optind
+ 2);
510 scsi_pt_close_device(g_dev_fd
);