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