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