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