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