A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 630 lines 20 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2016 by 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 "soc_desc.hpp" 22#include "soc_desc_v1.hpp" 23#include <cstdio> 24#include <cstdlib> 25#include <map> 26#include <set> 27#include <cstring> 28#include <fstream> 29#include <sstream> 30#include <cstring> 31#include <dirent.h> 32#include <getopt.h> 33#include <regex> 34 35using namespace soc_desc; 36int g_verbose = 0; 37bool g_inline = false; 38bool g_print_zero = false; 39bool g_regex_mode = false; 40std::regex_constants::syntax_option_type g_regex_flags; 41 42std::regex_constants::syntax_option_type parse_regex_mode(const std::string& mode) 43{ 44 std::regex_constants::syntax_option_type flags; 45 size_t index = 0; 46 while(index < mode.size()) 47 { 48 size_t end = mode.find(',', index); 49 if(end == std::string::npos) 50 end = mode.size(); 51 std::string opt = mode.substr(index, end - index); 52 if(opt == "icase") 53 flags |= std::regex_constants::icase; 54 else if(opt == "ECMAScript") 55 flags |= std::regex_constants::ECMAScript; 56 else if(opt == "basic") 57 flags |= std::regex_constants::basic; 58 else if(opt == "extended") 59 flags |= std::regex_constants::extended; 60 else if(opt == "awk") 61 flags |= std::regex_constants::awk; 62 else if(opt == "grep") 63 flags |= std::regex_constants::grep; 64 else if(opt == "egrep") 65 flags |= std::regex_constants::egrep; 66 else 67 { 68 fprintf(stderr, "Invalid regex option '%s'\n", opt.c_str()); 69 exit(1); 70 } 71 index = end + 1; 72 } 73 return flags; 74} 75 76void print_path(node_ref_t node, bool nl = true) 77{ 78 printf("%s", node.soc().get()->name.c_str()); 79 std::vector< std::string > path = node.path(); 80 for(size_t i = 0; i < path.size(); i++) 81 printf(".%s", path[i].c_str()); 82 if(nl) 83 printf("\n"); 84} 85 86template<typename T> 87std::string to_str(const T& t) 88{ 89 std::ostringstream oss; 90 oss << t; 91 return oss.str(); 92} 93 94std::string get_path(node_inst_t inst) 95{ 96 if(!inst.is_root()) 97 { 98 std::string path = get_path(inst.parent()) + "." + inst.name(); 99 if(inst.is_indexed()) 100 path += "[" + to_str(inst.index()) + "]"; 101 return path; 102 } 103 else 104 return inst.soc().get()->name; 105} 106 107void print_inst(node_inst_t inst, bool addr = true, bool nl = true) 108{ 109 if(!inst.is_root()) 110 { 111 print_inst(inst.parent(), false); 112 printf(".%s", inst.name().c_str()); 113 if(inst.is_indexed()) 114 printf("[%u]", (unsigned)inst.index()); 115 } 116 else 117 printf("%s", inst.soc().get()->name.c_str()); 118 if(addr) 119 { 120 printf(" @ %#x", inst.addr()); 121 if(nl) 122 printf("\n"); 123 } 124} 125 126void find_insts(std::vector< node_inst_t >& list, node_inst_t inst, soc_addr_t addr) 127{ 128 if(inst.addr() == addr) 129 { 130 /* only keep matches that are registers */ 131 if(inst.node().reg().valid()) 132 list.push_back(inst); 133 } 134 std::vector< node_inst_t > children = inst.children(); 135 for(size_t i = 0; i < children.size(); i++) 136 find_insts(list, children[i], addr); 137} 138 139void find_insts(std::vector< node_inst_t >& insts, 140 std::vector< soc_t >& soc_list, soc_addr_t addr) 141{ 142 for(size_t i = 0; i < soc_list.size(); i++) 143 find_insts(insts, soc_ref_t(&soc_list[i]).root_inst(), addr); 144} 145 146const size_t NO_INDEX = (size_t)-1; 147/* index is set to NO_INDEX if there is no index */ 148bool parse_name(std::string& name, std::string& component, size_t& index) 149{ 150 size_t i = 0; 151 /* name must be of the form [a-zA-Z0-9_]+ */ 152 while(i < name.size() && (isalnum(name[i]) || name[i] == '_')) 153 i++; 154 if(i == 0) 155 return false; 156 component = name.substr(0, i); 157 /* must stop at the end, or on '.' or on '[' */ 158 if(name.size() == i) 159 { 160 index = NO_INDEX; 161 name = name.substr(i); 162 return true; 163 } 164 else if(name[i] == '.') 165 { 166 index = NO_INDEX; 167 name = name.substr(i + 1); 168 return true; 169 } 170 else if(name[i] == '[') 171 { 172 /* parse index */ 173 char *end; 174 index = strtoul(name.c_str() + i + 1, &end, 0); 175 /* must stop on ']'. Also strtoul is happy with an empty string, check for this */ 176 if(*end != ']' || end == name.c_str() + i + 1) 177 return false; 178 i = end + 1 - name.c_str(); 179 /* check if we have a '.' after that, or the end */ 180 if(i < name.size() && name[i] != '.') 181 return false; 182 name = name.substr(i + 1); 183 return true; 184 } 185 else 186 return false; 187} 188 189int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::regex& regex) 190{ 191 /* only keep matches that are registers */ 192 if(inst.node().reg().valid()) 193 { 194 std::string path = get_path(inst); 195 if(regex_match(path, regex)) 196 matches.push_back(inst); 197 } 198 std::vector< node_inst_t > children = inst.children(); 199 for(size_t i = 0; i < children.size(); i++) 200 find_insts(matches, children[i], regex); 201 return 0; 202} 203 204int find_insts_regex(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list, std::string str) 205{ 206 auto flags = g_regex_flags | std::regex_constants::optimize; /* we will match a lot */ 207 /* any error during construction or mayching will throw exception, and print 208 * an error message so don't catch them */ 209 std::regex regex(str, flags); 210 for(size_t i = 0; i < soc_list.size(); i++) 211 if(find_insts(matches, soc_ref_t(&soc_list[i]).root_inst(), regex) != 0) 212 return 1; 213 return 0; 214} 215 216int find_insts(std::vector< node_inst_t >& matches, node_inst_t inst, std::string name) 217{ 218 if(name.empty()) 219 { 220 if(inst.node().reg().valid()) 221 matches.push_back(inst); 222 return 0; 223 } 224 std::string component; 225 size_t index; 226 bool ok = parse_name(name, component, index); 227 if(!ok) 228 { 229 fprintf(stderr, "invalid name '%s'\n", name.c_str()); 230 return 1; 231 } 232 if(index == NO_INDEX) 233 inst = inst.child(component); 234 else 235 inst = inst.child(component, index); 236 if(inst.valid()) 237 return find_insts(matches, inst, name); 238 else 239 return 0; 240} 241 242int find_insts(std::vector< node_inst_t >& matches, soc_t& soc, std::string name) 243{ 244 return find_insts(matches, soc_ref_t(&soc).root_inst(), name); 245} 246 247int find_insts(std::vector< node_inst_t >& matches, std::vector< soc_t >& soc_list, 248 std::string name) 249{ 250 /* regex mode is special */ 251 if(g_regex_mode) 252 return find_insts_regex(matches, soc_list, name); 253 /* if name is an integer, parse it */ 254 char *end; 255 unsigned long addr = strtoul(name.c_str(), &end, 0); 256 if(*end == 0) 257 { 258 find_insts(matches, soc_list, addr); 259 return 0; 260 } 261 /* else assume it's a name */ 262 std::string name_copy = name; 263 std::string component; 264 size_t index; 265 bool ok = parse_name(name_copy, component, index); 266 if(!ok) 267 { 268 fprintf(stderr, "invalid name '%s'\n", name.c_str()); 269 return 1; 270 } 271 /* a soc cannot be indexed */ 272 for(size_t i = 0; i < soc_list.size(); i++) 273 { 274 int ret; 275 if(index == NO_INDEX && soc_list[i].name == component) 276 ret = find_insts(matches, soc_list[i], name_copy); 277 else 278 ret = find_insts(matches, soc_list[i], name); 279 if(ret != 0) 280 return ret; 281 } 282 return 0; 283} 284 285void do_describe(node_inst_t inst, soc_word_t *decode_val) 286{ 287 if(decode_val) 288 printf(" = %#lx", (unsigned long)*decode_val); 289 if(g_inline) 290 printf(" {"); 291 else 292 printf("\n"); 293 std::vector< field_ref_t > fields = inst.node().reg().fields(); 294 bool first = true; 295 soc_word_t mask = 0; /* mask of all decoded bits */ 296 /* special index fields.size() means "undecoded bits" */ 297 for(size_t i = 0; i <= fields.size(); i++) 298 { 299 unsigned long val = 0; 300 bool dont_print = false; 301 field_t f; 302 if(i == fields.size()) 303 { 304 /* create fake field */ 305 f.name = "undecoded bits"; 306 f.pos = 0; 307 f.width = inst.node().reg().get()->width; 308 val = decode_val ? *decode_val & ~mask : 0; 309 /* only print if decoding and something was left */ 310 dont_print = (val == 0); 311 } 312 else 313 { 314 f = *fields[i].get(); 315 val = decode_val ? f.extract(*decode_val) : 0; 316 /* don't print zero fields unless asked */ 317 dont_print = decode_val && !g_print_zero && val == 0; 318 } 319 if(dont_print) 320 continue; 321 if(g_inline) 322 printf("%s", first ? " " : ", "); 323 else 324 printf(" "); 325 printf("%s", f.name.c_str()); 326 first = false; 327 if(f.width == 1) 328 printf("[%zu]", f.pos); 329 else 330 printf("[%zu:%zu]", f.pos + f.width - 1, f.pos); 331 /* decode if needed */ 332 if(decode_val) 333 { 334 /* track what we decoded */ 335 mask |= ~f.bitmask(); 336 printf(" = %#lx", val); 337 } 338 /* newline if need */ 339 if(!g_inline) 340 printf("\n"); 341 } 342 if(g_inline) 343 printf(" }\n"); 344} 345 346int do_find(std::vector< soc_t >& soc_list, int argc, char **argv, bool describe, 347 soc_word_t *decode_val = nullptr) 348{ 349 if(argc != 1) 350 { 351 fprintf(stderr, "action 'find' takes on argument: the name or address of a register\n"); 352 return 1; 353 } 354 std::vector< node_inst_t > matches; 355 int ret = find_insts(matches, soc_list, argv[0]); 356 if(ret != 0) 357 return 0; 358 /* print matches */ 359 if(matches.size() > 0) 360 { 361 for(size_t i = 0; i < matches.size(); i++) 362 { 363 print_inst(matches[i], true, !describe); 364 if(describe) 365 do_describe(matches[i], decode_val); 366 } 367 return 0; 368 } 369 else 370 { 371 fprintf(stderr, "No matches\n"); 372 return 1; 373 } 374 return 0; 375} 376 377int do_decode(std::vector< soc_t >& soc_list, int argc, char **argv) 378{ 379 if(argc != 2) 380 { 381 fprintf(stderr, "action 'decode' takes two arguments: the register and the value\n"); 382 return 1; 383 } 384 char *end; 385 soc_word_t val = strtoul(argv[1], &end, 0); 386 if(*end) 387 { 388 fprintf(stderr, "invalid value '%s'\n", argv[1]); 389 return 1; 390 } 391 return do_find(soc_list, argc - 1, argv, true, &val); 392} 393 394void print_context(const error_context_t& ctx) 395{ 396 for(size_t j = 0; j < ctx.count(); j++) 397 { 398 err_t e = ctx.get(j); 399 switch(e.level()) 400 { 401 case err_t::INFO: printf("[INFO]"); break; 402 case err_t::WARNING: printf("[WARN]"); break; 403 case err_t::FATAL: printf("[FATAL]"); break; 404 default: printf("[UNK]"); break; 405 } 406 if(e.location().size() != 0) 407 printf(" %s:", e.location().c_str()); 408 printf(" %s\n", e.message().c_str()); 409 } 410} 411 412int usage() 413{ 414 printf("usage: regtool [options] <action> [args]\n"); 415 printf("options:\n"); 416 printf(" -h Display this help\n"); 417 printf(" -f <regfile> Load a register file\n"); 418 printf(" -d <regdir> Specify a directory where to look for register files\n"); 419 printf(" -v Increase verbosity level\n"); 420 printf(" -s <soc list> Limit search to a one or more socs (comma separated)\n"); 421 printf(" -i Describe/decode in one line\n"); 422 printf(" -z Print fields even when the value is zero\n"); 423 printf(" -r <mode> Enable regex mode\n"); 424 printf("\n"); 425 printf("actions:\n"); 426 printf(" find <addr> Find all registers that match this address\n"); 427 printf(" find <name> Find the registers that match this name\n"); 428 printf(" describe <reg> Describe a register (either found by address or name)\n"); 429 printf(" decode <reg> <val> Decode a register value\n"); 430 printf("By default, regtool will look for register files in desc/, but if\n"); 431 printf("any file or directory is specified, regtool will ignore the default directory\n"); 432 printf("Adresses can be in decimal or hexadecimal (using 0x prefix).\n"); 433 printf("Names can be fully qualified with a soc name or left ambiguous.\n"); 434 printf(" <reg> ::= <soc>.<regpath> | <regpath>\n"); 435 printf(" <regpath> ::= (<regcomp>.)*<regcomp>\n"); 436 printf(" <regcomp> ::= <name> | <name>[<index>]\n"); 437 printf("In regex mode, all commands expect a regular expression that is matched\n"); 438 printf("against the full path (<soc>.<regpath>) of every register. The regex must\n"); 439 printf("follow the C++11 standard and accepts the following, comma-separated options:\n"); 440 printf(" icase Case insensitive match\n"); 441 printf(" ECMAScript Use ECMAScript grammar\n"); 442 printf(" basic Use basic POSIX grammar\n"); 443 printf(" extended Use extended basic grammar\n"); 444 printf(" awk Use awk grammar\n"); 445 printf(" grep Use grep grammar\n"); 446 printf(" egrep Use egrep grammar\n"); 447 printf("Examples:\n"); 448 printf(" regtool -d desc/ -s stmp3700,imx233 find 0x8001c310\n"); 449 printf(" regtool find DIGCTL.CHIPID\n"); 450 printf(" regtool describe imx233.DIGCTL.CHIPID\n"); 451 printf(" regtool -f desc/regs-stmp3780.xml decode 0x8001c310 0x37800001\n"); 452 printf(" regtool -r awk find 'imx233\\.LCDIF\\..*'\n"); 453 return 1; 454} 455 456std::string my_dirname(const std::string& filename) 457{ 458 size_t idx = filename.find_last_of("/\\"); 459 if(idx == std::string::npos) 460 return filename; 461 else 462 return filename.substr(0, idx); 463} 464 465/* warn controls whether we warn about error at lowest verbosity: we warn 466 * for files explicitely loaded but not the one listed in directories */ 467bool load_file(std::vector< soc_t >& soc_list, const std::string& file, bool user_load) 468{ 469 if(g_verbose >= 2) 470 fprintf(stderr, "Loading file '%s'...\n", file.c_str()); 471 error_context_t ctx; 472 soc_t s; 473 bool ret = parse_xml(file.c_str(), s, ctx); 474 if(g_verbose >= 1 || user_load) 475 { 476 if(ctx.count() != 0) 477 fprintf(stderr, "In file %s:\n", file.c_str()); 478 print_context(ctx); 479 } 480 if(!ret) 481 { 482 if(g_verbose >= 1 || user_load) 483 fprintf(stderr, "Cannot parse file '%s'\n", file.c_str()); 484 return !user_load; /* error only if user loaded */ 485 } 486 soc_list.push_back(s); 487 return true; 488} 489 490bool load_dir(std::vector< soc_t >& soc_list, const std::string& dirname, bool user_load) 491{ 492 if(g_verbose >= 2) 493 fprintf(stderr, "Loading directory '%s'...\n", dirname.c_str()); 494 DIR *dir = opendir(dirname.c_str()); 495 if(dir == NULL) 496 { 497 if(g_verbose >= 1 || user_load) 498 fprintf(stderr, "Warning: cannot open directory '%s'\n", dirname.c_str()); 499 return !user_load; /* error only if user loaded */ 500 } 501 struct dirent *ent; 502 while((ent = readdir(dir))) 503 { 504 std::string name(ent->d_name); 505 /* only list *.xml */ 506 if(name.size() < 4 || name.substr(name.size() - 4) != ".xml") 507 continue; 508 if(!load_file(soc_list, dirname + "/" + name, false)) 509 { 510 closedir(dir); 511 return false; 512 } 513 } 514 closedir(dir); 515 return true; 516} 517 518void add_socs_to_list(std::set< std::string >& soc_list, const std::string& list) 519{ 520 size_t idx = 0; 521 while(idx < list.size()) 522 { 523 size_t next = list.find(',', idx); 524 if(next == std::string::npos) 525 next = list.size(); 526 soc_list.insert(list.substr(idx, next - idx)); 527 idx = next + 1; 528 } 529} 530 531int main(int argc, char **argv) 532{ 533 std::vector< std::string > g_soc_dir; 534 std::vector< std::string > g_soc_files; 535 std::vector< soc_t > g_soc_list; 536 std::set< std::string> g_allowed_soc; 537 538 if(argc <= 1) 539 return usage(); 540 while(1) 541 { 542 static struct option long_options[] = 543 { 544 {"help", no_argument, 0, 'h'}, 545 {0, 0, 0, 0} 546 }; 547 548 int c = getopt_long(argc, argv, "hf:d:vs:izr:", long_options, NULL); 549 if(c == -1) 550 break; 551 switch(c) 552 { 553 case -1: 554 break; 555 case 'h': 556 return usage(); 557 case 's': 558 add_socs_to_list(g_allowed_soc, optarg); 559 break; 560 case 'd': 561 g_soc_dir.push_back(std::string(optarg)); 562 break; 563 case 'f': 564 g_soc_files.push_back(std::string(optarg)); 565 break; 566 case 'v': 567 g_verbose++; 568 break; 569 case 'i': 570 g_inline = true; 571 break; 572 case 'z': 573 g_print_zero = true; 574 break; 575 case 'r': 576 g_regex_mode = true; 577 g_regex_flags = parse_regex_mode(optarg); 578 break; 579 default: 580 abort(); 581 } 582 } 583 if(argc == optind) 584 { 585 fprintf(stderr, "You need at least one action\n"); 586 return 2; 587 } 588 /* if no file or directory, add default */ 589 if(g_soc_files.empty() && g_soc_dir.empty()) 590 load_dir(g_soc_list, my_dirname(argv[0]) + "/desc", false); 591 /* load directories */ 592 for(size_t i = 0; i < g_soc_dir.size(); i++) 593 load_dir(g_soc_list, g_soc_dir[i], true); 594 /* load files */ 595 for(size_t i = 0; i < g_soc_files.size(); i++) 596 load_file(g_soc_list, g_soc_files[i], true); 597 /* filter soc list */ 598 if(g_allowed_soc.size() > 0) 599 { 600 for(size_t i = 0; i < g_soc_list.size(); i++) 601 { 602 if(g_allowed_soc.find(g_soc_list[i].name) == g_allowed_soc.end()) 603 { 604 std::swap(g_soc_list[i], g_soc_list.back()); 605 g_soc_list.pop_back(); 606 i--; 607 } 608 } 609 } 610 /* print */ 611 if(g_verbose >= 1) 612 { 613 fprintf(stderr, "Available socs after filtering:"); 614 for(size_t i = 0; i < g_soc_list.size(); i++) 615 fprintf(stderr, " %s", g_soc_list[i].name.c_str()); 616 fprintf(stderr, "\n"); 617 } 618 619 std::string action = argv[optind]; 620 argc -= optind + 1; 621 argv += optind + 1; 622 if(action == "find") 623 return do_find(g_soc_list, argc, argv, false); 624 if(action == "describe") 625 return do_find(g_soc_list, argc, argv, true); 626 if(action == "decode") 627 return do_decode(g_soc_list, argc, argv); 628 fprintf(stderr, "unknown action '%s'\n", action.c_str()); 629 return 1; 630}