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