A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita audio rust zig deno mpris rockbox mpd
at master 496 lines 13 kB view raw
1/*************************************************************************** 2 * __________ __ ___. 3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___ 4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / 5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < 6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ 7 * \/ \/ \/ \/ \/ 8 * $Id$ 9 * 10 * Copyright (C) 2007 Jens Arnold 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 22#include "plugin.h" 23#include "lib/helper.h" 24 25 26 27#define TESTBASEDIR HOME_DIR "/__TEST__" 28#define TEST_FILE TESTBASEDIR "/test_disk.tmp" 29#define FRND_SEED 0x78C3 /* arbirary */ 30 31#if (CONFIG_STORAGE & STORAGE_MMC) 32#define TEST_SIZE (20*1024*1024) 33#else 34#define TEST_SIZE (300*1024*1024) 35#endif 36#define TEST_TIME 10 /* in seconds */ 37 38static unsigned char* audiobuf; 39static size_t audiobuflen; 40 41static unsigned short frnd_buffer; 42static int line = 0; 43static int max_line = 0; 44static int log_fd; 45static char logfilename[MAX_PATH]; 46static const char testbasedir[] = TESTBASEDIR; 47 48static void mem_fill_frnd(unsigned char *addr, int len) 49{ 50 unsigned char *end = addr + len; 51 unsigned random = frnd_buffer; 52 53 while (addr < end) 54 { 55 random = 75 * random + 74; 56 *addr++ = random >> 8; 57 } 58 frnd_buffer = random; 59} 60 61static bool mem_cmp_frnd(unsigned char *addr, int len) 62{ 63 unsigned char *end = addr + len; 64 unsigned random = frnd_buffer; 65 66 while (addr < end) 67 { 68 random = 75 * random + 74; 69 if (*addr++ != ((random >> 8) & 0xff)) 70 return false; 71 } 72 frnd_buffer = random; 73 return true; 74} 75 76static bool log_init(void) 77{ 78 int h; 79 80 rb->lcd_getstringsize("A", NULL, &h); 81 max_line = LCD_HEIGHT / h; 82 line = 0; 83 rb->lcd_clear_display(); 84 rb->lcd_update(); 85 86 rb->create_numbered_filename(logfilename, HOME_DIR, "test_disk_log_", ".txt", 87 2 IF_CNFN_NUM_(, NULL)); 88 log_fd = rb->open(logfilename, O_RDWR|O_CREAT|O_TRUNC, 0666); 89 return log_fd >= 0; 90} 91 92static void log_text(char *text, bool advance) 93{ 94 rb->lcd_puts(0, line, text); 95 rb->lcd_update(); 96 if (advance) 97 { 98 if (++line >= max_line) 99 line = 0; 100 rb->fdprintf(log_fd, "%s\n", text); 101 } 102} 103 104static void log_close(void) 105{ 106 rb->close(log_fd); 107} 108 109static bool test_fs(void) 110{ 111 unsigned char text_buf[32]; 112 int total, current, align; 113 int fd, ret; 114 115 log_init(); 116 log_text("test_disk WRITE&VERIFY", true); 117#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 118 rb->snprintf(text_buf, sizeof(text_buf), "CPU clock: %ld Hz", 119 *rb->cpu_frequency); 120 log_text(text_buf, true); 121#endif 122 log_text("----------------------", true); 123 rb->snprintf(text_buf, sizeof text_buf, "Data size: %dKB", (TEST_SIZE>>10)); 124 log_text(text_buf, true); 125 126 fd = rb->creat(TEST_FILE, 0666); 127 if (fd < 0) 128 { 129 rb->splashf(HZ, "creat() failed: %d", fd); 130 goto error; 131 } 132 133 frnd_buffer = FRND_SEED; 134 total = TEST_SIZE; 135 while (total > 0) 136 { 137 align = rb->rand() & 0xf; 138 current = rb->rand() % (audiobuflen - align); 139 current = MIN(current, total); 140 rb->snprintf(text_buf, sizeof text_buf, "Wrt %dKB, %dKB left", 141 current >> 10, total >> 10); 142 log_text(text_buf, false); 143 144 mem_fill_frnd(audiobuf + align, current); 145 ret = rb->write(fd, audiobuf + align, current); 146 if (current != ret) 147 { 148 rb->splashf(0, "write() failed: %d/%d", ret, current); 149 rb->close(fd); 150 goto error; 151 } 152 total -= current; 153 } 154 rb->close(fd); 155 156 fd = rb->open(TEST_FILE, O_RDONLY); 157 if (fd < 0) 158 { 159 rb->splashf(0, "open() failed: %d", fd); 160 goto error; 161 } 162 163 frnd_buffer = FRND_SEED; 164 total = TEST_SIZE; 165 while (total > 0) 166 { 167 align = rb->rand() & 0xf; 168 current = rb->rand() % (audiobuflen - align); 169 current = MIN(current, total); 170 rb->snprintf(text_buf, sizeof text_buf, "Cmp %dKB, %dKB left", 171 current >> 10, total >> 10); 172 log_text(text_buf, false); 173 174 ret = rb->read(fd, audiobuf + align, current); 175 if (current != ret) 176 { 177 rb->splashf(0, "read() failed: %d/%d", ret, current); 178 rb->close(fd); 179 goto error; 180 } 181 if (!mem_cmp_frnd(audiobuf + align, current)) 182 { 183 log_text(text_buf, true); 184 log_text("Compare error.", true); 185 rb->close(fd); 186 goto error; 187 } 188 total -= current; 189 } 190 rb->close(fd); 191 log_text(text_buf, true); 192 log_text("Test passed.", true); 193 194error: 195 log_close(); 196 rb->remove(TEST_FILE); 197 rb->button_clear_queue(); 198 rb->button_get(true); 199 200 return false; 201} 202 203static bool file_speed(int chunksize, bool align) 204{ 205 unsigned char text_buf[64]; 206 int fd, ret; 207 long filesize = 0; 208 long size, time; 209 210 if ((unsigned)chunksize >= audiobuflen) 211 return false; 212 213 log_text("--------------------", true); 214 215 /* File creation write speed */ 216 fd = rb->creat(TEST_FILE, 0666); 217 if (fd < 0) 218 { 219 rb->splashf(HZ, "creat() failed: %d", fd); 220 goto error; 221 } 222 time = *rb->current_tick; 223 while (TIME_BEFORE(*rb->current_tick, time + TEST_TIME*HZ)) 224 { 225 ret = rb->write(fd, audiobuf + (align ? 0 : 1), chunksize); 226 if (chunksize != ret) 227 { 228 rb->splashf(HZ, "write() failed: %d/%d", ret, chunksize); 229 rb->close(fd); 230 goto error; 231 } 232 filesize += chunksize; 233 } 234 time = *rb->current_tick - time; 235 rb->close(fd); 236 rb->snprintf(text_buf, sizeof text_buf, "Create (%d,%c): %ld KB/s", 237 chunksize, align ? 'A' : 'U', (25 * (filesize>>8) / time) ); 238 log_text(text_buf, true); 239 240 /* Existing file write speed */ 241 fd = rb->open(TEST_FILE, O_WRONLY); 242 if (fd < 0) 243 { 244 rb->splashf(0, "open() failed: %d", fd); 245 goto error; 246 } 247 time = *rb->current_tick; 248 for (size = filesize; size > 0; size -= chunksize) 249 { 250 ret = rb->write(fd, audiobuf + (align ? 0 : 1), chunksize); 251 if (chunksize != ret) 252 { 253 rb->splashf(0, "write() failed: %d/%d", ret, chunksize); 254 rb->close(fd); 255 goto error; 256 } 257 } 258 time = *rb->current_tick - time; 259 rb->close(fd); 260 rb->snprintf(text_buf, sizeof text_buf, "Write (%d,%c): %ld KB/s", 261 chunksize, align ? 'A' : 'U', (25 * (filesize>>8) / time) ); 262 log_text(text_buf, true); 263 264 /* File read speed */ 265 fd = rb->open(TEST_FILE, O_RDONLY); 266 if (fd < 0) 267 { 268 rb->splashf(0, "open() failed: %d", fd); 269 goto error; 270 } 271 time = *rb->current_tick; 272 for (size = filesize; size > 0; size -= chunksize) 273 { 274 ret = rb->read(fd, audiobuf + (align ? 0 : 1), chunksize); 275 if (chunksize != ret) 276 { 277 rb->splashf(0, "read() failed: %d/%d", ret, chunksize); 278 rb->close(fd); 279 goto error; 280 } 281 } 282 time = *rb->current_tick - time; 283 rb->close(fd); 284 rb->snprintf(text_buf, sizeof text_buf, "Read (%d,%c): %ld KB/s", 285 chunksize, align ? 'A' : 'U', (25 * (filesize>>8) / time) ); 286 log_text(text_buf, true); 287 rb->remove(TEST_FILE); 288 return true; 289 290 error: 291 rb->remove(TEST_FILE); 292 return false; 293} 294 295static bool test_speed(void) 296{ 297 unsigned char text_buf[64]; 298 DIR *dir = NULL; 299 struct dirent *entry = NULL; 300 int fd, last_file; 301 int i, n; 302 long time; 303 304 rb->memset(audiobuf, 'T', audiobuflen); 305 log_init(); 306 log_text("test_disk SPEED TEST", true); 307#if (CONFIG_PLATFORM & PLATFORM_NATIVE) 308 rb->snprintf(text_buf, sizeof(text_buf), "CPU clock: %ld Hz", 309 *rb->cpu_frequency); 310 log_text(text_buf, true); 311#endif 312 log_text("--------------------", true); 313 314 /* File creation speed */ 315 time = *rb->current_tick + TEST_TIME*HZ; 316 for (i = 0; TIME_BEFORE(*rb->current_tick, time); i++) 317 { 318 rb->snprintf(text_buf, sizeof(text_buf), TESTBASEDIR "/%08x.tmp", i); 319 fd = rb->creat(text_buf, 0666); 320 if (fd < 0) 321 { 322 last_file = i; 323 rb->splashf(HZ, "creat() failed: %d", fd); 324 goto error; 325 } 326 rb->close(fd); 327 } 328 last_file = i; 329 rb->snprintf(text_buf, sizeof(text_buf), "Create: %d files/s", 330 last_file / TEST_TIME); 331 log_text(text_buf, true); 332 333 /* File open speed */ 334 time = *rb->current_tick + TEST_TIME*HZ; 335 for (n = 0, i = 0; TIME_BEFORE(*rb->current_tick, time); n++, i++) 336 { 337 if (i >= last_file) 338 i = 0; 339 rb->snprintf(text_buf, sizeof(text_buf), TESTBASEDIR "/%08x.tmp", i); 340 fd = rb->open(text_buf, O_RDONLY); 341 if (fd < 0) 342 { 343 rb->splashf(HZ, "open() failed: %d", fd); 344 goto error; 345 } 346 rb->close(fd); 347 } 348 rb->snprintf(text_buf, sizeof(text_buf), "Open: %d files/s", n / TEST_TIME); 349 log_text(text_buf, true); 350 351 /* Directory scan speed */ 352 time = *rb->current_tick + TEST_TIME*HZ; 353 for (n = 0; TIME_BEFORE(*rb->current_tick, time); n++) 354 { 355 if (entry == NULL) 356 { 357 if (dir != NULL) 358 rb->closedir(dir); 359 dir = rb->opendir(testbasedir); 360 if (dir == NULL) 361 { 362 rb->splash(HZ, "opendir() failed."); 363 goto error; 364 } 365 } 366 entry = rb->readdir(dir); 367 } 368 rb->closedir(dir); 369 rb->snprintf(text_buf, sizeof(text_buf), "Dirscan: %d files/s", n / TEST_TIME); 370 log_text(text_buf, true); 371 372 dir = NULL; 373 entry = NULL; 374 /* Directory scan speed 2 */ 375 time = *rb->current_tick + TEST_TIME*HZ; 376 for (n = 0; TIME_BEFORE(*rb->current_tick, time); n++) 377 { 378 if (entry == NULL) 379 { 380 if (dir != NULL) 381 rb->closedir(dir); 382 dir = rb->opendir(testbasedir); 383 if (dir == NULL) 384 { 385 rb->splash(HZ, "opendir() failed."); 386 goto error; 387 } 388 } 389 else 390 (void) rb->dir_get_info(dir, entry); 391 entry = rb->readdir(dir); 392 } 393 rb->closedir(dir); 394 rb->snprintf(text_buf, sizeof(text_buf), "Dirscan w info: %d files/s", n / TEST_TIME); 395 log_text(text_buf, true); 396 397 /* File delete speed */ 398 time = *rb->current_tick; 399 for (i = 0; i < last_file; i++) 400 { 401 rb->snprintf(text_buf, sizeof(text_buf), TESTBASEDIR "/%08x.tmp", i); 402 rb->remove(text_buf); 403 } 404 rb->snprintf(text_buf, sizeof(text_buf), "Delete: %ld files/s", 405 last_file * HZ / (*rb->current_tick - time)); 406 log_text(text_buf, true); 407 408 if (file_speed(512, true) 409 && file_speed(512, false) 410 && file_speed(4096, true) 411 && file_speed(4096, false) 412 && file_speed(1048576, true)) 413 file_speed(1048576, false); 414 415 log_text("DONE", false); 416 log_close(); 417 rb->button_clear_queue(); 418 rb->button_get(true); 419 return false; 420 421 error: 422 for (i = 0; i < last_file; i++) 423 { 424 rb->snprintf(text_buf, sizeof(text_buf), TESTBASEDIR "/%08x.tmp", i); 425 rb->remove(text_buf); 426 } 427 log_text("DONE", false); 428 log_close(); 429 rb->button_clear_queue(); 430 rb->button_get(true); 431 return false; 432} 433 434 435/* this is the plugin entry point */ 436enum plugin_status plugin_start(const void* parameter) 437{ 438 MENUITEM_STRINGLIST(menu, "Test Disk Menu", NULL, 439 "Disk speed", "Write & verify"); 440 int selected=0; 441 bool quit = false; 442 DIR *dir; 443 444 (void)parameter; 445 446 if ((dir = rb->opendir(testbasedir)) == NULL) 447 { 448 if (rb->mkdir(testbasedir) < 0) 449 { 450 rb->splash(HZ*2, "Can't create test directory."); 451 return PLUGIN_ERROR; 452 } 453 } 454 else 455 { 456 rb->closedir(dir); 457 } 458 459 audiobuf = rb->plugin_get_audio_buffer(&audiobuflen); 460#ifdef STORAGE_WANTS_ALIGN 461 /* align start and length for DMA */ 462 STORAGE_ALIGN_BUFFER(audiobuf, audiobuflen); 463#else 464 /* align start and length to 32 bit */ 465 ALIGN_BUFFER(audiobuf, audiobuflen, 4); 466#endif 467 468 rb->srand(*rb->current_tick); 469 470 /* Turn off backlight timeout */ 471 backlight_ignore_timeout(); 472 473 474 while(!quit) 475 { 476 switch(rb->do_menu(&menu, &selected, NULL, false)) 477 { 478 case 0: 479 test_speed(); 480 break; 481 case 1: 482 test_fs(); 483 break; 484 default: 485 quit = true; 486 break; 487 } 488 } 489 490 /* Turn on backlight timeout (revert to settings) */ 491 backlight_use_settings(); 492 493 rb->rmdir(testbasedir); 494 495 return PLUGIN_OK; 496}