A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 420 lines 12 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2021 Aidan MacDonald 11 * 12 * Directly adapted from jz4760_tools/usbboot.c, 13 * Copyright (C) 2015 by Amaury Pouly 14 * 15 * This program is free software; you can redistribute it and/or 16 * modify it under the terms of the GNU General Public License 17 * as published by the Free Software Foundation; either version 2 18 * of the License, or (at your option) any later version. 19 * 20 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY 21 * KIND, either express or implied. 22 * 23 ****************************************************************************/ 24 25#include <libusb.h> 26#include <getopt.h> 27#include <unistd.h> 28#include <stdlib.h> 29#include <stdbool.h> 30#include <string.h> 31#include <stdio.h> 32#include <stdarg.h> 33 34#define VR_GET_CPU_INFO 0 35#define VR_SET_DATA_ADDRESS 1 36#define VR_SET_DATA_LENGTH 2 37#define VR_FLUSH_CACHES 3 38#define VR_PROGRAM_START1 4 39#define VR_PROGRAM_START2 5 40 41/* Global variables */ 42bool g_verbose = false; 43libusb_device_handle* g_usb_dev = NULL; 44int g_vid = 0, g_pid = 0; 45 46/* Utility functions */ 47void die(const char* msg, ...) 48{ 49 va_list ap; 50 va_start(ap, msg); 51 vfprintf(stderr, msg, ap); 52 fprintf(stderr, "\n"); 53 va_end(ap); 54 exit(1); 55} 56 57void verbose(const char* msg, ...) 58{ 59 if(!g_verbose) 60 return; 61 62 va_list ap; 63 va_start(ap, msg); 64 vprintf(msg, ap); 65 printf("\n"); 66 va_end(ap); 67} 68 69void open_usb(void) 70{ 71 if(g_usb_dev) { 72 verbose("Closing USB device"); 73 libusb_close(g_usb_dev); 74 } 75 76 if(g_vid == 0 || g_pid == 0) 77 die("Can't open USB device: vendor/product ID not specified"); 78 79 verbose("Opening USB device %04x:%04x", g_vid, g_pid); 80 g_usb_dev = libusb_open_device_with_vid_pid(NULL, g_vid, g_pid); 81 if(!g_usb_dev) 82 die("Could not open USB device"); 83 84 int ret = libusb_claim_interface(g_usb_dev, 0); 85 if(ret != 0) { 86 libusb_close(g_usb_dev); 87 die("Could not claim interface: %d", ret); 88 } 89} 90 91void ensure_usb(void) 92{ 93 if(!g_usb_dev) 94 open_usb(); 95} 96 97/* USB communication functions */ 98void jz_get_cpu_info(void) 99{ 100 ensure_usb(); 101 verbose("Issue GET_CPU_INFO"); 102 103 uint8_t buf[9]; 104 int ret = libusb_control_transfer(g_usb_dev, 105 LIBUSB_ENDPOINT_IN | LIBUSB_REQUEST_TYPE_VENDOR | LIBUSB_RECIPIENT_DEVICE, 106 VR_GET_CPU_INFO, 0, 0, buf, 8, 1000); 107 if(ret != 0) 108 die("Can't get CPU info: %d", ret); 109 110 buf[8] = 0; 111 printf("CPU info: %s\n", buf); 112} 113 114void jz_upload(const char* filename, int length) 115{ 116 if(length < 0) 117 die("invalid upload length: %d", length); 118 119 ensure_usb(); 120 verbose("Transfer %d bytes from device to host", length); 121 122 void* data = malloc(length); 123 int xfered = 0; 124 int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_IN | 1, 125 data, length, &xfered, 10000); 126 if(ret != 0) 127 die("Transfer failed: %d", ret); 128 if(xfered != length) 129 die("Transfer error: got %d bytes, expected %d", xfered, length); 130 131 FILE* f = fopen(filename, "wb"); 132 if(f == NULL) 133 die("Can't open file '%s' for writing", filename); 134 135 if(fwrite(data, length, 1, f) != 1) 136 die("Error writing transfered data to file"); 137 138 fclose(f); 139 free(data); 140} 141 142void jz_download(const char* filename) 143{ 144 FILE* f = fopen(filename, "rb"); 145 if(f == NULL) 146 die("Can't open file '%s' for reading", filename); 147 148 fseek(f, 0, SEEK_END); 149 int length = ftell(f); 150 fseek(f, 0, SEEK_SET); 151 152 void* data = malloc(length); 153 if(fread(data, length, 1, f) != 1) 154 die("Error reading data from file"); 155 fclose(f); 156 157 verbose("Transfer %d bytes from host to device", length); 158 int xfered = 0; 159 int ret = libusb_bulk_transfer(g_usb_dev, LIBUSB_ENDPOINT_OUT | 1, 160 data, length, &xfered, 10000); 161 if(ret != 0) 162 die("Transfer failed: %d", ret); 163 if(xfered != length) 164 die("Transfer error: %d bytes recieved, expected %d", xfered, length); 165 166 free(data); 167} 168 169#define jz_vendor_out_func(name, type, fmt) \ 170 void name(unsigned long param) { \ 171 ensure_usb(); \ 172 verbose("Issue " #type fmt, param); \ 173 int ret = libusb_control_transfer(g_usb_dev, \ 174 LIBUSB_ENDPOINT_OUT|LIBUSB_REQUEST_TYPE_VENDOR|LIBUSB_RECIPIENT_DEVICE, \ 175 VR_##type, param >> 16, param & 0xffff, NULL, 0, 1000); \ 176 if(ret != 0) \ 177 die("Request " #type " failed: %d", ret); \ 178 } 179 180jz_vendor_out_func(jz_set_data_address, SET_DATA_ADDRESS, " 0x%08lx") 181jz_vendor_out_func(jz_set_data_length, SET_DATA_LENGTH, " 0x%0lx") 182jz_vendor_out_func(_jz_flush_caches, FLUSH_CACHES, "") 183jz_vendor_out_func(jz_program_start1, PROGRAM_START1, " 0x%08lx") 184jz_vendor_out_func(jz_program_start2, PROGRAM_START2, " 0x%08lx") 185#define jz_flush_caches() _jz_flush_caches(0) 186 187/* Default settings */ 188struct cpu_profile { 189 const char* name; 190 int vid, pid; 191 unsigned long s1_load_addr, s1_exec_addr; 192 unsigned long s2_load_addr, s2_exec_addr; 193}; 194 195static const struct cpu_profile cpu_profiles[] = { 196 {"x1000", 197 0xa108, 0x1000, 198 0xf4001000, 0xf4001800, 199 0x80004000, 0x80004000}, 200 {"jz4760", 201 0x601a, 0x4760, 202 0x80000000, 0x80000000, 203 0x80000000, 0x80000000}, 204 {NULL} 205}; 206 207/* Simple "download and run" functions for dev purposes */ 208unsigned long s1_load_addr = 0, s1_exec_addr = 0; 209unsigned long s2_load_addr = 0, s2_exec_addr = 0; 210 211void apply_cpu_profile(const char* name) 212{ 213 const struct cpu_profile* p = &cpu_profiles[0]; 214 for(p = &cpu_profiles[0]; p->name != NULL; ++p) { 215 if(strcmp(p->name, name) != 0) 216 continue; 217 218 g_vid = p->vid; 219 g_pid = p->pid; 220 s1_load_addr = p->s1_load_addr; 221 s1_exec_addr = p->s1_exec_addr; 222 s2_load_addr = p->s2_load_addr; 223 s2_exec_addr = p->s2_exec_addr; 224 return; 225 } 226 227 die("CPU '%s' not known", name); 228} 229 230void run_stage1(const char* filename) 231{ 232 if(s1_load_addr == 0 || s1_exec_addr == 0) 233 die("No stage1 binary settings -- did you specify --cpu?"); 234 jz_set_data_address(s1_load_addr); 235 jz_download(filename); 236 jz_program_start1(s1_exec_addr); 237} 238 239void run_stage2(const char* filename) 240{ 241 if(s2_load_addr == 0 || s2_exec_addr == 0) 242 die("No stage2 binary settings -- did you specify --cpu?"); 243 jz_set_data_address(s2_load_addr); 244 jz_download(filename); 245 jz_flush_caches(); 246 jz_program_start2(s2_exec_addr); 247} 248 249/* Main functions */ 250void usage() 251{ 252 printf("\ 253Usage: usbboot [options]\n\ 254\n\ 255Basic options:\n\ 256 --cpu <cpu> Select device CPU type\n\ 257 --stage1 <file> Download and execute stage1 binary\n\ 258 --stage2 <file> Download and execute stage2 binary\n\ 259\n\ 260Advanced options:\n\ 261 --vid <vid> Specify USB vendor ID\n\ 262 --pid <pid> Specify USB product ID\n\ 263 --cpuinfo Ask device for CPU info\n\ 264 --addr <addr> Set data address\n\ 265 --length <len> Set data length\n\ 266 --upload <file> Transfer data from device (needs prior --length)\n\ 267 --download <file> Transfer data to device\n\ 268 --start1 <addr> Execute stage1 code at address\n\ 269 --start2 <addr> Execute stage2 code at address\n\ 270 --flush-caches Flush device CPU caches\n\ 271 --renumerate Close and re-open the USB device\n\ 272 --wait <time> Wait for <time> seconds\n\ 273 -v, --verbose Be verbose\n\ 274\n\ 275Known CPU types and default stage1/stage2 binary settings:\n"); 276 const struct cpu_profile* p = &cpu_profiles[0]; 277 for(p = &cpu_profiles[0]; p->name != NULL; ++p) { 278 printf("* %s\n", p->name); 279 printf(" - USB ID: %04x:%04x\n", p->vid, p->pid); 280 printf(" - Stage1: load %#08lx, exec %#08lx\n", 281 p->s1_load_addr, p->s1_exec_addr); 282 printf(" - Stage2: load %#08lx, exec %#08lx\n", 283 p->s2_load_addr, p->s2_exec_addr); 284 } 285 286 exit(1); 287} 288 289void cleanup() 290{ 291 if(g_usb_dev == NULL) 292 libusb_close(g_usb_dev); 293 libusb_exit(NULL); 294} 295 296int main(int argc, char* argv[]) 297{ 298 if(argc <= 1) 299 usage(); 300 301 libusb_init(NULL); 302 atexit(cleanup); 303 304 enum { 305 OPT_VID = 0x100, OPT_PID, 306 OPT_CPUINFO, 307 OPT_START1, OPT_START2, OPT_FLUSH_CACHES, 308 OPT_RENUMERATE, OPT_WAIT, 309 }; 310 311 static const struct option long_options[] = { 312 {"cpu", required_argument, 0, 'c'}, 313 {"stage1", required_argument, 0, '1'}, 314 {"stage2", required_argument, 0, '2'}, 315 {"vid", required_argument, 0, OPT_VID}, 316 {"pid", required_argument, 0, OPT_PID}, 317 {"cpuinfo", no_argument, 0, OPT_CPUINFO}, 318 {"addr", required_argument, 0, 'a'}, 319 {"length", required_argument, 0, 'l'}, 320 {"upload", required_argument, 0, 'u'}, 321 {"download", required_argument, 0, 'd'}, 322 {"start1", required_argument, 0, OPT_START1}, 323 {"start2", required_argument, 0, OPT_START2}, 324 {"flush-caches", no_argument, 0, OPT_FLUSH_CACHES}, 325 {"renumerate", no_argument, 0, OPT_RENUMERATE}, 326 {"wait", required_argument, 0, OPT_WAIT}, 327 {"help", no_argument, 0, 'h'}, 328 {"verbose", no_argument, 0, 'v'}, 329 {0, 0, 0, 0} 330 }; 331 332 int opt; 333 int data_length = -1; 334 while((opt = getopt_long(argc, argv, "hvc:1:2:a:l:u:d:", long_options, NULL)) != -1) { 335 unsigned long param; 336 char* end; 337 switch(opt) { 338 case OPT_VID: 339 case OPT_PID: 340 case 'a': 341 case 'l': 342 case OPT_START1: 343 case OPT_START2: 344 case OPT_WAIT: 345 param = strtoul(optarg, &end, 0); 346 if(*end) 347 die("Invalid argument '%s'", optarg); 348 break; 349 default: 350 break; 351 } 352 353 switch(opt) { 354 case 'h': 355 usage(); 356 break; 357 case 'v': 358 g_verbose = true; 359 break; 360 case 'c': 361 apply_cpu_profile(optarg); 362 break; 363 case '1': 364 run_stage1(optarg); 365 break; 366 case '2': 367 run_stage2(optarg); 368 break; 369 case OPT_VID: 370 g_vid = param & 0xffff; 371 break; 372 case OPT_PID: 373 g_pid = param & 0xffff; 374 break; 375 case OPT_CPUINFO: 376 jz_get_cpu_info(); 377 break; 378 case 'a': 379 jz_set_data_address(param); 380 break; 381 case 'l': 382 data_length = param; 383 jz_set_data_length(param); 384 break; 385 case 'u': 386 if(data_length < 0) 387 die("Need to specify --length before --upload"); 388 jz_upload(optarg, data_length); 389 break; 390 case 'd': 391 jz_download(optarg); 392 break; 393 case OPT_START1: 394 jz_program_start1(param); 395 break; 396 case OPT_START2: 397 jz_program_start2(param); 398 break; 399 case OPT_FLUSH_CACHES: 400 jz_flush_caches(); 401 break; 402 case OPT_RENUMERATE: 403 open_usb(); 404 break; 405 case OPT_WAIT: 406 verbose("Wait %lu seconds", param); 407 sleep(param); 408 break; 409 default: 410 /* should only happen due to a bug */ 411 die("Bad option"); 412 break; 413 } 414 } 415 416 if(optind != argc) 417 die("Extra arguments on command line"); 418 419 return 0; 420}