A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 525 lines 17 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2014 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#define _XOPEN_SOURCE 500 22#define _DEFAULT_SOURCE 23#include <stdlib.h> 24#include <stdio.h> 25#include <stdarg.h> 26#include "rbscsi.h" 27 28static void misc_std_printf(void *user, const char *fmt, ...) 29{ 30 (void) user; 31 va_list args; 32 va_start(args, fmt); 33 vprintf(fmt, args); 34 va_end(args); 35} 36 37#if defined(_WIN32) || defined(__WIN32__) 38#define RB_SCSI_WINDOWS 39#include <windows.h> 40#include <ntddscsi.h> 41#include <stdint.h> 42typedef HANDLE rb_scsi_handle_t; 43#elif defined(__linux) || defined(__linux__) || defined(linux) 44#include <unistd.h> 45#include <fcntl.h> 46#include <stdio.h> 47#include <string.h> 48#include <stdint.h> 49#include <errno.h> 50#include <sys/ioctl.h> 51#include <scsi/sg.h> 52#include <dirent.h> 53#include <linux/limits.h> 54#include <sys/types.h> 55#include <sys/stat.h> 56#define RB_SCSI_LINUX 57typedef int rb_scsi_handle_t; 58#else 59typedef void *rb_scsi_handle_t; 60#endif 61 62struct rb_scsi_device_t 63{ 64 rb_scsi_handle_t handle; 65 void *user; 66 rb_scsi_printf_t printf; 67 unsigned flags; 68}; 69 70/* Linux */ 71#ifdef RB_SCSI_LINUX 72/* the values for hdr.driver_status are not defined in public headers */ 73#define DRIVER_SENSE 0x08 74 75rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user, 76 rb_scsi_printf_t printf) 77{ 78 if(printf == NULL) 79 printf = misc_std_printf; 80 int fd = open(path, (flags & RB_SCSI_READ_ONLY) ? O_RDONLY : O_RDWR); 81 if(fd < 0) 82 { 83 if(flags & RB_SCSI_DEBUG) 84 printf(user, "rb_scsi: open failed: %s\n", strerror(errno)); 85 return NULL; 86 } 87 rb_scsi_device_t dev = malloc(sizeof(struct rb_scsi_device_t)); 88 dev->flags = flags; 89 dev->handle = fd; 90 dev->user = user; 91 dev->printf = printf ? printf : misc_std_printf; 92 return dev; 93} 94 95int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw) 96{ 97#define rb_printf(...) \ 98 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0) 99 sg_io_hdr_t hdr; 100 memset(&hdr, 0, sizeof(hdr)); 101 /* prepare transfer descriptor */ 102 hdr.interface_id = 'S'; 103 switch(raw->dir) 104 { 105 case RB_SCSI_NONE: hdr.dxfer_direction = SG_DXFER_NONE; break; 106 case RB_SCSI_READ: hdr.dxfer_direction = SG_DXFER_FROM_DEV; break; 107 case RB_SCSI_WRITE: hdr.dxfer_direction = SG_DXFER_TO_DEV; break; 108 default: rb_printf("rb_scsi: invalid direction"); return -1; 109 } 110 hdr.cmd_len = raw->cdb_len; 111 hdr.mx_sb_len = raw->sense_len; 112 hdr.dxfer_len = raw->buf_len; 113 hdr.dxferp = raw->buf; 114 hdr.cmdp = raw->cdb; 115 hdr.sbp = raw->sense; 116 hdr.timeout = raw->tmo * 1000; 117 hdr.flags = SG_FLAG_LUN_INHIBIT; 118 /* do raw command */ 119 if(ioctl(dev->handle, SG_IO, &hdr) < 0) 120 { 121 raw->status = errno; 122 rb_printf("rb_scsi: ioctl failed: %s\n", strerror(raw->status)); 123 return RB_SCSI_OS_ERROR; 124 } 125 /* error handling is weird: host status has the priority */ 126 if(hdr.host_status) 127 { 128 rb_printf("rb_scsi: host error: %d\n", hdr.host_status); 129 raw->status = hdr.host_status; 130 return RB_SCSI_ERROR; 131 } 132 raw->status = hdr.status & 0x7e; 133 raw->buf_len -= hdr.resid; 134 raw->sense_len = hdr.sb_len_wr; 135 /* driver error can report sense */ 136 if((hdr.driver_status & 0xf) == DRIVER_SENSE) 137 { 138 rb_printf("rb_scsi: driver status reported sense\n"); 139 return RB_SCSI_SENSE; 140 } 141 /* otherwise errors */ 142 if(hdr.driver_status) 143 { 144 rb_printf("rb_scsi: driver error: %d\n", hdr.driver_status); 145 return RB_SCSI_ERROR; 146 } 147 /* scsi status can imply sense */ 148 if(raw->status == RB_SCSI_CHECK_CONDITION || raw->status == RB_SCSI_COMMAND_TERMINATED) 149 return RB_SCSI_SENSE; 150 return raw->status ? RB_SCSI_STATUS : RB_SCSI_OK; 151#undef rb_printf 152} 153 154void rb_scsi_close(rb_scsi_device_t dev) 155{ 156 close(dev->handle); 157 free(dev); 158} 159 160/* find the first entry of a directory (which is expended to contain only one) and turn 161 * it into a /dev/ path */ 162static int scan_resolve_first_dev_path(char *path, size_t pathsz) 163{ 164 DIR *dir = opendir(path); 165 if(dir == NULL) 166 return 0; 167 struct dirent *d; 168 int ret = 0; 169 while((d = readdir(dir))) 170 { 171 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 172 continue; 173 /* we expect a directory, but it could be a symlink, only the name matters */ 174 if(d->d_type == DT_LNK || d->d_type == DT_DIR) 175 { 176 snprintf(path, pathsz, "/dev/%s", d->d_name); 177 ret = 1; 178 } 179 break; 180 } 181 closedir(dir); 182 return ret; 183} 184 185static char *read_file_or_null(const char *path) 186{ 187 FILE *f = fopen(path, "r"); 188 if(f == NULL) 189 return NULL; 190 char buffer[1024]; 191 if(fgets(buffer, sizeof(buffer), f) == NULL) 192 { 193 fclose(f); 194 return NULL; 195 } 196 fclose(f); 197 /* the kernel appends a '\n' at the end, remove it */ 198 size_t len = strlen(buffer); 199 if(len > 0 && buffer[len - 1] == '\n') 200 buffer[len - 1] = 0; 201 return strdup(buffer); 202} 203 204struct rb_scsi_devent_t *rb_scsi_list(void) 205{ 206 /* list devices in /sys/class/scsi_generic 207 * we only keep entries of the form sgX */ 208#define SYS_SCSI_DEV_PATH "/sys/class/scsi_generic" 209 DIR *dir = opendir(SYS_SCSI_DEV_PATH); 210 if(dir == NULL) 211 return NULL; 212 struct dirent *d; 213 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t)); 214 dev[0].scsi_path = NULL; 215 dev[0].block_path = NULL; 216 int nr_dev = 0; 217 while((d = readdir(dir))) 218 { 219 if(!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) 220 continue; 221 /* make sure the name is of the form sgX, and extract X */ 222 int sg_idx; 223 if(sscanf(d->d_name, "sg%d", &sg_idx) != 1) 224 continue; 225 dev = realloc(dev, (2 + nr_dev) * sizeof(struct rb_scsi_devent_t)); 226 227 char path[PATH_MAX]; 228 snprintf(path, sizeof(path), "/dev/sg%d", sg_idx); 229 dev[nr_dev].scsi_path = strdup(path); 230 /* /sys/class/scsi_generic/sgX is a folder containing a subfolder 'device' that contains 231 * - a 'block' folder with a single entry: the block device 232 * - the vendor/model/rev files */ 233 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/block", sg_idx); 234 if(!scan_resolve_first_dev_path(path, sizeof(path))) 235 path[0] = 0; 236 dev[nr_dev].block_path = path[0] == 0 ? NULL : strdup(path); 237 /* fill vendor/model/rev */ 238 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/vendor", sg_idx); 239 dev[nr_dev].vendor = read_file_or_null(path); 240 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/model", sg_idx); 241 dev[nr_dev].model = read_file_or_null(path); 242 snprintf(path, sizeof(path), SYS_SCSI_DEV_PATH "/sg%d/device/rev", sg_idx); 243 dev[nr_dev].rev = read_file_or_null(path); 244 245 /* sentinel */ 246 dev[++nr_dev].scsi_path = NULL; 247 dev[nr_dev].block_path = NULL; 248 } 249 closedir(dir); 250 return dev; 251} 252/* Windows */ 253#elif defined(RB_SCSI_WINDOWS) 254/* return either path or something allocated with malloc() */ 255static const char *map_to_physical_drive(const char *path, unsigned flags, void *user, 256 rb_scsi_printf_t printf) 257{ 258 /* don't do anything if path starts with '\' */ 259 if(path[0] == '\\') 260 return path; 261 /* Convert to UNC path (C: -> \\.\C:) otherwise it won't work) */ 262 char *unc_path = malloc(strlen(path) + 5); 263 sprintf(unc_path, "\\\\.\\%s", path); 264 if(flags & RB_SCSI_DEBUG) 265 printf(user, "rb_scsi: map to UNC path: %s\n", unc_path); 266 return unc_path; 267} 268 269rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user, 270 rb_scsi_printf_t printf) 271{ 272 if(printf == NULL) 273 printf = misc_std_printf; 274 /* magic to auto-detect physical drive */ 275 const char *open_path = map_to_physical_drive(path, flags, user, printf); 276 HANDLE h = CreateFileA(open_path, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, 277 NULL, OPEN_EXISTING, FILE_FLAG_WRITE_THROUGH | FILE_FLAG_NO_BUFFERING, NULL); 278 /* free path if it was allocated */ 279 if(open_path != path) 280 free((char *)open_path); 281 if(h == INVALID_HANDLE_VALUE) 282 { 283 if(flags & RB_SCSI_DEBUG) 284 { 285 LPSTR msg = NULL; 286 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 287 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); 288 printf(user, "rb_scsi: open failed: %s\n", msg); 289 LocalFree(msg); 290 } 291 return NULL; 292 } 293 rb_scsi_device_t dev = malloc(sizeof(struct rb_scsi_device_t)); 294 dev->flags = flags; 295 dev->handle = h; 296 dev->user = user; 297 dev->printf = printf ? printf : misc_std_printf; 298 return dev; 299} 300 301int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw) 302{ 303#define rb_printf(...) \ 304 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0) 305 /* we need to allocate memory for SCSI_PASS_THROUGH_WITH_BUFFERS and the 306 * data and sense */ 307 DWORD length = sizeof(SCSI_PASS_THROUGH) + raw->buf_len + raw->sense_len; 308 DWORD returned = 0; 309 SCSI_PASS_THROUGH *spt = malloc(length); 310 memset(spt, 0, length); 311 spt->Length = sizeof(SCSI_PASS_THROUGH); 312 spt->PathId = 0; 313 spt->TargetId = 0; 314 spt->Lun = 0; 315 spt->CdbLength = raw->cdb_len; 316 memcpy(spt->Cdb, raw->cdb, raw->cdb_len); 317 spt->SenseInfoLength = raw->sense_len; 318 spt->SenseInfoOffset = spt->Length; /* Sense after header */ 319 switch(raw->dir) 320 { 321 case RB_SCSI_NONE: spt->DataIn = SCSI_IOCTL_DATA_UNSPECIFIED; break; 322 case RB_SCSI_READ: spt->DataIn = SCSI_IOCTL_DATA_IN; break; 323 case RB_SCSI_WRITE: spt->DataIn = SCSI_IOCTL_DATA_OUT; break; 324 default: 325 free(spt); 326 rb_printf("rb_scsi: invalid direction"); return -1; 327 } 328 spt->DataTransferLength = raw->buf_len; 329 spt->DataBufferOffset = spt->SenseInfoOffset + spt->SenseInfoLength; /* Data after Sense */ 330 spt->TimeOutValue = raw->tmo; 331 /* on write, copy data to buffer */ 332 if(raw->dir == RB_SCSI_WRITE) 333 memcpy((BYTE *)spt + spt->DataBufferOffset, raw->buf, raw->buf_len); 334 BOOL status = DeviceIoControl(dev->handle, IOCTL_SCSI_PASS_THROUGH, 335 spt, length, spt, length, &returned, FALSE); 336 if(!status) 337 { 338 if(dev->flags & RB_SCSI_DEBUG) 339 { 340 LPSTR msg = NULL; 341 FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, 342 NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&msg, 0, NULL); 343 printf(dev->user, "rb_scsi: ioctl failed: %s\n", msg); 344 LocalFree(msg); 345 } 346 free(spt); 347 return RB_SCSI_OS_ERROR; 348 } 349 /* on read, copy data from buffer */ 350 if(raw->dir == RB_SCSI_READ) 351 { 352 raw->buf_len = spt->DataTransferLength; 353 memcpy(raw->buf, (BYTE *)spt + spt->DataBufferOffset, raw->buf_len); 354 } 355 /* check status */ 356 raw->status = spt->ScsiStatus; 357 if(raw->status == RB_SCSI_CHECK_CONDITION || raw->status == RB_SCSI_COMMAND_TERMINATED) 358 { 359 memcpy(raw->sense, (BYTE *)spt + spt->SenseInfoOffset, raw->sense_len); 360 free(spt); 361 return RB_SCSI_SENSE; 362 } 363 else 364 raw->sense_len = 0; 365 free(spt); 366 return raw->status ? RB_SCSI_STATUS : RB_SCSI_OK; 367#undef rb_printf 368} 369 370void rb_scsi_close(rb_scsi_device_t dev) 371{ 372 CloseHandle(dev->handle); 373 free(dev); 374} 375 376struct rb_scsi_devent_t *rb_scsi_list(void) 377{ 378 /* unimplemented */ 379 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t)); 380 dev[0].scsi_path = NULL; 381 dev[0].block_path = NULL; 382 int nr_dev = 0; 383 /* list logical drives */ 384 DWORD cbStrSize = GetLogicalDriveStringsA(0, NULL); 385 LPSTR pszDrives = malloc(cbStrSize); 386 cbStrSize = GetLogicalDriveStringsA(cbStrSize, pszDrives); 387 /* drives are separated by a NULL character, the last drive is just a NULL one */ 388 for(LPSTR pszDriveRoot = pszDrives; *pszDriveRoot != '\0'; 389 pszDriveRoot += lstrlen(pszDriveRoot) + 1) 390 { 391 // each drive is of the form "X:\" 392 char path[3]; 393 path[0] = pszDriveRoot[0]; // letter 394 path[1] = ':'; 395 path[2] = 0; 396 /* open drive */ 397 rb_scsi_device_t rdev = rb_scsi_open(path, 0, NULL, NULL); 398 if(rdev == NULL) 399 continue; // ignore device 400 // send an INQUIRY device to check it's actually an SCSI device and get information 401 uint8_t inquiry_data[36]; 402 uint8_t cdb[6] = {0x12, 0, 0, 0, sizeof(inquiry_data), 0}; // INQUIRY 403 404 struct rb_scsi_raw_cmd_t raw; 405 raw.dir = RB_SCSI_READ; 406 raw.cdb_len = sizeof(cdb); 407 raw.cdb = cdb; 408 raw.buf = inquiry_data; 409 raw.buf_len = sizeof(inquiry_data); 410 raw.sense_len = 0; // don't bother with SENSE, this command cannot possibly fail for good reasons 411 raw.sense = NULL; 412 raw.tmo = 5; 413 int ret = rb_scsi_raw_xfer(rdev, &raw); 414 rb_scsi_close(rdev); 415 if(ret != RB_SCSI_OK) 416 continue; // ignore device 417 if(raw.buf_len != (int)sizeof(inquiry_data)) 418 continue; // ignore device (what kind of device would not return all the INQUIRY data?) 419 420 /* fill device details */ 421 dev = realloc(dev, (2 + nr_dev) * sizeof(struct rb_scsi_devent_t)); 422 dev[nr_dev].scsi_path = strdup(path); 423 dev[nr_dev].block_path = strdup(path); 424 /* fill vendor/model/rev */ 425 char info[17]; 426 snprintf(info, sizeof(info), "%.8s", inquiry_data + 8); 427 dev[nr_dev].vendor = strdup(info); 428 snprintf(info, sizeof(info), "%.16s", inquiry_data + 16); 429 dev[nr_dev].model = strdup(info); 430 snprintf(info, sizeof(info), "%.4s", inquiry_data + 32); 431 dev[nr_dev].rev = strdup(info); 432 433 /* sentinel */ 434 dev[++nr_dev].scsi_path = NULL; 435 dev[nr_dev].block_path = NULL; 436 } 437 438 return dev; 439} 440/* other targets */ 441#else 442rb_scsi_device_t rb_scsi_open(const char *path, unsigned flags, void *user, 443 rb_scsi_printf_t printf) 444{ 445 if(printf == NULL) 446 printf = misc_std_printf; 447 (void) path; 448 if(flags & RB_SCSI_DEBUG) 449 printf(user, "rb_scsi: unimplemented on this platform\n"); 450 return NULL; 451} 452 453int rb_scsi_raw_xfer(rb_scsi_device_t dev, struct rb_scsi_raw_cmd_t *raw) 454{ 455 return RB_SCSI_ERROR; 456} 457 458void rb_scsi_close(rb_scsi_device_t dev) 459{ 460 free(dev); 461} 462 463struct rb_scsi_devent_t *rb_scsi_list(void) 464{ 465 /* unimplemented */ 466 struct rb_scsi_devent_t *dev = malloc(sizeof(struct rb_scsi_devent_t)); 467 dev[0].scsi_path = NULL; 468 dev[0].block_path = NULL; 469 return dev; 470} 471#endif 472 473void rb_scsi_decode_sense(rb_scsi_device_t dev, void *_sense, int sense_len) 474{ 475#define rb_printf(...) \ 476 do{ if(dev->flags & RB_SCSI_DEBUG) dev->printf(dev->user, __VA_ARGS__); }while(0) 477 478 uint8_t *sense = _sense; 479 uint8_t type = sense[0] & 0x7f; 480 switch(type) 481 { 482 case 0x70: case 0x73: rb_printf("Current sense: "); break; 483 case 0x71: case 0x72: rb_printf("Previous sense: "); break; 484 default: rb_printf("Unknown sense\n"); return; 485 } 486 unsigned key = sense[2] & 0xf; 487 switch(key) 488 { 489 case 0: rb_printf("no sense"); break; 490 case 1: rb_printf("recovered error"); break; 491 case 2: rb_printf("not ready"); break; 492 case 3: rb_printf("medium error"); break; 493 case 4: rb_printf("hardware error"); break; 494 case 5: rb_printf("illegal request"); break; 495 case 6: rb_printf("unit attention"); break; 496 case 7: rb_printf("data protect"); break; 497 case 8: rb_printf("blank check"); break; 498 case 9: rb_printf("vendor specific"); break; 499 case 10: rb_printf("copy aborted"); break; 500 case 11: rb_printf("aborted command"); break; 501 default: rb_printf("unknown key"); break; 502 } 503 rb_printf("\n"); 504 505#undef rb_printf 506} 507 508void rb_scsi_free_list(struct rb_scsi_devent_t *list) 509{ 510 if(list == NULL) 511 return; 512 for(struct rb_scsi_devent_t *p = list; p->scsi_path; p++) 513 { 514 free(p->scsi_path); 515 if(p->block_path) 516 free(p->block_path); 517 if(p->vendor) 518 free(p->vendor); 519 if(p->model) 520 free(p->model); 521 if(p->rev) 522 free(p->rev); 523 } 524 free(list); 525}