cdcontrol.c revision 29103
110099Sjkh/* 213884Sache * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>. 310099Sjkh * Based on the non-X based CD player by Jean-Marc Zucconi and 413832Sache * Andrey A. Chernov. 513884Sache * 613985Sache * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>. 713985Sache * 813985Sache * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 913985Sache * A couple of further fixes to my own earlier "fixes". 1013985Sache * 1113985Sache * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi> 1213985Sache * Added an ability to specify addresses relative to the 1313985Sache * beginning of a track. This is in fact a variation of 1413985Sache * doing the simple play_msf() call. 1513985Sache * 1613985Sache * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru> 1713985Sache * New eject algorithm. 1813985Sache * Some code style reformatting. 1910099Sjkh */ 2013985Sache 2129103Scharnier#ifndef lint 2229103Scharnierstatic const char rcsid[] = 2329103Scharnier "$Id$"; 2429103Scharnier#endif /* not lint */ 2529103Scharnier 2613985Sache#include <ctype.h> 2729103Scharnier#include <err.h> 2829103Scharnier#include <errno.h> 2910099Sjkh#include <stdio.h> 3010099Sjkh#include <stdlib.h> 3110099Sjkh#include <string.h> 3210099Sjkh#include <unistd.h> 3310099Sjkh#include <sys/file.h> 3410099Sjkh#include <sys/cdio.h> 3510099Sjkh#include <sys/ioctl.h> 3610099Sjkh 3713884Sache#define VERSION "2.0" 3810099Sjkh 3913985Sache#define ASTS_INVALID 0x00 /* Audio status byte not valid */ 4013985Sache#define ASTS_PLAYING 0x11 /* Audio play operation in progress */ 4113985Sache#define ASTS_PAUSED 0x12 /* Audio play operation paused */ 4213985Sache#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ 4313985Sache#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ 4413985Sache#define ASTS_VOID 0x15 /* No current audio status to return */ 4510099Sjkh 4613985Sache#ifndef DEFAULT_CD_DRIVE 4713985Sache# define DEFAULT_CD_DRIVE "/dev/cd0c" 4813985Sache#endif 4913985Sache 5013985Sache#ifndef DEFAULT_CD_PARTITION 5113985Sache# define DEFAULT_CD_PARTITION "c" 5213985Sache#endif 5313985Sache 5413985Sache#define CMD_DEBUG 1 5513985Sache#define CMD_EJECT 2 5613985Sache#define CMD_HELP 3 5713985Sache#define CMD_INFO 4 5813985Sache#define CMD_PAUSE 5 5913985Sache#define CMD_PLAY 6 6013985Sache#define CMD_QUIT 7 6113985Sache#define CMD_RESUME 8 6213985Sache#define CMD_STOP 9 6313985Sache#define CMD_VOLUME 10 6413985Sache#define CMD_CLOSE 11 6513985Sache#define CMD_RESET 12 6613985Sache#define CMD_SET 13 6713985Sache#define CMD_STATUS 14 6813985Sache 6910099Sjkhstruct cmdtab { 7010099Sjkh int command; 7110099Sjkh char *name; 7213985Sache unsigned min; 7310099Sjkh char *args; 7410099Sjkh} cmdtab[] = { 7513985Sache{ CMD_CLOSE, "close", 1, "" }, 7613985Sache{ CMD_DEBUG, "debug", 1, "on | off" }, 7713985Sache{ CMD_EJECT, "eject", 1, "" }, 7813985Sache{ CMD_HELP, "?", 1, 0 }, 7913985Sache{ CMD_HELP, "help", 1, "" }, 8013985Sache{ CMD_INFO, "info", 1, "" }, 8113985Sache{ CMD_PAUSE, "pause", 2, "" }, 8213985Sache{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, 8313985Sache{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, 8413985Sache{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, 8513985Sache{ CMD_PLAY, "play", 1, "[#block [len]]" }, 8613985Sache{ CMD_QUIT, "quit", 1, "" }, 8713985Sache{ CMD_RESET, "reset", 4, "" }, 8813985Sache{ CMD_RESUME, "resume", 1, "" }, 8913985Sache{ CMD_SET, "set", 2, "msf | lba" }, 9013985Sache{ CMD_STATUS, "status", 1, "" }, 9113985Sache{ CMD_STOP, "stop", 3, "" }, 9213985Sache{ CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" }, 9313985Sache{ 0, } 9410099Sjkh}; 9510099Sjkh 9613985Sachestruct cd_toc_entry toc_buffer[100]; 9710099Sjkh 9813985Sacheconst char *cdname; 9913985Sacheint fd = -1; 10013985Sacheint verbose = 1; 10113985Sacheint msf = 1; 10210099Sjkh 10313985Sacheint setvol __P((int, int)); 10413985Sacheint read_toc_entrys __P((int)); 10513985Sacheint play_msf __P((int, int, int, int, int, int)); 10613985Sacheint play_track __P((int, int, int, int)); 10713985Sacheint get_vol __P((int *, int *)); 10813985Sacheint status __P((int *, int *, int *, int *)); 10913985Sacheint open_cd __P((void)); 11013985Sacheint play __P((char *arg)); 11113985Sacheint info __P((char *arg)); 11213985Sacheint pstatus __P((char *arg)); 11313985Sachechar *input __P((int *)); 11413985Sachevoid prtrack __P((struct cd_toc_entry *e, int lastflag)); 11513985Sachevoid lba2msf __P((unsigned long lba, 11613985Sache u_char *m, u_char *s, u_char *f)); 11713985Sacheunsigned int msf2lba __P((u_char m, u_char s, u_char f)); 11813985Sacheint play_blocks __P((int blk, int len)); 11913985Sacheint run __P((int cmd, char *arg)); 12013985Sachechar *parse __P((char *buf, int *cmd)); 12110099Sjkh 12210099Sjkhvoid help () 12310099Sjkh{ 12410099Sjkh struct cmdtab *c; 12513985Sache char *s, n; 12613985Sache int i; 12710099Sjkh 12810099Sjkh for (c=cmdtab; c->name; ++c) { 12910099Sjkh if (! c->args) 13010099Sjkh continue; 13113985Sache printf("\t"); 13213985Sache for (i = c->min, s = c->name; *s; s++, i--) { 13313985Sache if (i > 0) 13413985Sache n = toupper(*s); 13513985Sache else 13613985Sache n = *s; 13713985Sache putchar(n); 13813985Sache } 13910099Sjkh if (*c->args) 14010099Sjkh printf (" %s", c->args); 14110099Sjkh printf ("\n"); 14210099Sjkh } 14313985Sache printf ("\n\tThe word \"play\" is not required for the play commands.\n"); 14413985Sache printf ("\tThe plain target address is taken as a synonym for play.\n"); 14510099Sjkh} 14610099Sjkh 14710099Sjkhvoid usage () 14810099Sjkh{ 14929103Scharnier fprintf (stderr, "usage: cdcontrol [-vs] [-f disc] [command args ...]\n"); 15010099Sjkh exit (1); 15110099Sjkh} 15210099Sjkh 15310099Sjkhint main (int argc, char **argv) 15410099Sjkh{ 15510099Sjkh int cmd; 15610099Sjkh char *arg; 15710099Sjkh 15813985Sache cdname = getenv ("MUSIC_CD"); 15910099Sjkh if (! cdname) 16013985Sache cdname = getenv ("CD_DRIVE"); 16113985Sache if (! cdname) 16213985Sache cdname = getenv ("DISC"); 16313985Sache if (! cdname) 16410099Sjkh cdname = getenv ("CDPLAY"); 16510099Sjkh 16610099Sjkh for (;;) { 16710099Sjkh switch (getopt (argc, argv, "svhf:")) { 16810099Sjkh case EOF: 16910099Sjkh break; 17010099Sjkh case 's': 17110099Sjkh verbose = 0; 17210099Sjkh continue; 17310099Sjkh case 'v': 17410099Sjkh verbose = 2; 17510099Sjkh continue; 17610099Sjkh case 'f': 17710099Sjkh cdname = optarg; 17810099Sjkh continue; 17910099Sjkh case 'h': 18010099Sjkh default: 18110099Sjkh usage (); 18210099Sjkh } 18310099Sjkh break; 18410099Sjkh } 18510099Sjkh argc -= optind; 18610099Sjkh argv += optind; 18710099Sjkh 18813985Sache if (argc > 0 && ! strcasecmp (*argv, "help")) 18910099Sjkh usage (); 19010099Sjkh 19110099Sjkh if (! cdname) { 19213985Sache cdname = DEFAULT_CD_DRIVE; 19329103Scharnier warnx("no CD device name specified, defaulting to %s", cdname); 19410099Sjkh } 19510099Sjkh 19610099Sjkh if (argc > 0) { 19710099Sjkh char buf[80], *p; 19810099Sjkh int len; 19910099Sjkh 20013985Sache for (p=buf; argc-->0; ++argv) { 20110099Sjkh len = strlen (*argv); 20213985Sache 20310099Sjkh if (p + len >= buf + sizeof (buf) - 1) 20410099Sjkh usage (); 20513985Sache 20610099Sjkh if (p > buf) 20710099Sjkh *p++ = ' '; 20813985Sache 20910099Sjkh strcpy (p, *argv); 21010099Sjkh p += len; 21110099Sjkh } 21210099Sjkh *p = 0; 21310099Sjkh arg = parse (buf, &cmd); 21413985Sache return (run (cmd, arg)); 21510099Sjkh } 21610099Sjkh 21710099Sjkh if (verbose == 1) 21810099Sjkh verbose = isatty (0); 21913985Sache 22010099Sjkh if (verbose) { 22113985Sache printf ("Compact Disc Control utility, version %s\n", VERSION); 22210099Sjkh printf ("Type `?' for command list\n\n"); 22310099Sjkh } 22410099Sjkh 22510099Sjkh for (;;) { 22610099Sjkh arg = input (&cmd); 22710099Sjkh if (run (cmd, arg) < 0) { 22810099Sjkh if (verbose) 22929103Scharnier warn(NULL); 23010099Sjkh close (fd); 23110099Sjkh fd = -1; 23210099Sjkh } 23310099Sjkh fflush (stdout); 23410099Sjkh } 23510099Sjkh} 23610099Sjkh 23710099Sjkhint run (int cmd, char *arg) 23810099Sjkh{ 23910099Sjkh int l, r, rc; 24010099Sjkh 24110099Sjkh switch (cmd) { 24213985Sache 24310099Sjkh case CMD_QUIT: 24410099Sjkh exit (0); 24510099Sjkh 24613985Sache case CMD_INFO: 24713985Sache if (fd < 0 && ! open_cd ()) 24813985Sache return (0); 24910099Sjkh 25010099Sjkh return info (arg); 25110099Sjkh 25213884Sache case CMD_STATUS: 25313985Sache if (fd < 0 && ! open_cd ()) 25413985Sache return (0); 25513985Sache 25613884Sache return pstatus (arg); 25713884Sache 25810099Sjkh case CMD_PAUSE: 25913985Sache if (fd < 0 && ! open_cd ()) 26013985Sache return (0); 26113985Sache 26210099Sjkh return ioctl (fd, CDIOCPAUSE); 26310099Sjkh 26410099Sjkh case CMD_RESUME: 26513985Sache if (fd < 0 && ! open_cd ()) 26613985Sache return (0); 26713985Sache 26810099Sjkh return ioctl (fd, CDIOCRESUME); 26910099Sjkh 27010099Sjkh case CMD_STOP: 27113985Sache if (fd < 0 && ! open_cd ()) 27213985Sache return (0); 27310099Sjkh 27413985Sache rc = ioctl (fd, CDIOCSTOP); 27513985Sache 27613985Sache (void) ioctl (fd, CDIOCALLOW); 27713985Sache 27813985Sache return (rc); 27913985Sache 28013884Sache case CMD_RESET: 28113985Sache if (fd < 0 && ! open_cd ()) 28213985Sache return (0); 28313985Sache 28413884Sache rc = ioctl (fd, CDIOCRESET); 28513884Sache if (rc < 0) 28613884Sache return rc; 28713884Sache close(fd); 28813884Sache fd = -1; 28913884Sache return (0); 29013884Sache 29110099Sjkh case CMD_DEBUG: 29213985Sache if (fd < 0 && ! open_cd ()) 29313985Sache return (0); 29413985Sache 29513985Sache if (! strcasecmp (arg, "on")) 29610099Sjkh return ioctl (fd, CDIOCSETDEBUG); 29713985Sache 29813985Sache if (! strcasecmp (arg, "off")) 29910099Sjkh return ioctl (fd, CDIOCCLRDEBUG); 30013985Sache 30129103Scharnier warnx("invalid command arguments"); 30213985Sache 30310099Sjkh return (0); 30410099Sjkh 30510099Sjkh case CMD_EJECT: 30613985Sache if (fd < 0 && ! open_cd ()) 30713985Sache return (0); 30813985Sache 30910099Sjkh (void) ioctl (fd, CDIOCALLOW); 31010099Sjkh rc = ioctl (fd, CDIOCEJECT); 31110099Sjkh if (rc < 0) 31210099Sjkh return (rc); 31313865Sache return (0); 31413865Sache 31513985Sache case CMD_CLOSE: 31613985Sache if (fd < 0 && ! open_cd ()) 31713985Sache return (0); 31813985Sache 31913985Sache (void) ioctl (fd, CDIOCALLOW); 32013865Sache rc = ioctl (fd, CDIOCCLOSE); 32113865Sache if (rc < 0) 32213865Sache return (rc); 32313865Sache close(fd); 32410099Sjkh fd = -1; 32510099Sjkh return (0); 32610099Sjkh 32710099Sjkh case CMD_PLAY: 32813985Sache if (fd < 0 && ! open_cd ()) 32913985Sache return (0); 33013985Sache 33113985Sache while (isspace (*arg)) 33213985Sache arg++; 33313985Sache 33410099Sjkh return play (arg); 33510099Sjkh 33613884Sache case CMD_SET: 33713985Sache if (! strcasecmp (arg, "msf")) 33813884Sache msf = 1; 33913985Sache else if (! strcasecmp (arg, "lba")) 34013884Sache msf = 0; 34113884Sache else 34229103Scharnier warnx("invalid command arguments"); 34313884Sache return (0); 34413884Sache 34510099Sjkh case CMD_VOLUME: 34613985Sache if (fd < 0 && !open_cd ()) 34713985Sache return (0); 34810099Sjkh 34913985Sache if (! strncasecmp (arg, "left", strlen(arg))) 35010099Sjkh return ioctl (fd, CDIOCSETLEFT); 35113985Sache 35213985Sache if (! strncasecmp (arg, "right", strlen(arg))) 35310099Sjkh return ioctl (fd, CDIOCSETRIGHT); 35413985Sache 35513985Sache if (! strncasecmp (arg, "mono", strlen(arg))) 35610099Sjkh return ioctl (fd, CDIOCSETMONO); 35713985Sache 35813985Sache if (! strncasecmp (arg, "stereo", strlen(arg))) 35910099Sjkh return ioctl (fd, CDIOCSETSTERIO); 36010099Sjkh 36113985Sache if (! strncasecmp (arg, "mute", strlen(arg))) 36213985Sache return ioctl (fd, CDIOCSETMUTE); 36313985Sache 36410099Sjkh if (2 != sscanf (arg, "%d %d", &l, &r)) { 36529103Scharnier warnx("invalid command arguments"); 36610099Sjkh return (0); 36710099Sjkh } 36813985Sache 36910099Sjkh return setvol (l, r); 37013985Sache 37113985Sache default: 37213985Sache case CMD_HELP: 37313985Sache help (); 37413985Sache return (0); 37513985Sache 37610099Sjkh } 37710099Sjkh} 37810099Sjkh 37910099Sjkhint play (char *arg) 38010099Sjkh{ 38110099Sjkh struct ioc_toc_header h; 38210099Sjkh int rc, n, start, end = 0, istart = 1, iend = 1; 38310099Sjkh 38410099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 38513985Sache 38610099Sjkh if (rc < 0) 38710099Sjkh return (rc); 38810099Sjkh 38910099Sjkh n = h.ending_track - h.starting_track + 1; 39010099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 39113985Sache 39210099Sjkh if (rc < 0) 39310099Sjkh return (rc); 39410099Sjkh 39513989Sache if (! arg || ! *arg) { 39613985Sache /* Play the whole disc */ 39713989Sache if (msf) 39813989Sache return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, 39913989Sache toc_buffer[n].addr.msf.second, 40013989Sache toc_buffer[n].addr.msf.frame)); 40113989Sache else 40213989Sache return play_blocks (0, ntohl(toc_buffer[n].addr.lba)); 40313989Sache } 40410099Sjkh 40510099Sjkh if (strchr (arg, '#')) { 40613985Sache /* Play block #blk [ len ] */ 40710099Sjkh int blk, len = 0; 40810099Sjkh 40910099Sjkh if (2 != sscanf (arg, "#%d%d", &blk, &len) && 41013985Sache 1 != sscanf (arg, "#%d", &blk)) 41113985Sache goto Clean_up; 41213985Sache 41313989Sache if (len == 0) { 41413989Sache if (msf) 41513989Sache len = msf2lba (toc_buffer[n].addr.msf.minute, 41613989Sache toc_buffer[n].addr.msf.second, 41713989Sache toc_buffer[n].addr.msf.frame) - blk; 41813989Sache else 41913989Sache len = ntohl(toc_buffer[n].addr.lba) - blk; 42013989Sache } 42110099Sjkh return play_blocks (blk, len); 42210099Sjkh } 42310099Sjkh 42410099Sjkh if (strchr (arg, ':')) { 42510099Sjkh /* 42610099Sjkh * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] 42713985Sache * 42813985Sache * Will now also undestand timed addresses relative 42913985Sache * to the beginning of a track in the form... 43013985Sache * 43113985Sache * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] 43210099Sjkh */ 43313985Sache unsigned tr1, tr2; 43413985Sache unsigned m1, m2, s1, s2, f1, f2; 43513989Sache unsigned char tm, ts, tf; 43610099Sjkh 43713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 43813985Sache if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", 43913985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) 44013985Sache goto Play_Relative_Addresses; 44113985Sache 44213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44313985Sache if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", 44413985Sache &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) 44513985Sache goto Play_Relative_Addresses; 44613985Sache 44713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 44813985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", 44913985Sache &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) 45013985Sache goto Play_Relative_Addresses; 45113985Sache 45213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45313985Sache if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", 45413985Sache &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) 45513985Sache goto Play_Relative_Addresses; 45613985Sache 45713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 45813985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", 45913985Sache &tr1, &m1, &s1, &f1, &m2, &s2)) 46013985Sache goto Play_Relative_Addresses; 46113985Sache 46213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46313985Sache if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", 46413985Sache &tr1, &m1, &s1, &m2, &s2, &f2)) 46513985Sache goto Play_Relative_Addresses; 46613985Sache 46713985Sache tr2 = m2 = s2 = f2 = f1 = 0; 46813985Sache if (6 == sscanf (arg, "%d %d:%d.%d %d %d", 46913985Sache &tr1, &m1, &s1, &f1, &tr2, &m2)) 47013985Sache goto Play_Relative_Addresses; 47113985Sache 47213985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47313985Sache if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) 47413985Sache goto Play_Relative_Addresses; 47513985Sache 47613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 47713985Sache if (5 == sscanf (arg, "%d %d:%d %d %d", 47813985Sache &tr1, &m1, &s1, &tr2, &m2)) 47913985Sache goto Play_Relative_Addresses; 48013985Sache 48113985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48213985Sache if (5 == sscanf (arg, "%d %d:%d.%d %d", 48313985Sache &tr1, &m1, &s1, &f1, &tr2)) 48413985Sache goto Play_Relative_Addresses; 48513985Sache 48613985Sache tr2 = m2 = s2 = f2 = f1 = 0; 48713985Sache if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) 48813985Sache goto Play_Relative_Addresses; 48913985Sache 49013985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49113985Sache if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) 49213985Sache goto Play_Relative_Addresses; 49313985Sache 49413985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49513985Sache if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) 49613985Sache goto Play_Relative_Addresses; 49713985Sache 49813985Sache tr2 = m2 = s2 = f2 = f1 = 0; 49913985Sache goto Try_Absolute_Timed_Addresses; 50013985Sache 50113985SachePlay_Relative_Addresses: 50213985Sache if (! tr1) 50313985Sache tr1 = 1; 50413985Sache else if (tr1 > n) 50513985Sache tr1 = n; 50613985Sache 50713989Sache if (msf) { 50813989Sache tm = toc_buffer[tr1].addr.msf.minute; 50913989Sache ts = toc_buffer[tr1].addr.msf.second; 51013989Sache tf = toc_buffer[tr1].addr.msf.frame; 51113989Sache } else 51213989Sache lba2msf(ntohl(toc_buffer[tr1].addr.lba), 51313989Sache &tm, &ts, &tf); 51413989Sache if ((m1 > tm) 51513989Sache || ((m1 == tm) 51613989Sache && ((s1 > ts) 51713989Sache || ((s1 == ts) 51813989Sache && (f1 > tf))))) { 51913985Sache printf ("Track %d is not that long.\n", tr1); 52013985Sache return (0); 52113985Sache } 52213985Sache 52313985Sache tr1--; 52413985Sache 52513989Sache f1 += tf; 52613985Sache if (f1 >= 75) { 52713985Sache s1 += f1 / 75; 52813985Sache f1 %= 75; 52913985Sache } 53013985Sache 53113989Sache s1 += ts; 53213985Sache if (s1 >= 60) { 53313985Sache m1 += s1 / 60; 53413985Sache s1 %= 60; 53513985Sache } 53613985Sache 53713989Sache m1 += tm; 53813985Sache 53913985Sache if (! tr2) { 54013985Sache if (m2 || s2 || f2) { 54113985Sache tr2 = tr1; 54213985Sache f2 += f1; 54313985Sache if (f2 >= 75) { 54413985Sache s2 += f2 / 75; 54513985Sache f2 %= 75; 54613985Sache } 54713985Sache 54813985Sache s2 += s1; 54913985Sache if (s2 > 60) { 55013985Sache m2 += s2 / 60; 55113985Sache s2 %= 60; 55213985Sache } 55313985Sache 55413985Sache m2 += m1; 55513985Sache } else { 55613985Sache tr2 = n; 55713989Sache if (msf) { 55813989Sache m2 = toc_buffer[n].addr.msf.minute; 55913989Sache s2 = toc_buffer[n].addr.msf.second; 56013989Sache f2 = toc_buffer[n].addr.msf.frame; 56113989Sache } else { 56213989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 56313989Sache &tm, &ts, &tf); 56413989Sache m2 = tm; 56513989Sache s2 = ts; 56613989Sache f2 = tf; 56713989Sache } 56813985Sache } 56913985Sache } else if (tr2 > n) { 57013985Sache tr2 = n; 57113985Sache m2 = s2 = f2 = 0; 57213985Sache } else { 57313985Sache if (m2 || s2 || f2) 57413985Sache tr2--; 57513989Sache if (msf) { 57613989Sache tm = toc_buffer[tr2].addr.msf.minute; 57713989Sache ts = toc_buffer[tr2].addr.msf.second; 57813989Sache tf = toc_buffer[tr2].addr.msf.frame; 57913989Sache } else 58013989Sache lba2msf(ntohl(toc_buffer[tr2].addr.lba), 58113989Sache &tm, &ts, &tf); 58213989Sache f2 += tf; 58313985Sache if (f2 >= 75) { 58413985Sache s2 += f2 / 75; 58513985Sache f2 %= 75; 58613985Sache } 58713985Sache 58813989Sache s2 += ts; 58913985Sache if (s2 > 60) { 59013985Sache m2 += s2 / 60; 59113985Sache s2 %= 60; 59213985Sache } 59313985Sache 59413989Sache m2 += tm; 59513985Sache } 59613985Sache 59713989Sache if (msf) { 59813989Sache tm = toc_buffer[n].addr.msf.minute; 59913989Sache ts = toc_buffer[n].addr.msf.second; 60013989Sache tf = toc_buffer[n].addr.msf.frame; 60113989Sache } else 60213989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 60313989Sache &tm, &ts, &tf); 60413985Sache if ((tr2 < n) 60513989Sache && ((m2 > tm) 60613989Sache || ((m2 == tm) 60713989Sache && ((s2 > ts) 60813989Sache || ((s2 == ts) 60913989Sache && (f2 > tf)))))) { 61013985Sache printf ("The playing time of the disc is not that long.\n"); 61113985Sache return (0); 61213985Sache } 61313985Sache return (play_msf (m1, s1, f1, m2, s2, f2)); 61413985Sache 61513985SacheTry_Absolute_Timed_Addresses: 61613985Sache if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", 61713985Sache &m1, &s1, &f1, &m2, &s2, &f2) && 61810099Sjkh 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 61910099Sjkh 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 62010099Sjkh 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 62110099Sjkh 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 62210099Sjkh 2 != sscanf (arg, "%d:%d", &m1, &s1)) 62313985Sache goto Clean_up; 62413985Sache 62510099Sjkh if (m2 == 0) { 62613989Sache if (msf) { 62713989Sache m2 = toc_buffer[n].addr.msf.minute; 62813989Sache s2 = toc_buffer[n].addr.msf.second; 62913989Sache f2 = toc_buffer[n].addr.msf.frame; 63013989Sache } else { 63113989Sache lba2msf(ntohl(toc_buffer[n].addr.lba), 63213989Sache &tm, &ts, &tf); 63313989Sache m2 = tm; 63413989Sache s2 = ts; 63513989Sache f2 = tf; 63613989Sache } 63710099Sjkh } 63810099Sjkh return play_msf (m1, s1, f1, m2, s2, f2); 63910099Sjkh } 64010099Sjkh 64110099Sjkh /* 64210099Sjkh * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] 64310099Sjkh */ 64410099Sjkh if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 64510099Sjkh 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 64610099Sjkh 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 64710099Sjkh 2 != sscanf (arg, "%d.%d", &start, &istart) && 64810099Sjkh 2 != sscanf (arg, "%d%d", &start, &end) && 64910099Sjkh 1 != sscanf (arg, "%d", &start)) 65013985Sache goto Clean_up; 65113985Sache 65210099Sjkh if (end == 0) 65310099Sjkh end = n; 65413985Sache return (play_track (start, istart, end, iend)); 65513985Sache 65613985SacheClean_up: 65729103Scharnier warnx("invalid command arguments"); 65813985Sache return (0); 65910099Sjkh} 66010099Sjkh 66110099Sjkhchar *strstatus (int sts) 66210099Sjkh{ 66310099Sjkh switch (sts) { 66410099Sjkh case ASTS_INVALID: return ("invalid"); 66510099Sjkh case ASTS_PLAYING: return ("playing"); 66610099Sjkh case ASTS_PAUSED: return ("paused"); 66710099Sjkh case ASTS_COMPLETED: return ("completed"); 66810099Sjkh case ASTS_ERROR: return ("error"); 66910099Sjkh case ASTS_VOID: return ("void"); 67010099Sjkh default: return ("??"); 67110099Sjkh } 67210099Sjkh} 67310099Sjkh 67413884Sacheint pstatus (char *arg) 67510099Sjkh{ 67610099Sjkh struct ioc_vol v; 67713888Sache struct ioc_read_subchannel ss; 67813888Sache struct cd_sub_channel_info data; 67913884Sache int rc, trk, m, s, f; 68010099Sjkh 68110099Sjkh rc = status (&trk, &m, &s, &f); 68210099Sjkh if (rc >= 0) 68310099Sjkh if (verbose) 68410099Sjkh printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", 68510099Sjkh rc, strstatus (rc), trk, m, s, f); 68610099Sjkh else 68710099Sjkh printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); 68810099Sjkh else 68913888Sache printf ("No current status info available\n"); 69010099Sjkh 69113888Sache bzero (&ss, sizeof (ss)); 69213888Sache ss.data = &data; 69313888Sache ss.data_len = sizeof (data); 69413888Sache ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 69513888Sache ss.data_format = CD_MEDIA_CATALOG; 69613888Sache rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); 69713888Sache if (rc >= 0) { 69813889Sache printf("Media catalog is %sactive", 69913888Sache ss.data->what.media_catalog.mc_valid ? "": "in"); 70016736Sache if (ss.data->what.media_catalog.mc_valid && 70116736Sache ss.data->what.media_catalog.mc_number[0]) 70213889Sache printf(", number \"%.15s\"", 70313889Sache ss.data->what.media_catalog.mc_number); 70413889Sache putchar('\n'); 70513888Sache } else 70613888Sache printf("No media catalog info available\n"); 70713888Sache 70810099Sjkh rc = ioctl (fd, CDIOCGETVOL, &v); 70910099Sjkh if (rc >= 0) 71010099Sjkh if (verbose) 71110099Sjkh printf ("Left volume = %d, right volume = %d\n", 71210099Sjkh v.vol[0], v.vol[1]); 71310099Sjkh else 71410099Sjkh printf ("%d %d\n", v.vol[0], v.vol[1]); 71510099Sjkh else 71613888Sache printf ("No volume level info available\n"); 71713884Sache return(0); 71813884Sache} 71910099Sjkh 72013884Sacheint info (char *arg) 72113884Sache{ 72213884Sache struct ioc_toc_header h; 72313884Sache int rc, i, n; 72413884Sache 72510099Sjkh rc = ioctl (fd, CDIOREADTOCHEADER, &h); 72613888Sache if (rc >= 0) { 72710099Sjkh if (verbose) 72810099Sjkh printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", 72910099Sjkh h.starting_track, h.ending_track, h.len); 73010099Sjkh else 73110099Sjkh printf ("%d %d %d\n", h.starting_track, 73210099Sjkh h.ending_track, h.len); 73313888Sache } else { 73429103Scharnier warn("getting toc header"); 73510099Sjkh return (rc); 73610099Sjkh } 73710099Sjkh 73810099Sjkh n = h.ending_track - h.starting_track + 1; 73910099Sjkh rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); 74010099Sjkh if (rc < 0) 74110099Sjkh return (rc); 74213985Sache 74310099Sjkh if (verbose) { 74410099Sjkh printf ("track start duration block length type\n"); 74510099Sjkh printf ("-------------------------------------------------\n"); 74610099Sjkh } 74713985Sache 74810099Sjkh for (i = 0; i < n; i++) { 74910099Sjkh printf ("%5d ", toc_buffer[i].track); 75010099Sjkh prtrack (toc_buffer + i, 0); 75110099Sjkh } 75213867Sache printf ("%5d ", toc_buffer[n].track); 75310099Sjkh prtrack (toc_buffer + n, 1); 75410099Sjkh return (0); 75510099Sjkh} 75610099Sjkh 75713985Sachevoid lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) 75810099Sjkh{ 75913985Sache lba += 150; /* block start offset */ 76013985Sache lba &= 0xffffff; /* negative lbas use only 24 bits */ 76110099Sjkh *m = lba / (60 * 75); 76210099Sjkh lba %= (60 * 75); 76310099Sjkh *s = lba / 75; 76410099Sjkh *f = lba % 75; 76510099Sjkh} 76610099Sjkh 76713985Sacheunsigned int msf2lba (u_char m, u_char s, u_char f) 76810099Sjkh{ 76910099Sjkh return (((m * 60) + s) * 75 + f) - 150; 77010099Sjkh} 77110099Sjkh 77210099Sjkhvoid prtrack (struct cd_toc_entry *e, int lastflag) 77310099Sjkh{ 77410099Sjkh int block, next, len; 77510099Sjkh u_char m, s, f; 77610099Sjkh 77713884Sache if (msf) { 77813884Sache /* Print track start */ 77913884Sache printf ("%2d:%02d.%02d ", e->addr.msf.minute, 78013884Sache e->addr.msf.second, e->addr.msf.frame); 78110099Sjkh 78213884Sache block = msf2lba (e->addr.msf.minute, e->addr.msf.second, 78313884Sache e->addr.msf.frame); 78413884Sache } else { 78513884Sache block = ntohl(e->addr.lba); 78613884Sache lba2msf(block, &m, &s, &f); 78713884Sache /* Print track start */ 78813884Sache printf ("%2d:%02d.%02d ", m, s, f); 78913884Sache } 79010099Sjkh if (lastflag) { 79110099Sjkh /* Last track -- print block */ 79210099Sjkh printf (" - %6d - -\n", block); 79310099Sjkh return; 79410099Sjkh } 79510099Sjkh 79613884Sache if (msf) 79713884Sache next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, 79813884Sache e[1].addr.msf.frame); 79913884Sache else 80013884Sache next = ntohl(e[1].addr.lba); 80110099Sjkh len = next - block; 80210099Sjkh lba2msf (len, &m, &s, &f); 80310099Sjkh 80410099Sjkh /* Print duration, block, length, type */ 80510099Sjkh printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, 80613985Sache (e->control & 4) ? "data" : "audio"); 80710099Sjkh} 80810099Sjkh 80910099Sjkhint play_track (int tstart, int istart, int tend, int iend) 81010099Sjkh{ 81110099Sjkh struct ioc_play_track t; 81210099Sjkh 81310099Sjkh t.start_track = tstart; 81410099Sjkh t.start_index = istart; 81510099Sjkh t.end_track = tend; 81610099Sjkh t.end_index = iend; 81713985Sache 81810099Sjkh return ioctl (fd, CDIOCPLAYTRACKS, &t); 81910099Sjkh} 82010099Sjkh 82110099Sjkhint play_blocks (int blk, int len) 82210099Sjkh{ 82313985Sache struct ioc_play_blocks t; 82410099Sjkh 82510099Sjkh t.blk = blk; 82610099Sjkh t.len = len; 82713985Sache 82810099Sjkh return ioctl (fd, CDIOCPLAYBLOCKS, &t); 82910099Sjkh} 83010099Sjkh 83113985Sacheint setvol (int left, int right) 83210099Sjkh{ 83313985Sache struct ioc_vol v; 83410099Sjkh 83513985Sache v.vol[0] = left; 83613985Sache v.vol[1] = right; 83710099Sjkh v.vol[2] = 0; 83810099Sjkh v.vol[3] = 0; 83913985Sache 84010099Sjkh return ioctl (fd, CDIOCSETVOL, &v); 84110099Sjkh} 84210099Sjkh 84310099Sjkhint read_toc_entrys (int len) 84410099Sjkh{ 84510099Sjkh struct ioc_read_toc_entry t; 84610099Sjkh 84713884Sache t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 84813737Sache t.starting_track = 0; 84910099Sjkh t.data_len = len; 85010099Sjkh t.data = toc_buffer; 85113985Sache 85213985Sache return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); 85310099Sjkh} 85410099Sjkh 85510099Sjkhint play_msf (int start_m, int start_s, int start_f, 85613985Sache int end_m, int end_s, int end_f) 85710099Sjkh{ 85813985Sache struct ioc_play_msf a; 85910099Sjkh 86010099Sjkh a.start_m = start_m; 86110099Sjkh a.start_s = start_s; 86210099Sjkh a.start_f = start_f; 86310099Sjkh a.end_m = end_m; 86410099Sjkh a.end_s = end_s; 86510099Sjkh a.end_f = end_f; 86613985Sache 86710099Sjkh return ioctl (fd, CDIOCPLAYMSF, (char *) &a); 86810099Sjkh} 86910099Sjkh 87010099Sjkhint status (int *trk, int *min, int *sec, int *frame) 87110099Sjkh{ 87210099Sjkh struct ioc_read_subchannel s; 87310099Sjkh struct cd_sub_channel_info data; 87413884Sache u_char mm, ss, ff; 87510099Sjkh 87610099Sjkh bzero (&s, sizeof (s)); 87710099Sjkh s.data = &data; 87810099Sjkh s.data_len = sizeof (data); 87913884Sache s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; 88010099Sjkh s.data_format = CD_CURRENT_POSITION; 88113985Sache 88210099Sjkh if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) 88310099Sjkh return -1; 88413985Sache 88510099Sjkh *trk = s.data->what.position.track_number; 88613884Sache if (msf) { 88713884Sache *min = s.data->what.position.reladdr.msf.minute; 88813884Sache *sec = s.data->what.position.reladdr.msf.second; 88913884Sache *frame = s.data->what.position.reladdr.msf.frame; 89013884Sache } else { 89113884Sache lba2msf(ntohl(s.data->what.position.reladdr.lba), 89213884Sache &mm, &ss, &ff); 89313884Sache *min = mm; 89413884Sache *sec = ss; 89513884Sache *frame = ff; 89613884Sache } 89713985Sache 89810099Sjkh return s.data->header.audio_status; 89910099Sjkh} 90010099Sjkh 90110099Sjkhchar *input (int *cmd) 90210099Sjkh{ 90310099Sjkh static char buf[80]; 90410099Sjkh char *p; 90510099Sjkh 90610099Sjkh do { 90710099Sjkh if (verbose) 90829103Scharnier fprintf (stderr, "cdcontrol> "); 90910099Sjkh if (! fgets (buf, sizeof (buf), stdin)) { 91010099Sjkh *cmd = CMD_QUIT; 91113985Sache fprintf (stderr, "\r\n"); 91213985Sache return (0); 91310099Sjkh } 91410099Sjkh p = parse (buf, cmd); 91510099Sjkh } while (! p); 91610099Sjkh return (p); 91710099Sjkh} 91810099Sjkh 91910099Sjkhchar *parse (char *buf, int *cmd) 92010099Sjkh{ 92110099Sjkh struct cmdtab *c; 92210099Sjkh char *p; 92310099Sjkh int len; 92410099Sjkh 92513985Sache for (p=buf; isspace (*p); p++) 92613985Sache continue; 92710099Sjkh 92813985Sache if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { 92913985Sache *cmd = CMD_PLAY; 93013985Sache return (p); 93113985Sache } 93210099Sjkh 93313985Sache for (buf = p; *p && ! isspace (*p); p++) 93413985Sache continue; 93513985Sache 93613985Sache len = p - buf; 93710099Sjkh if (! len) 93810099Sjkh return (0); 93913985Sache 94013985Sache if (*p) { /* It must be a spacing character! */ 94113985Sache char *q; 94213985Sache 94313985Sache *p++ = 0; 94413985Sache for (q=p; *q && *q != '\n' && *q != '\r'; q++) 94513985Sache continue; 94613985Sache *q = 0; 94713985Sache } 94813985Sache 94910099Sjkh *cmd = -1; 95010099Sjkh for (c=cmdtab; c->name; ++c) { 95113985Sache /* Is it an exact match? */ 95213985Sache if (! strcasecmp (buf, c->name)) { 95313985Sache *cmd = c->command; 95413985Sache break; 95513985Sache } 95613985Sache 95713985Sache /* Try short hand forms then... */ 95813985Sache if (len >= c->min && ! strncasecmp (buf, c->name, len)) { 95913985Sache if (*cmd != -1 && *cmd != c->command) { 96029103Scharnier warnx("ambiguous command"); 96113985Sache return (0); 96213985Sache } 96310099Sjkh *cmd = c->command; 96413985Sache } 96513985Sache } 96610099Sjkh 96710099Sjkh if (*cmd == -1) { 96829103Scharnier warnx("invalid command, enter ``help'' for commands"); 96910099Sjkh return (0); 97010099Sjkh } 97113985Sache 97213985Sache while (isspace (*p)) 97313985Sache p++; 97410099Sjkh return p; 97510099Sjkh} 97610099Sjkh 97710099Sjkhint open_cd () 97810099Sjkh{ 97910099Sjkh char devbuf[80]; 98010099Sjkh 98110099Sjkh if (fd > -1) 98210099Sjkh return (1); 98313985Sache 98410099Sjkh if (*cdname == '/') 98510099Sjkh strcpy (devbuf, cdname); 98610099Sjkh else if (*cdname == 'r') 98710099Sjkh sprintf (devbuf, "/dev/%s", cdname); 98810099Sjkh else 98910099Sjkh sprintf (devbuf, "/dev/r%s", cdname); 99013985Sache 99110099Sjkh fd = open (devbuf, O_RDONLY); 99213985Sache 99310099Sjkh if (fd < 0 && errno == ENOENT) { 99413985Sache strcat (devbuf, DEFAULT_CD_PARTITION); 99510099Sjkh fd = open (devbuf, O_RDONLY); 99610099Sjkh } 99713985Sache 99810099Sjkh if (fd < 0) { 99913985Sache if (errno == ENXIO) { 100013985Sache /* ENXIO has an overloaded meaning here. 100113985Sache * The original "Device not configured" should 100213985Sache * be interpreted as "No disc in drive %s". */ 100329103Scharnier warnx("no disc in drive %s", devbuf); 100413985Sache return (0); 100510099Sjkh } 100629103Scharnier err(1, "%s", devbuf); 100710099Sjkh } 100810099Sjkh return (1); 100910099Sjkh} 1010