A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 504 lines 16 kB view raw
1/*************************************************************************** 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> 34#include <inttypes.h> 35#include <errno.h> 36#include "rbscsi.h" 37#include "misc.h" 38#include "stmp_scsi.h" 39 40bool g_debug = false; 41bool g_force = false; 42stmp_device_t g_dev_fd; 43 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 if(buffer_size == 0) 67 printf("\n"); 68} 69 70static const char *get_size_suffix(unsigned long long size) 71{ 72 int order = 0; 73 while(size >= 1024) 74 { 75 size /= 1024; 76 order++; 77 } 78 static const char *suffix[] = {"B", "KiB", "MiB", "GiB", "TiB"}; 79 return suffix[order]; 80} 81 82static float get_size_natural(unsigned long long size) 83{ 84 float res = size; 85 while(res >= 1024) 86 res /= 1024; 87 return res; 88} 89 90static void print_ver(struct scsi_stmp_logical_drive_info_version_t *ver) 91{ 92 cprintf(YELLOW, "%x.%x.%x\n", ver->major, ver->minor, ver->revision); 93} 94 95static int do_info(void) 96{ 97 cprintf(BLUE, "Information\n"); 98 99 uint8_t dev_type; 100 char vendor[9]; 101 char product[17]; 102 int ret = stmp_scsi_inquiry(g_dev_fd, &dev_type, vendor, product); 103 if(ret == 0) 104 { 105 cprintf_field(" Vendor: ", "%s\n", vendor); 106 cprintf_field(" Product: ", "%s\n", product); 107 } 108 else if(g_debug) 109 cprintf(GREY, "Cannot get inquiry data: %d\n", ret); 110 111 struct scsi_stmp_protocol_version_t ver; 112 ret = stmp_get_protocol_version(g_dev_fd, &ver); 113 if(ret == 0) 114 cprintf_field(" Protocol: ", "%x.%x\n", ver.major, ver.minor); 115 else if(g_debug) 116 cprintf(GREY, "Cannot get protocol version: %d\n", ret); 117 118 cprintf(BLUE, "Device\n"); 119 120 uint8_t *serial; 121 int serial_len; 122 if(!stmp_get_device_serial(g_dev_fd, &serial, &serial_len)) 123 { 124 cprintf_field(" Serial Number:", " "); 125 print_hex(serial, serial_len); 126 free(serial); 127 } 128 129 uint16_t chip_rev; 130 ret = stmp_get_chip_major_rev_id(g_dev_fd, &chip_rev); 131 if(ret) 132 cprintf(GREY, "Cannot get chip major revision id: %d\n", ret); 133 else 134 cprintf_field(" Chip Major Rev ID: ", "%x\n", chip_rev); 135 136 uint16_t rom_rev; 137 ret = stmp_get_rom_rev_id(g_dev_fd, &rom_rev); 138 if(ret) 139 cprintf(GREY, "Cannot get rom revision id: %d\n", ret); 140 else 141 cprintf_field(" ROM Rev ID: ", "%x\n", rom_rev); 142 143 cprintf(BLUE, "Logical Media\n"); 144 struct stmp_logical_media_info_t info; 145 ret = stmp_get_logical_media_info(g_dev_fd, &info); 146 if(!ret) 147 { 148 if(info.has.nr_drives) 149 cprintf_field(" Number of drives:", " %u\n", info.nr_drives); 150 if(info.has.size) 151 { 152 cprintf_field(" Media size:", " %" PRIu64 " ", info.size); 153 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); 154 } 155 if(info.has.alloc_size) 156 { 157 cprintf_field(" Allocation unit size:", " %lu ", (unsigned long)info.alloc_size); 158 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.alloc_size), get_size_suffix(info.alloc_size)); 159 } 160 if(info.has.initialised) 161 cprintf_field(" Initialised:", " %u\n", info.initialised); 162 if(info.has.state) 163 cprintf_field(" State:", " %u\n", info.state); 164 if(info.has.write_protected) 165 cprintf_field(" Write protected:", " %u\n", info.write_protected); 166 if(info.has.type) 167 { 168 cprintf_field(" Type:", " %u ", info.type); 169 cprintf(RED, "(%s)\n", stmp_get_logical_media_type_string(info.type)); 170 } 171 if(info.has.serial) 172 { 173 cprintf_field(" Serial:", " "); 174 print_hex(info.serial, info.serial_len); 175 free(info.serial); 176 } 177 if(info.has.system) 178 cprintf_field(" System:", " %u\n", info.system); 179 if(info.has.present) 180 cprintf_field(" Present:", " %u\n", info.present); 181 if(info.has.page_size) 182 { 183 cprintf_field(" Page size:", " %lu ", (unsigned long)info.page_size); 184 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.page_size), get_size_suffix(info.page_size)); 185 } 186 if(info.has.vendor) 187 { 188 cprintf_field(" Vendor:", " %u ", info.vendor); 189 cprintf(RED, "(%s)\n", stmp_get_logical_media_vendor_string(info.vendor)); 190 } 191 if(info.has.nand_id) 192 { 193 cprintf_field(" Nand ID:", " "); 194 print_hex(info.nand_id, sizeof(info.nand_id)); 195 } 196 if(info.has.nr_devices) 197 cprintf_field(" Number of devices:", " %lu\n", (unsigned long)info.nr_devices); 198 } 199 else 200 cprintf(GREY, "Cannot get media info: %d\n", ret); 201 202 struct stmp_logical_media_table_t *table; 203 ret = stmp_get_logical_media_table(g_dev_fd, &table); 204 if(!ret) 205 { 206 cprintf(BLUE, "Logical Media Table\n"); 207 for(int i = 0; i < table->header.count; i++) 208 { 209 cprintf(RED, " Drive "); 210 cprintf_field("No: ", "%2x", table->entry[i].drive_no); 211 cprintf_field(" Type: ", "%#x ", table->entry[i].type); 212 cprintf(RED, "(%s)", stmp_get_logical_drive_type_string(table->entry[i].type)); 213 cprintf_field(" Tag: ", "%#x ", table->entry[i].tag); 214 cprintf(RED, "(%s)", stmp_get_logical_drive_tag_string(table->entry[i].tag)); 215 unsigned long long size = table->entry[i].size; 216 cprintf_field(" Size: ", "%.3f %s", get_size_natural(size), get_size_suffix(size)); 217 cprintf(OFF, "\n"); 218 } 219 220 for(int i = 0; i < table->header.count; i++) 221 { 222 uint8_t drive = table->entry[i].drive_no; 223 cprintf(BLUE, "Drive "); 224 cprintf(YELLOW, "%02x\n", drive); 225 struct stmp_logical_drive_info_t info; 226 ret = stmp_get_logical_drive_info(g_dev_fd, drive, &info); 227 if(ret) 228 continue; 229 if(info.has.sector_size) 230 { 231 cprintf_field(" Sector size:", " %" PRIu32 " ", info.sector_size); 232 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.sector_size), get_size_suffix(info.sector_size)); 233 } 234 if(info.has.erase_size) 235 { 236 cprintf_field(" Erase size:", " %" PRIu32 " ", info.erase_size); 237 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.erase_size), get_size_suffix(info.erase_size)); 238 } 239 if(info.has.size) 240 { 241 cprintf_field(" Drive size:", " %" PRIu64 " ", info.size); 242 cprintf(RED, "(%.3f %s)\n", get_size_natural(info.size), get_size_suffix(info.size)); 243 } 244 if(info.has.sector_count) 245 cprintf_field(" Sector count:", " %lu\n", (unsigned long)info.sector_count); 246 if(info.has.type) 247 { 248 cprintf_field(" Type:", " %u ", info.type); 249 cprintf(RED, "(%s)\n", stmp_get_logical_drive_type_string(info.type)); 250 } 251 if(info.has.tag) 252 { 253 cprintf_field(" Tag:", " %u ", info.tag); 254 cprintf(RED, "(%s)\n", stmp_get_logical_drive_tag_string(info.tag)); 255 } 256 if(info.has.component_version) 257 { 258 cprintf_field(" Component version:", " "); 259 print_ver(&info.component_version); 260 } 261 if(info.has.project_version) 262 { 263 cprintf_field(" Project version:", " "); 264 print_ver(&info.project_version); 265 } 266 if(info.has.write_protected) 267 cprintf_field(" Write protected:", " %u\n", info.write_protected); 268 if(info.has.serial) 269 { 270 cprintf_field(" Serial:", " "); 271 print_hex(info.serial, info.serial_len); 272 free(info.serial); 273 } 274 if(info.has.change) 275 cprintf_field(" Change:", " %u\n", info.change); 276 if(info.has.present) 277 cprintf_field(" Present:", " %u\n", info.present); 278 } 279 free(table); 280 } 281 else 282 cprintf(GREY, "Cannot get logical table: %d\n", ret); 283 284 return 0; 285} 286 287struct rw_fw_context_t 288{ 289 int tot_size; 290 int cur_size; 291 int last_percent; 292 FILE *f; 293 bool read; 294}; 295 296int rw_fw(void *user, void *buf, size_t size) 297{ 298 struct rw_fw_context_t *ctx = user; 299 int this_percent = (ctx->cur_size * 100LLU) / ctx->tot_size; 300 if(this_percent != ctx->last_percent && (this_percent % 5) == 0) 301 { 302 cprintf(RED, "%d%%", this_percent); 303 cprintf(YELLOW, "..."); 304 fflush(stdout); 305 } 306 ctx->last_percent = this_percent; 307 int ret = -1; 308 if(ctx->read) 309 ret = fread(buf, size, 1, ctx->f); 310 else 311 ret = fwrite(buf, size, 1, ctx->f); 312 ctx->cur_size += size; 313 if(ret != 1) 314 return -1; 315 else 316 return size; 317} 318 319void rw_finish(struct rw_fw_context_t *ctx) 320{ 321 if(ctx->last_percent == 100) 322 return; 323 cprintf(RED, "100%%\n"); 324} 325 326void do_extract(const char *file) 327{ 328 FILE *f = fopen(file, "wb"); 329 if(f == NULL) 330 { 331 cprintf(GREY, "Cannot open output file: %s\n", strerror(errno)); 332 return; 333 } 334 int ret = stmp_read_firmware(g_dev_fd, NULL, NULL); 335 if(ret < 0) 336 { 337 cprintf(GREY, "Cannot get firmware size: %d\n", ret); 338 return; 339 } 340 struct rw_fw_context_t ctx; 341 ctx.tot_size = ret; 342 ctx.cur_size = 0; 343 ctx.f = f; 344 ctx.last_percent = -1; 345 ctx.read = false; 346 ret = stmp_read_firmware(g_dev_fd, &ctx, &rw_fw); 347 if(ret < 0) 348 cprintf(GREY, "Cannot read firmware: %d\n", ret); 349 rw_finish(&ctx); 350 fclose(f); 351} 352 353void do_write(const char *file, int want_a_brick) 354{ 355 if(!want_a_brick) 356 { 357 cprintf(GREY, "Writing a new firmware is a dangerous operation that should be attempted\n"); 358 cprintf(GREY, "if you know what you are doing. If you do, please add the --yes-i-want-a-brick\n"); 359 cprintf(GREY, "option on the command line and do not complain if you end up with a brick ;)\n"); 360 return; 361 } 362 FILE *f = fopen(file, "rb"); 363 if(f == NULL) 364 { 365 cprintf(GREY, "Cannot open output file: %s\n", strerror(errno)); 366 return; 367 } 368 struct rw_fw_context_t ctx; 369 fseek(f, 0, SEEK_END); 370 ctx.tot_size = ftell(f); 371 fseek(f, 0, SEEK_SET); 372 ctx.cur_size = 0; 373 ctx.f = f; 374 ctx.last_percent = -1; 375 ctx.read = true; 376 int ret = stmp_write_firmware(g_dev_fd, &ctx, &rw_fw); 377 if(ret < 0) 378 cprintf(GREY, "Cannot write firmware: %d\n", ret); 379 rw_finish(&ctx); 380 fclose(f); 381} 382 383static void usage(void) 384{ 385 printf("Usage: scsitool [options] <dev>\n"); 386 printf("Options:\n"); 387 printf(" -f/--force Force to continue on errors\n"); 388 printf(" -?/--help Display this message\n"); 389 printf(" -d/--debug Display debug messages\n"); 390 printf(" -c/--no-color Disable color output\n"); 391 printf(" -x/--extract-fw <file> Extract firmware to file\n"); 392 printf(" -w/--write-fw <file> Write firmware to device\n"); 393 printf(" -i/--info Display device information\n"); 394 printf(" --yes-i-want-a-brick Allow the tool to turn your device into a brick\n"); 395 exit(1); 396} 397 398static int g_yes_i_want_a_brick = 0; 399 400static void scsi_printf(void *user, const char *fmt, ...) 401{ 402 (void)user; 403 if(!g_debug) 404 return; 405 va_list args; 406 va_start(args, fmt); 407 color(GREY); 408 vprintf(fmt, args); 409 va_end(args); 410} 411 412int main(int argc, char **argv) 413{ 414 if(argc == 1) 415 usage(); 416 const char *extract_fw = NULL; 417 const char *write_fw = NULL; 418 bool info = false; 419 while(1) 420 { 421 static struct option long_options[] = 422 { 423 {"help", no_argument, 0, '?'}, 424 {"debug", no_argument, 0, 'd'}, 425 {"no-color", no_argument, 0, 'c'}, 426 {"force", no_argument, 0, 'f'}, 427 {"extract-fw", required_argument, 0, 'x'}, 428 {"write-fw", required_argument, 0, 'w'}, 429 {"info", no_argument, 0, 'i'}, 430 {"yes-i-want-a-brick", no_argument, &g_yes_i_want_a_brick, 1}, 431 {0, 0, 0, 0} 432 }; 433 434 int c = getopt_long(argc, argv, "?dcfx:iw:", long_options, NULL); 435 if(c == -1) 436 break; 437 switch(c) 438 { 439 case 0: 440 continue; 441 case -1: 442 break; 443 case 'c': 444 enable_color(false); 445 break; 446 case 'd': 447 g_debug = true; 448 break; 449 case 'f': 450 g_force = true; 451 break; 452 case '?': 453 usage(); 454 break; 455 case 'x': 456 extract_fw = optarg; 457 break; 458 case 'w': 459 write_fw = optarg; 460 break; 461 case 'i': 462 info = true; 463 break; 464 default: 465 abort(); 466 } 467 } 468 469 if(argc - optind != 1) 470 { 471 usage(); 472 return 1; 473 } 474 475 int ret = 0; 476 rb_scsi_device_t scsi_dev = rb_scsi_open(argv[optind], g_debug ? RB_SCSI_DEBUG : 0, NULL, scsi_printf); 477 if(scsi_dev == 0) 478 { 479 cprintf(GREY, "Cannot open device\n"); 480 ret = 1; 481 goto Lend; 482 } 483 g_dev_fd = stmp_open(scsi_dev, g_debug ? STMP_DEBUG : 0, NULL, scsi_printf); 484 if(g_dev_fd == 0) 485 { 486 cprintf(GREY, "Cannot open stmp device\n"); 487 ret = 2; 488 goto Lend; 489 } 490 491 if(extract_fw) 492 do_extract(extract_fw); 493 if(info) 494 do_info(); 495 if(write_fw) 496 do_write(write_fw, g_yes_i_want_a_brick); 497 498 stmp_close(g_dev_fd); 499 rb_scsi_close(scsi_dev); 500Lend: 501 color(OFF); 502 503 return ret; 504}