1 /* 2 * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>. 3 * Based on the non-X based CD player by Jean-Marc Zucconi and 4 * Andrey A. Chernov. 5 * 6 * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>. 7 * 8 * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 9 * A couple of further fixes to my own earlier "fixes". 10 * 11 * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 12 * Added an ability to specify addresses relative to the 13 * beginning of a track. This is in fact a variation of 14 * doing the simple play_msf() call. 15 * 16 * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru> 17 * New eject algorithm. 18 * Some code style reformatting. 19 */ 20 21 #ifndef lint 22 static const char rcsid[] = 23 "$FreeBSD$"; 24 #endif /* not lint */ 25 26 #include <ctype.h> 27 #include <err.h> 28 #include <errno.h> 29 #include <stdio.h> 30 #include <stdlib.h> 31 #include <string.h> 32 #include <unistd.h> 33 #include <sys/file.h> 34 #include <sys/cdio.h> 35 #include <sys/ioctl.h> 36 #include <sys/param.h> 37 #include <histedit.h> 38 39 #define VERSION "2.0" 40 41 #define ASTS_INVALID 0x00 /* Audio status byte not valid */ 42 #define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 43 #define ASTS_PAUSED 0x12 /* Audio play operation paused */ 44 #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 45 #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 46 #define ASTS_VOID 0x15 /* No current audio status to return */ 47 48 #ifndef DEFAULT_CD_DRIVE 49 # define DEFAULT_CD_DRIVE "/dev/cd0c" 50 #endif 51 52 #ifndef DEFAULT_CD_PARTITION 53 # define DEFAULT_CD_PARTITION "c" 54 #endif 55 56 #define CMD_DEBUG 1 57 #define CMD_EJECT 2 58 #define CMD_HELP 3 59 #define CMD_INFO 4 60 #define CMD_PAUSE 5 61 #define CMD_PLAY 6 62 #define CMD_QUIT 7 63 #define CMD_RESUME 8 64 #define CMD_STOP 9 65 #define CMD_VOLUME 10 66 #define CMD_CLOSE 11 67 #define CMD_RESET 12 68 #define CMD_SET 13 69 #define CMD_STATUS 14 70 #define STATUS_AUDIO 0x1 71 #define STATUS_MEDIA 0x2 72 #define STATUS_VOLUME 0x4 73 74 struct cmdtab { 75 int command; 76 char *name; 77 unsigned min; 78 char *args; 79 } cmdtab[] = { 80 { CMD_CLOSE, "close", 1, "" }, 81 { CMD_DEBUG, "debug", 1, "on | off" }, 82 { CMD_EJECT, "eject", 1, "" }, 83 { CMD_HELP, "?", 1, 0 }, 84 { CMD_HELP, "help", 1, "" }, 85 { CMD_INFO, "info", 1, "" }, 86 { CMD_PAUSE, "pause", 2, "" }, 87 { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 88 { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 89 { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 90 { CMD_PLAY, "play", 1, "[#block [len]]" }, 91 { CMD_QUIT, "quit", 1, "" }, 92 { CMD_RESET, "reset", 4, "" }, 93 { CMD_RESUME, "resume", 1, "" }, 94 { CMD_SET, "set", 2, "msf | lba" }, 95 { CMD_STATUS, "status", 1, "[audio | media | volume]" }, 96 { CMD_STOP, "stop", 3, "" }, 97 { CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" }, 98 { 0, } 99 }; 100 101 struct cd_toc_entry toc_buffer[100]; 102 103 const char *cdname; 104 int fd = -1; 105 int verbose = 1; 106 int msf = 1; 107 108 int setvol __P((int, int)); 109 int read_toc_entrys __P((int)); 110 int play_msf __P((int, int, int, int, int, int)); 111 int play_track __P((int, int, int, int)); 112 int get_vol __P((int *, int *)); 113 int status __P((int *, int *, int *, int *)); 114 int open_cd __P((void)); 115 int play __P((char *arg)); 116 int info __P((char *arg)); 117 int pstatus __P((char *arg)); 118 char *input __P((int *)); 119 void prtrack __P((struct cd_toc_entry *e, int lastflag)); 120 void lba2msf __P((unsigned long lba, 121 u_char *m, u_char *s, u_char *f)); 122 unsigned int msf2lba __P((u_char m, u_char s, u_char f)); 123 int play_blocks __P((int blk, int len)); 124 int run __P((int cmd, char *arg)); 125 char *parse __P((char *buf, int *cmd)); 126 127 void help () 128 { 129 struct cmdtab *c; 130 char *s, n; 131 int i; 132 133 for (c=cmdtab; c->name; ++c) { 134 if (! c->args) 135 continue; 136 printf("\t"); 137 for (i = c->min, s = c->name; *s; s++, i--) { 138 if (i > 0) 139 n = toupper(*s); 140 else 141 n = *s; 142 putchar(n); 143 } 144 if (*c->args) 145 printf (" %s", c->args); 146 printf ("\n"); 147 } 148 printf ("\n\tThe word \"play\" is not required for the play commands.\n"); 149 printf ("\tThe plain target address is taken as a synonym for play.\n"); 150 } 151 152 void usage () 153 { 154 fprintf (stderr, "usage: cdcontrol [-sv] [-f device] [command ...]\n"); 155 exit (1); 156 } 157 158 int main (int argc, char **argv) 159 { 160 int cmd; 161 char *arg; 162 163 cdname = getenv ("MUSIC_CD"); 164 if (! cdname) 165 cdname = getenv ("CD_DRIVE"); 166 if (! cdname) 167 cdname = getenv ("DISC"); 168 if (! cdname) 169 cdname = getenv ("CDPLAY"); 170 171 for (;;) { 172 switch (getopt (argc, argv, "svhf:")) { 173 case EOF: 174 break; 175 case 's': 176 verbose = 0; 177 continue; 178 case 'v': 179 verbose = 2; 180 continue; 181 case 'f': 182 cdname = optarg; 183 continue; 184 case 'h': 185 default: 186 usage (); 187 } 188 break; 189 } 190 argc -= optind; 191 argv += optind; 192 193 if (argc > 0 && ! strcasecmp (*argv, "help")) 194 usage (); 195 196 if (! cdname) { 197 cdname = DEFAULT_CD_DRIVE; 198 warnx("no CD device name specified, defaulting to %s", cdname); 199 } 200 201 if (argc > 0) { 202 char buf[80], *p; 203 int len; 204 205 for (p=buf; argc-->0; ++argv) { 206 len = strlen (*argv); 207 208 if (p + len >= buf + sizeof (buf) - 1) 209 usage (); 210 211 if (p > buf) 212 *p++ = ' '; 213 214 strcpy (p, *argv); 215 p += len; 216 } 217 *p = 0; 218 arg = parse (buf, &cmd); 219 return (run (cmd, arg)); 220 } 221 222 if (verbose == 1) 223 verbose = isatty (0); 224 225 if (verbose) { 226 printf ("Compact Disc Control utility, version %s\n", VERSION); 227 printf ("Type `?' for command list\n\n"); 228 } 229 230 for (;;) { 231 arg = input (&cmd); 232 if (run (cmd, arg) < 0) { 233 if (verbose) 234 warn(NULL); 235 close (fd); 236 fd = -1; 237 } 238 fflush (stdout); 239 } 240 } 241 242 int run (int cmd, char *arg) 243 { 244 int l, r, rc; 245 246 if (arg == NULL) 247 return 0; 248 switch (cmd) { 249 250 case CMD_QUIT: 251 exit (0); 252 253 case CMD_INFO: 254 if (fd < 0 && ! open_cd ()) 255 return (0); 256 257 return info (arg); 258 259 case CMD_STATUS: 260 if (fd < 0 && ! open_cd ()) 261 return (0); 262 263 return pstatus (arg); 264 265 case CMD_PAUSE: 266 if (fd < 0 && ! open_cd ()) 267 return (0); 268 269 return ioctl (fd, CDIOCPAUSE); 270 271 case CMD_RESUME: 272 if (fd < 0 && ! open_cd ()) 273 return (0); 274 275 return ioctl (fd, CDIOCRESUME); 276 277 case CMD_STOP: 278 if (fd < 0 && ! open_cd ()) 279 return (0); 280 281 rc = ioctl (fd, CDIOCSTOP); 282 283 (void) ioctl (fd, CDIOCALLOW); 284 285 return (rc); 286 287 case CMD_RESET: 288 if (fd < 0 && ! open_cd ()) 289 return (0); 290 291 rc = ioctl (fd, CDIOCRESET); 292 if (rc < 0) 293 return rc; 294 close(fd); 295 fd = -1; 296 return (0); 297 298 case CMD_DEBUG: 299 if (fd < 0 && ! open_cd ()) 300 return (0); 301 302 if (! strcasecmp (arg, "on")) 303 return ioctl (fd, CDIOCSETDEBUG); 304 305 if (! strcasecmp (arg, "off")) 306 return ioctl (fd, CDIOCCLRDEBUG); 307 308 warnx("invalid command arguments"); 309 310 return (0); 311 312 case CMD_EJECT: 313 if (fd < 0 && ! open_cd ()) 314 return (0); 315 316 (void) ioctl (fd, CDIOCALLOW); 317 rc = ioctl (fd, CDIOCEJECT); 318 if (rc < 0) 319 return (rc); 320 return (0); 321 322 case CMD_CLOSE: 323 if (fd < 0 && ! open_cd ()) 324 return (0); 325 326 (void) ioctl (fd, CDIOCALLOW); 327 rc = ioctl (fd, CDIOCCLOSE); 328 if (rc < 0) 329 return (rc); 330 close(fd); 331 fd = -1; 332 return (0); 333 334 case CMD_PLAY: 335 if (fd < 0 && ! open_cd ()) 336 return (0); 337 338 while (isspace (*arg)) 339 arg++; 340 341 return play (arg); 342 343 case CMD_SET: 344 if (! strcasecmp (arg, "msf")) 345 msf = 1; 346 else if (! strcasecmp (arg, "lba")) 347 msf = 0; 348 else 349 warnx("invalid command arguments"); 350 return (0); 351 352 case CMD_VOLUME: 353 if (fd < 0 && !open_cd ()) 354 return (0); 355 356 if (! strncasecmp (arg, "left", strlen(arg))) 357 return ioctl (fd, CDIOCSETLEFT); 358 359 if (! strncasecmp (arg, "right", strlen(arg))) 360 return ioctl (fd, CDIOCSETRIGHT); 361 362 if (! strncasecmp (arg, "mono", strlen(arg))) 363 return ioctl (fd, CDIOCSETMONO); 364 365 if (! strncasecmp (arg, "stereo", strlen(arg))) 366 return ioctl (fd, CDIOCSETSTERIO); 367 368 if (! strncasecmp (arg, "mute", strlen(arg))) 369 return ioctl (fd, CDIOCSETMUTE); 370 371 if (2 != sscanf (arg, "%d %d", &l, &r)) { 372 warnx("invalid command arguments"); 373 return (0); 374 } 375 376 return setvol (l, r); 377 378 default: 379 case CMD_HELP: 380 help (); 381 return (0); 382 383 } 384 } 385 386 int play (char *arg) 387 { 388 struct ioc_toc_header h; 389 int rc, n, start, end = 0, istart = 1, iend = 1; 390 391 rc = ioctl (fd, CDIOREADTOCHEADER, &h); 392 393 if (rc < 0) 394 return (rc); 395 396 n = h.ending_track - h.starting_track + 1; 397 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 398 399 if (rc < 0) 400 return (rc); 401 402 if (! arg || ! *arg) { 403 /* Play the whole disc */ 404 if (msf) 405 return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, 406 toc_buffer[n].addr.msf.second, 407 toc_buffer[n].addr.msf.frame)); 408 else 409 return play_blocks (0, ntohl(toc_buffer[n].addr.lba)); 410 } 411 412 if (strchr (arg, '#')) { 413 /* Play block #blk [ len ] */ 414 int blk, len = 0; 415 416 if (2 != sscanf (arg, "#%d%d", &blk, &len) && 417 1 != sscanf (arg, "#%d", &blk)) 418 goto Clean_up; 419 420 if (len == 0) { 421 if (msf) 422 len = msf2lba (toc_buffer[n].addr.msf.minute, 423 toc_buffer[n].addr.msf.second, 424 toc_buffer[n].addr.msf.frame) - blk; 425 else 426 len = ntohl(toc_buffer[n].addr.lba) - blk; 427 } 428 return play_blocks (blk, len); 429 } 430 431 if (strchr (arg, ':')) { 432 /* 433 * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 434 * 435 * Will now also undestand timed addresses relative 436 * to the beginning of a track in the form... 437 * 438 * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 439 */ 440 unsigned tr1, tr2; 441 unsigned m1, m2, s1, s2, f1, f2; 442 unsigned char tm, ts, tf; 443 444 tr2 = m2 = s2 = f2 = f1 = 0; 445 if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", 446 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 447 goto Play_Relative_Addresses; 448 449 tr2 = m2 = s2 = f2 = f1 = 0; 450 if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", 451 &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 452 goto Play_Relative_Addresses; 453 454 tr2 = m2 = s2 = f2 = f1 = 0; 455 if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", 456 &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 457 goto Play_Relative_Addresses; 458 459 tr2 = m2 = s2 = f2 = f1 = 0; 460 if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", 461 &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 462 goto Play_Relative_Addresses; 463 464 tr2 = m2 = s2 = f2 = f1 = 0; 465 if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", 466 &tr1, &m1, &s1, &f1, &m2, &s2)) 467 goto Play_Relative_Addresses; 468 469 tr2 = m2 = s2 = f2 = f1 = 0; 470 if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", 471 &tr1, &m1, &s1, &m2, &s2, &f2)) 472 goto Play_Relative_Addresses; 473 474 tr2 = m2 = s2 = f2 = f1 = 0; 475 if (6 == sscanf (arg, "%d %d:%d.%d %d %d", 476 &tr1, &m1, &s1, &f1, &tr2, &m2)) 477 goto Play_Relative_Addresses; 478 479 tr2 = m2 = s2 = f2 = f1 = 0; 480 if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 481 goto Play_Relative_Addresses; 482 483 tr2 = m2 = s2 = f2 = f1 = 0; 484 if (5 == sscanf (arg, "%d %d:%d %d %d", 485 &tr1, &m1, &s1, &tr2, &m2)) 486 goto Play_Relative_Addresses; 487 488 tr2 = m2 = s2 = f2 = f1 = 0; 489 if (5 == sscanf (arg, "%d %d:%d.%d %d", 490 &tr1, &m1, &s1, &f1, &tr2)) 491 goto Play_Relative_Addresses; 492 493 tr2 = m2 = s2 = f2 = f1 = 0; 494 if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 495 goto Play_Relative_Addresses; 496 497 tr2 = m2 = s2 = f2 = f1 = 0; 498 if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 499 goto Play_Relative_Addresses; 500 501 tr2 = m2 = s2 = f2 = f1 = 0; 502 if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) 503 goto Play_Relative_Addresses; 504 505 tr2 = m2 = s2 = f2 = f1 = 0; 506 goto Try_Absolute_Timed_Addresses; 507 508 Play_Relative_Addresses: 509 if (! tr1) 510 tr1 = 1; 511 else if (tr1 > n) 512 tr1 = n; 513 514 if (msf) { 515 tm = toc_buffer[tr1].addr.msf.minute; 516 ts = toc_buffer[tr1].addr.msf.second; 517 tf = toc_buffer[tr1].addr.msf.frame; 518 } else 519 lba2msf(ntohl(toc_buffer[tr1].addr.lba), 520 &tm, &ts, &tf); 521 if ((m1 > tm) 522 || ((m1 == tm) 523 && ((s1 > ts) 524 || ((s1 == ts) 525 && (f1 > tf))))) { 526 printf ("Track %d is not that long.\n", tr1); 527 return (0); 528 } 529 530 tr1--; 531 532 f1 += tf; 533 if (f1 >= 75) { 534 s1 += f1 / 75; 535 f1 %= 75; 536 } 537 538 s1 += ts; 539 if (s1 >= 60) { 540 m1 += s1 / 60; 541 s1 %= 60; 542 } 543 544 m1 += tm; 545 546 if (! tr2) { 547 if (m2 || s2 || f2) { 548 tr2 = tr1; 549 f2 += f1; 550 if (f2 >= 75) { 551 s2 += f2 / 75; 552 f2 %= 75; 553 } 554 555 s2 += s1; 556 if (s2 > 60) { 557 m2 += s2 / 60; 558 s2 %= 60; 559 } 560 561 m2 += m1; 562 } else { 563 tr2 = n; 564 if (msf) { 565 m2 = toc_buffer[n].addr.msf.minute; 566 s2 = toc_buffer[n].addr.msf.second; 567 f2 = toc_buffer[n].addr.msf.frame; 568 } else { 569 lba2msf(ntohl(toc_buffer[n].addr.lba), 570 &tm, &ts, &tf); 571 m2 = tm; 572 s2 = ts; 573 f2 = tf; 574 } 575 } 576 } else if (tr2 > n) { 577 tr2 = n; 578 m2 = s2 = f2 = 0; 579 } else { 580 if (m2 || s2 || f2) 581 tr2--; 582 if (msf) { 583 tm = toc_buffer[tr2].addr.msf.minute; 584 ts = toc_buffer[tr2].addr.msf.second; 585 tf = toc_buffer[tr2].addr.msf.frame; 586 } else 587 lba2msf(ntohl(toc_buffer[tr2].addr.lba), 588 &tm, &ts, &tf); 589 f2 += tf; 590 if (f2 >= 75) { 591 s2 += f2 / 75; 592 f2 %= 75; 593 } 594 595 s2 += ts; 596 if (s2 > 60) { 597 m2 += s2 / 60; 598 s2 %= 60; 599 } 600 601 m2 += tm; 602 } 603 604 if (msf) { 605 tm = toc_buffer[n].addr.msf.minute; 606 ts = toc_buffer[n].addr.msf.second; 607 tf = toc_buffer[n].addr.msf.frame; 608 } else 609 lba2msf(ntohl(toc_buffer[n].addr.lba), 610 &tm, &ts, &tf); 611 if ((tr2 < n) 612 && ((m2 > tm) 613 || ((m2 == tm) 614 && ((s2 > ts) 615 || ((s2 == ts) 616 && (f2 > tf)))))) { 617 printf ("The playing time of the disc is not that long.\n"); 618 return (0); 619 } 620 return (play_msf (m1, s1, f1, m2, s2, f2)); 621 622 Try_Absolute_Timed_Addresses: 623 if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", 624 &m1, &s1, &f1, &m2, &s2, &f2) && 625 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 626 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 627 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 628 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 629 2 != sscanf (arg, "%d:%d", &m1, &s1)) 630 goto Clean_up; 631 632 if (m2 == 0) { 633 if (msf) { 634 m2 = toc_buffer[n].addr.msf.minute; 635 s2 = toc_buffer[n].addr.msf.second; 636 f2 = toc_buffer[n].addr.msf.frame; 637 } else { 638 lba2msf(ntohl(toc_buffer[n].addr.lba), 639 &tm, &ts, &tf); 640 m2 = tm; 641 s2 = ts; 642 f2 = tf; 643 } 644 } 645 return play_msf (m1, s1, f1, m2, s2, f2); 646 } 647 648 /* 649 * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 650 */ 651 if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 652 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 653 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 654 2 != sscanf (arg, "%d.%d", &start, &istart) && 655 2 != sscanf (arg, "%d%d", &start, &end) && 656 1 != sscanf (arg, "%d", &start)) 657 goto Clean_up; 658 659 if (end == 0) 660 end = n; 661 return (play_track (start, istart, end, iend)); 662 663 Clean_up: 664 warnx("invalid command arguments"); 665 return (0); 666 } 667 668 char *strstatus (int sts) 669 { 670 switch (sts) { 671 case ASTS_INVALID: return ("invalid"); 672 case ASTS_PLAYING: return ("playing"); 673 case ASTS_PAUSED: return ("paused"); 674 case ASTS_COMPLETED: return ("completed"); 675 case ASTS_ERROR: return ("error"); 676 case ASTS_VOID: return ("void"); 677 default: return ("??"); 678 } 679 } 680 681 int pstatus (char *arg) 682 { 683 struct ioc_vol v; 684 struct ioc_read_subchannel ss; 685 struct cd_sub_channel_info data; 686 int rc, trk, m, s, f; 687 int what = 0; 688 char *p; 689 690 while ((p = strtok(arg, " \t"))) { 691 arg = 0; 692 if (!strncasecmp(p, "audio", strlen(p))) 693 what |= STATUS_AUDIO; 694 else if (!strncasecmp(p, "media", strlen(p))) 695 what |= STATUS_MEDIA; 696 else if (!strncasecmp(p, "volume", strlen(p))) 697 what |= STATUS_VOLUME; 698 else { 699 warnx("invalid command arguments"); 700 return 0; 701 } 702 } 703 if (!what) 704 what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME; 705 if (what & STATUS_AUDIO) { 706 rc = status (&trk, &m, &s, &f); 707 if (rc >= 0) 708 if (verbose) 709 printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", 710 rc, strstatus (rc), trk, m, s, f); 711 else 712 printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 713 else 714 printf ("No current status info available\n"); 715 } 716 if (what & STATUS_MEDIA) { 717 bzero (&ss, sizeof (ss)); 718 ss.data = &data; 719 ss.data_len = sizeof (data); 720 ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 721 ss.data_format = CD_MEDIA_CATALOG; 722 rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); 723 if (rc >= 0) { 724 printf("Media catalog is %sactive", 725 ss.data->what.media_catalog.mc_valid ? "": "in"); 726 if (ss.data->what.media_catalog.mc_valid && 727 ss.data->what.media_catalog.mc_number[0]) 728 printf(", number \"%.15s\"", 729 ss.data->what.media_catalog.mc_number); 730 putchar('\n'); 731 } else 732 printf("No media catalog info available\n"); 733 } 734 if (what & STATUS_VOLUME) { 735 rc = ioctl (fd, CDIOCGETVOL, &v); 736 if (rc >= 0) 737 if (verbose) 738 printf ("Left volume = %d, right volume = %d\n", 739 v.vol[0], v.vol[1]); 740 else 741 printf ("%d %d\n", v.vol[0], v.vol[1]); 742 else 743 printf ("No volume level info available\n"); 744 } 745 return(0); 746 } 747 748 int info (char *arg) 749 { 750 struct ioc_toc_header h; 751 int rc, i, n; 752 753 rc = ioctl (fd, CDIOREADTOCHEADER, &h); 754 if (rc >= 0) { 755 if (verbose) 756 printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 757 h.starting_track, h.ending_track, h.len); 758 else 759 printf ("%d %d %d\n", h.starting_track, 760 h.ending_track, h.len); 761 } else { 762 warn("getting toc header"); 763 return (rc); 764 } 765 766 n = h.ending_track - h.starting_track + 1; 767 rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 768 if (rc < 0) 769 return (rc); 770 771 if (verbose) { 772 printf ("track start duration block length type\n"); 773 printf ("-------------------------------------------------\n"); 774 } 775 776 for (i = 0; i < n; i++) { 777 printf ("%5d ", toc_buffer[i].track); 778 prtrack (toc_buffer + i, 0); 779 } 780 printf ("%5d ", toc_buffer[n].track); 781 prtrack (toc_buffer + n, 1); 782 return (0); 783 } 784 785 void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) 786 { 787 lba += 150; /* block start offset */ 788 lba &= 0xffffff; /* negative lbas use only 24 bits */ 789 *m = lba / (60 * 75); 790 lba %= (60 * 75); 791 *s = lba / 75; 792 *f = lba % 75; 793 } 794 795 unsigned int msf2lba (u_char m, u_char s, u_char f) 796 { 797 return (((m * 60) + s) * 75 + f) - 150; 798 } 799 800 void prtrack (struct cd_toc_entry *e, int lastflag) 801 { 802 int block, next, len; 803 u_char m, s, f; 804 805 if (msf) { 806 /* Print track start */ 807 printf ("%2d:%02d.%02d ", e->addr.msf.minute, 808 e->addr.msf.second, e->addr.msf.frame); 809 810 block = msf2lba (e->addr.msf.minute, e->addr.msf.second, 811 e->addr.msf.frame); 812 } else { 813 block = ntohl(e->addr.lba); 814 lba2msf(block, &m, &s, &f); 815 /* Print track start */ 816 printf ("%2d:%02d.%02d ", m, s, f); 817 } 818 if (lastflag) { 819 /* Last track -- print block */ 820 printf (" - %6d - -\n", block); 821 return; 822 } 823 824 if (msf) 825 next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, 826 e[1].addr.msf.frame); 827 else 828 next = ntohl(e[1].addr.lba); 829 len = next - block; 830 lba2msf (len, &m, &s, &f); 831 832 /* Print duration, block, length, type */ 833 printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 834 (e->control & 4) ? "data" : "audio"); 835 } 836 837 int play_track (int tstart, int istart, int tend, int iend) 838 { 839 struct ioc_play_track t; 840 841 t.start_track = tstart; 842 t.start_index = istart; 843 t.end_track = tend; 844 t.end_index = iend; 845 846 return ioctl (fd, CDIOCPLAYTRACKS, &t); 847 } 848 849 int play_blocks (int blk, int len) 850 { 851 struct ioc_play_blocks t; 852 853 t.blk = blk; 854 t.len = len; 855 856 return ioctl (fd, CDIOCPLAYBLOCKS, &t); 857 } 858 859 int setvol (int left, int right) 860 { 861 struct ioc_vol v; 862 863 v.vol[0] = left; 864 v.vol[1] = right; 865 v.vol[2] = 0; 866 v.vol[3] = 0; 867 868 return ioctl (fd, CDIOCSETVOL, &v); 869 } 870 871 int read_toc_entrys (int len) 872 { 873 struct ioc_read_toc_entry t; 874 875 t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 876 t.starting_track = 0; 877 t.data_len = len; 878 t.data = toc_buffer; 879 880 return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); 881 } 882 883 int play_msf (int start_m, int start_s, int start_f, 884 int end_m, int end_s, int end_f) 885 { 886 struct ioc_play_msf a; 887 888 a.start_m = start_m; 889 a.start_s = start_s; 890 a.start_f = start_f; 891 a.end_m = end_m; 892 a.end_s = end_s; 893 a.end_f = end_f; 894 895 return ioctl (fd, CDIOCPLAYMSF, (char *) &a); 896 } 897 898 int status (int *trk, int *min, int *sec, int *frame) 899 { 900 struct ioc_read_subchannel s; 901 struct cd_sub_channel_info data; 902 u_char mm, ss, ff; 903 904 bzero (&s, sizeof (s)); 905 s.data = &data; 906 s.data_len = sizeof (data); 907 s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 908 s.data_format = CD_CURRENT_POSITION; 909 910 if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 911 return -1; 912 913 *trk = s.data->what.position.track_number; 914 if (msf) { 915 *min = s.data->what.position.reladdr.msf.minute; 916 *sec = s.data->what.position.reladdr.msf.second; 917 *frame = s.data->what.position.reladdr.msf.frame; 918 } else { 919 lba2msf(ntohl(s.data->what.position.reladdr.lba), 920 &mm, &ss, &ff); 921 *min = mm; 922 *sec = ss; 923 *frame = ff; 924 } 925 926 return s.data->header.audio_status; 927 } 928 929 const char * 930 cdcontrol_prompt() 931 { 932 return ("cdcontrol> "); 933 } 934 935 char * 936 input (int *cmd) 937 { 938 #define MAXLINE 80 939 static EditLine *el = NULL; 940 static History *hist = NULL; 941 static char buf[MAXLINE]; 942 int num = 0; 943 int len; 944 const char *bp = NULL; 945 char *p; 946 947 do { 948 if (verbose) { 949 if (!el) { 950 el = el_init("cdcontrol", stdin, stdout); 951 hist = history_init(); 952 history(hist, H_EVENT, 100); 953 el_set(el, EL_HIST, history, hist); 954 el_set(el, EL_EDITOR, "emacs"); 955 el_set(el, EL_PROMPT, cdcontrol_prompt); 956 el_set(el, EL_SIGNAL, 1); 957 el_source(el, NULL); 958 } 959 if ((bp = el_gets(el, &num)) == NULL || num == 0) 960 return (0); 961 962 len = (num > MAXLINE) ? MAXLINE : num; 963 memcpy(buf, bp, len); 964 buf[len] = 0; 965 history(hist, H_ENTER, bp); 966 #undef MAXLINE 967 968 } else { 969 if (! fgets (buf, sizeof (buf), stdin)) { 970 *cmd = CMD_QUIT; 971 fprintf (stderr, "\r\n"); 972 return (0); 973 } 974 } 975 p = parse (buf, cmd); 976 } while (! p); 977 return (p); 978 } 979 980 char *parse (char *buf, int *cmd) 981 { 982 struct cmdtab *c; 983 char *p; 984 int len; 985 986 for (p=buf; isspace (*p); p++) 987 continue; 988 989 if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { 990 *cmd = CMD_PLAY; 991 return (p); 992 } 993 994 for (buf = p; *p && ! isspace (*p); p++) 995 continue; 996 997 len = p - buf; 998 if (! len) 999 return (0); 1000 1001 if (*p) { /* It must be a spacing character! */ 1002 char *q; 1003 1004 *p++ = 0; 1005 for (q=p; *q && *q != '\n' && *q != '\r'; q++) 1006 continue; 1007 *q = 0; 1008 } 1009 1010 *cmd = -1; 1011 for (c=cmdtab; c->name; ++c) { 1012 /* Is it an exact match? */ 1013 if (! strcasecmp (buf, c->name)) { 1014 *cmd = c->command; 1015 break; 1016 } 1017 1018 /* Try short hand forms then... */ 1019 if (len >= c->min && ! strncasecmp (buf, c->name, len)) { 1020 if (*cmd != -1 && *cmd != c->command) { 1021 warnx("ambiguous command"); 1022 return (0); 1023 } 1024 *cmd = c->command; 1025 } 1026 } 1027 1028 if (*cmd == -1) { 1029 warnx("invalid command, enter ``help'' for commands"); 1030 return (0); 1031 } 1032 1033 while (isspace (*p)) 1034 p++; 1035 return p; 1036 } 1037 1038 int open_cd () 1039 { 1040 char devbuf[MAXPATHLEN]; 1041 1042 if (fd > -1) 1043 return (1); 1044 1045 if (*cdname == '/') { 1046 snprintf (devbuf, MAXPATHLEN, "%s", cdname); 1047 } else if (*cdname == 'r') { 1048 snprintf (devbuf, MAXPATHLEN, "/dev/%s", cdname); 1049 } else { 1050 snprintf (devbuf, MAXPATHLEN, "/dev/r%s", cdname); 1051 } 1052 1053 fd = open (devbuf, O_RDONLY); 1054 1055 if (fd < 0 && errno == ENOENT) { 1056 strcat (devbuf, DEFAULT_CD_PARTITION); 1057 fd = open (devbuf, O_RDONLY); 1058 } 1059 1060 if (fd < 0) { 1061 if (errno == ENXIO) { 1062 /* ENXIO has an overloaded meaning here. 1063 * The original "Device not configured" should 1064 * be interpreted as "No disc in drive %s". */ 1065 warnx("no disc in drive %s", devbuf); 1066 return (0); 1067 } 1068 err(1, "%s", devbuf); 1069 } 1070 return (1); 1071 } 1072