A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 310 lines 9.0 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2014 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 <stdlib.h> 23#include <string.h> 24#include <libusb.h> 25#include <stdint.h> 26#include <stdbool.h> 27#include <getopt.h> 28 29bool g_debug = false; 30 31struct dev_info_t 32{ 33 uint16_t vendor_id; 34 uint16_t product_id; 35 unsigned xfer_size; 36}; 37 38struct dev_info_t g_dev_info[] = 39{ 40 {0x0781, 0x0720, 64}, /* Sandisk E200/C200/View */ 41 {0x0b70, 0x0003, 64}, /* Portal Player */ 42}; 43 44#ifndef MIN 45#define MIN(a,b) ((a) < (b) ? (a) : (b)) 46#endif 47 48#ifndef MAX 49#define MAX(a,b) ((a) > (b) ? (a) : (b)) 50#endif 51 52static void put32le(uint8_t *buf, uint32_t i) 53{ 54 *buf++ = i & 0xff; 55 *buf++ = (i >> 8) & 0xff; 56 *buf++ = (i >> 16) & 0xff; 57 *buf++ = (i >> 24) & 0xff; 58} 59 60static int send_recovery(libusb_device_handle *dev, int xfer_size, uint8_t *data, int size) 61{ 62 // there should be no kernel driver attached but in doubt... 63 libusb_detach_kernel_driver(dev, 0); 64 libusb_claim_interface(dev, 0); 65 66 uint8_t size_buffer[4]; 67 put32le(size_buffer, size); 68 int xfered; 69 int ret = libusb_bulk_transfer(dev, 1, size_buffer, sizeof(size_buffer), &xfered, 1000); 70 if(ret < 0 || xfered != sizeof(size_buffer)) 71 { 72 printf("transfer error at init step: %d", ret); 73 return 1; 74 } 75 76 int sent = 0; 77 while(sent < size) 78 { 79 int xfered; 80 int len = MIN(size - sent, xfer_size); 81 int ret = libusb_bulk_transfer(dev, 1, data + sent, len, &xfered, 1000); 82 if(ret < 0 || xfered != len) 83 { 84 printf("transfer error at send offset %d: %d\n", sent, ret); 85 return 1; 86 } 87 sent += xfered; 88 } 89 return 0; 90} 91 92static void usage(void) 93{ 94 printf("sbloader [options] file\n"); 95 printf("options:\n"); 96 printf(" -h/-?/--help Display this help\n"); 97 printf(" -d/--debug Enable debug output\n"); 98 printf(" -x <size> Force transfer size\n"); 99 printf(" -u <vid>:<pid> Force USB PID and VID\n"); 100 printf(" -b <bus>:<dev> Force USB bus and device\n"); 101 printf("The following devices are known to this tool:\n"); 102 for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) 103 { 104 printf(" %04x:%04x (%d bytes/xfer)\n", g_dev_info[i].vendor_id, 105 g_dev_info[i].product_id, g_dev_info[i].xfer_size); 106 } 107 printf("You can select a particular device by USB PID and VID.\n"); 108 printf("In case this is ambiguous, use bus and device number.\n"); 109 printf("Protocol is infered if possible and unspecified.\n"); 110 printf("Transfer size is infered if possible.\n"); 111 exit(1); 112} 113 114static bool dev_match(libusb_device *dev, struct dev_info_t *arg_di, 115 int usb_bus, int usb_dev, int *db_idx) 116{ 117 // match bus/dev 118 if(usb_bus != -1) 119 return libusb_get_bus_number(dev) == usb_bus && libusb_get_device_address(dev) == usb_dev; 120 // get device descriptor 121 struct libusb_device_descriptor desc; 122 if(libusb_get_device_descriptor(dev, &desc)) 123 return false; 124 // match command line vid/pid if specified 125 if(arg_di->vendor_id != 0) 126 return desc.idVendor == arg_di->vendor_id && desc.idProduct == arg_di->product_id; 127 // match known vid/pid 128 for(unsigned i = 0; i < sizeof(g_dev_info) / sizeof(g_dev_info[0]); i++) 129 if(desc.idVendor == g_dev_info[i].vendor_id && desc.idProduct == g_dev_info[i].product_id) 130 { 131 if(db_idx) 132 *db_idx = i; 133 return true; 134 } 135 return false; 136} 137 138static void print_match(libusb_device *dev) 139{ 140 struct libusb_device_descriptor desc; 141 if(libusb_get_device_descriptor(dev, &desc)) 142 printf("????:????"); 143 else 144 printf("%04x:%04x", desc.idVendor, desc.idProduct); 145 printf(" @ %d.%d\n", libusb_get_bus_number(dev), libusb_get_device_address(dev)); 146} 147 148int main(int argc, char **argv) 149{ 150 if(argc <= 1) 151 usage(); 152 struct dev_info_t di = {.vendor_id = 0, .product_id = 0, .xfer_size = 64}; 153 int usb_bus = -1; 154 int usb_dev = -1; 155 int force_xfer_size = 0; 156 /* parse command line */ 157 while(1) 158 { 159 static struct option long_options[] = 160 { 161 {"help", no_argument, 0, '?'}, 162 {"debug", no_argument, 0, 'd'}, 163 {0, 0, 0, 0} 164 }; 165 166 int c = getopt_long(argc, argv, "?dx:u:b:p:", long_options, NULL); 167 if(c == -1) 168 break; 169 switch(c) 170 { 171 case -1: 172 break; 173 case 'd': 174 g_debug = true; 175 break; 176 case '?': 177 usage(); 178 break; 179 case 'x': 180 { 181 char *end; 182 force_xfer_size = strtoul(optarg, &end, 0); 183 if(*end) 184 { 185 printf("Invalid transfer size!\n"); 186 exit(2); 187 } 188 break; 189 } 190 case 'u': 191 { 192 char *end; 193 di.vendor_id = strtoul(optarg, &end, 16); 194 if(*end != ':') 195 { 196 printf("Invalid USB PID!\n"); 197 exit(3); 198 } 199 di.product_id = strtoul(end + 1, &end, 16); 200 if(*end) 201 { 202 printf("Invalid USB VID!\n"); 203 exit(4); 204 } 205 break; 206 } 207 case 'b': 208 { 209 char *end; 210 usb_bus = strtol(optarg, &end, 0); 211 if(*end != ':') 212 { 213 printf("Invalid USB bus!\n"); 214 exit(5); 215 } 216 usb_dev = strtol(end, &end, 0); 217 if(*end) 218 { 219 printf("Invalid USB device!\n"); 220 exit(6); 221 } 222 break; 223 } 224 default: 225 printf("Internal error: unknown option '%c'\n", c); 226 abort(); 227 } 228 } 229 230 if(optind + 1 != argc) 231 usage(); 232 const char *filename = argv[optind]; 233 /* lookup device */ 234 libusb_init(NULL); 235 libusb_set_debug(NULL, 3); 236 libusb_device **list; 237 ssize_t list_size = libusb_get_device_list(NULL, &list); 238 libusb_device_handle *dev = NULL; 239 int db_idx = -1; 240 241 { 242 libusb_device *mdev = NULL; 243 int nr_matches = 0; 244 for(int i = 0; i < list_size; i++) 245 { 246 // match bus/dev if specified 247 if(dev_match(list[i], &di, usb_bus, usb_dev, &db_idx)) 248 { 249 mdev = list[i]; 250 nr_matches++; 251 } 252 } 253 if(nr_matches == 0) 254 { 255 printf("No device found\n"); 256 exit(8); 257 } 258 if(nr_matches > 1) 259 { 260 printf("Several devices match the specified parameters:\n"); 261 for(int i = 0; i < list_size; i++) 262 { 263 // match bus/dev if specified 264 if(dev_match(list[i], &di, usb_bus, usb_dev, NULL)) 265 { 266 printf(" "); 267 print_match(list[i]); 268 } 269 } 270 } 271 printf("Device: "); 272 print_match(mdev); 273 libusb_open(mdev, &dev); 274 } 275 if(dev == NULL) 276 { 277 printf("Cannot open device\n"); 278 return 1; 279 } 280 /* get protocol */ 281 int xfer_size = di.xfer_size; 282 if(db_idx >= 0) 283 xfer_size = g_dev_info[db_idx].xfer_size; 284 if(force_xfer_size > 0) 285 xfer_size = force_xfer_size; 286 /* open file */ 287 FILE *f = fopen(filename, "r"); 288 if(f == NULL) 289 { 290 perror("Cannot open file"); 291 return 1; 292 } 293 fseek(f, 0, SEEK_END); 294 size_t size = ftell(f); 295 fseek(f, 0, SEEK_SET); 296 297 printf("Transfer size: %d\n", xfer_size); 298 uint8_t *file_buf = malloc(size); 299 if(fread(file_buf, size, 1, f) != 1) 300 { 301 perror("read error"); 302 fclose(f); 303 return 1; 304 } 305 fclose(f); 306 /* send file */ 307 return send_recovery(dev, xfer_size, file_buf, size); 308} 309 310