Index: stable/2.2/usr.sbin/cdcontrol/cdcontrol.1 =================================================================== --- stable/2.2/usr.sbin/cdcontrol/cdcontrol.1 (revision 32953) +++ stable/2.2/usr.sbin/cdcontrol/cdcontrol.1 (revision 32954) @@ -1,170 +1,178 @@ -.\" $Id: cdcontrol.1,v 1.8.2.1 1997/09/15 06:27:47 charnier Exp $ +.\" $Id: cdcontrol.1,v 1.8.2.2 1997/12/23 07:15:39 charnier Exp $ .\" .Dd July 3, 1995 .Dt CDCONTROL 1 .Os FreeBSD .Sh NAME .Nm cdcontrol .Nd compact disc control utility .Sh SYNOPSIS .Nm cdcontrol .Op Fl s .Op Fl v .Op Fl f Ar discname .Op Ar command args ... .Sh DESCRIPTION .Nm Cdcontrol is a program to control audio features of a CD drive. The device is a name such as .Pa cd0 or .Pa mcd0 . .Pp If the device not specified, the environment variable .Ev DISC will be used to find the cd device. .Pp If no command is given, then .Nm enters an interactive mode, reading commands from the standard input. .Pp The following options are available: .Bl -tag -width indent .It Fl s Silent mode - do not print table headers and human readable comments. .It Fl v Verbose mode - print as much information as possible. .It Fl f Ar discname Specify a device name, such as .Pa /dev/cd0c or .Pa mcd0 . Both absolute path and relative to .Pa /dev filename are possible. Suffix `c' is added to the device name if needed. .El .Pp The available commands are listed below. Only as many characters as are required to uniquely identify a command need be specified. Word .Em play can be ommitted. .Bl -tag -width Cm .It Cm play Ar first_track Op Ar last_track Play from track .Ar first_track to track .Ar last_track . The first track has number 1. Can be ommited in all cases. .It Cm play Ar start_m:start_s.start_f Op Ar end_m:end_s.end_f Play from the absolute address (MSF) defined by .Ar start_m in minutes, .Ar start_s , in seconds and .Ar start_f (frame number) to the absolute address defined by .Ar end_m in minutes, .Ar end_s , in seconds and .Ar end_f (frame number). Minutes are in the range 0-99. Seconds are in the range 0-59. Frame numbers are in the range 0-74. .It Cm play Op Ar #start_block Op length Play starting from the logical block .Ar start_block using .Ar length logical blocks. .It Cm pause Stop playing. Do not stop the disc. .It Cm resume Resume playing. Used after the .Em pause command. .It Cm stop Stop the disc. .It Cm eject Eject the disc. .It Cm close Inject the disc. .It Cm volume Ar left_channel Ar right_channel Set the volume of left channel to .Ar left_channel and the volume of right channel to .Ar right_channel . Allowed values are in the range 0-255. .It Cm volume Ar mute Turn the sound off. .It Cm volume Ar mono Set the mono mode. .It Cm volume Ar stereo Set the stereo mode. .It Cm volume Ar left Play the left subtrack on both left and right channels. .It Cm volume Ar right Play the right subtrack on both left and right channels. .It Cm info Print the table of contents. -.It Cm status +.It Cm status +.Op Ar audio | media | volume + Print the information about the disc: + +.Nm audio the current playing status and position, + +.Nm media the current media catalog status, + +.Nm volume the current values of the volume for left and right channels. .It Cm help Print the list of available commands. .It Cm debug Ar on Enable the debugging mode of the CD device driver. .It Cm debug Ar off Disable the driver debugging mode. .It Cm reset Perform the hardware reset of the device. .It Cm set Ar msf Set minute-second-frame ioctl mode (default). .It Cm set Ar lba Set LBA ioctl mode. .It Cm quit Quit the program. .Sh FILES .Bl -tag -width /dev/rmcd0c -compact .It Pa /dev/rcd0c .It Pa /dev/rmcd0c .It Pa /dev/rwcd0c .El .Sh AUTHORS .An Jean-Marc Zucconi , .An Andrey A.\ Chernov , .An Serge V.\ Vakulenko .Sh HISTORY The .Nm command appeared in .Fx 2.1 . Index: stable/2.2/usr.sbin/cdcontrol/cdcontrol.c =================================================================== --- stable/2.2/usr.sbin/cdcontrol/cdcontrol.c (revision 32953) +++ stable/2.2/usr.sbin/cdcontrol/cdcontrol.c (revision 32954) @@ -1,1009 +1,1033 @@ /* * Compact Disc Control Utility by Serge V. Vakulenko . * Based on the non-X based CD player by Jean-Marc Zucconi and * Andrey A. Chernov. * * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen . * * 11-Sep-1995: Jukka A. Ukkonen * A couple of further fixes to my own earlier "fixes". * * 18-Sep-1995: Jukka A. Ukkonen * Added an ability to specify addresses relative to the * beginning of a track. This is in fact a variation of * doing the simple play_msf() call. * * 11-Oct-1995: Serge V.Vakulenko * New eject algorithm. * Some code style reformatting. */ #ifndef lint static const char rcsid[] = - "$Id$"; + "$Id: cdcontrol.c,v 1.13.2.1 1997/09/15 06:27:49 charnier Exp $"; #endif /* not lint */ #include #include #include #include #include #include #include #include #include #include #define VERSION "2.0" #define ASTS_INVALID 0x00 /* Audio status byte not valid */ #define ASTS_PLAYING 0x11 /* Audio play operation in progress */ #define ASTS_PAUSED 0x12 /* Audio play operation paused */ #define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */ #define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */ #define ASTS_VOID 0x15 /* No current audio status to return */ #ifndef DEFAULT_CD_DRIVE # define DEFAULT_CD_DRIVE "/dev/cd0c" #endif #ifndef DEFAULT_CD_PARTITION # define DEFAULT_CD_PARTITION "c" #endif #define CMD_DEBUG 1 #define CMD_EJECT 2 #define CMD_HELP 3 #define CMD_INFO 4 #define CMD_PAUSE 5 #define CMD_PLAY 6 #define CMD_QUIT 7 #define CMD_RESUME 8 #define CMD_STOP 9 #define CMD_VOLUME 10 #define CMD_CLOSE 11 #define CMD_RESET 12 #define CMD_SET 13 #define CMD_STATUS 14 +#define STATUS_AUDIO 0x1 +#define STATUS_MEDIA 0x2 +#define STATUS_VOLUME 0x4 struct cmdtab { int command; char *name; unsigned min; char *args; } cmdtab[] = { { CMD_CLOSE, "close", 1, "" }, { CMD_DEBUG, "debug", 1, "on | off" }, { CMD_EJECT, "eject", 1, "" }, { CMD_HELP, "?", 1, 0 }, { CMD_HELP, "help", 1, "" }, { CMD_INFO, "info", 1, "" }, { CMD_PAUSE, "pause", 2, "" }, { CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" }, { CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" }, { CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" }, { CMD_PLAY, "play", 1, "[#block [len]]" }, { CMD_QUIT, "quit", 1, "" }, { CMD_RESET, "reset", 4, "" }, { CMD_RESUME, "resume", 1, "" }, { CMD_SET, "set", 2, "msf | lba" }, -{ CMD_STATUS, "status", 1, "" }, +{ CMD_STATUS, "status", 1, "[audio | media | volume]" }, { CMD_STOP, "stop", 3, "" }, { CMD_VOLUME, "volume", 1, " | left | right | mute | mono | stereo" }, { 0, } }; struct cd_toc_entry toc_buffer[100]; const char *cdname; int fd = -1; int verbose = 1; int msf = 1; int setvol __P((int, int)); int read_toc_entrys __P((int)); int play_msf __P((int, int, int, int, int, int)); int play_track __P((int, int, int, int)); int get_vol __P((int *, int *)); int status __P((int *, int *, int *, int *)); int open_cd __P((void)); int play __P((char *arg)); int info __P((char *arg)); int pstatus __P((char *arg)); char *input __P((int *)); void prtrack __P((struct cd_toc_entry *e, int lastflag)); void lba2msf __P((unsigned long lba, u_char *m, u_char *s, u_char *f)); unsigned int msf2lba __P((u_char m, u_char s, u_char f)); int play_blocks __P((int blk, int len)); int run __P((int cmd, char *arg)); char *parse __P((char *buf, int *cmd)); void help () { struct cmdtab *c; char *s, n; int i; for (c=cmdtab; c->name; ++c) { if (! c->args) continue; printf("\t"); for (i = c->min, s = c->name; *s; s++, i--) { if (i > 0) n = toupper(*s); else n = *s; putchar(n); } if (*c->args) printf (" %s", c->args); printf ("\n"); } printf ("\n\tThe word \"play\" is not required for the play commands.\n"); printf ("\tThe plain target address is taken as a synonym for play.\n"); } void usage () { fprintf (stderr, "usage: cdcontrol [-vs] [-f disc] [command args ...]\n"); exit (1); } int main (int argc, char **argv) { int cmd; char *arg; cdname = getenv ("MUSIC_CD"); if (! cdname) cdname = getenv ("CD_DRIVE"); if (! cdname) cdname = getenv ("DISC"); if (! cdname) cdname = getenv ("CDPLAY"); for (;;) { switch (getopt (argc, argv, "svhf:")) { case EOF: break; case 's': verbose = 0; continue; case 'v': verbose = 2; continue; case 'f': cdname = optarg; continue; case 'h': default: usage (); } break; } argc -= optind; argv += optind; if (argc > 0 && ! strcasecmp (*argv, "help")) usage (); if (! cdname) { cdname = DEFAULT_CD_DRIVE; warnx("no CD device name specified, defaulting to %s", cdname); } if (argc > 0) { char buf[80], *p; int len; for (p=buf; argc-->0; ++argv) { len = strlen (*argv); if (p + len >= buf + sizeof (buf) - 1) usage (); if (p > buf) *p++ = ' '; strcpy (p, *argv); p += len; } *p = 0; arg = parse (buf, &cmd); return (run (cmd, arg)); } if (verbose == 1) verbose = isatty (0); if (verbose) { printf ("Compact Disc Control utility, version %s\n", VERSION); printf ("Type `?' for command list\n\n"); } for (;;) { arg = input (&cmd); if (run (cmd, arg) < 0) { if (verbose) warn(NULL); close (fd); fd = -1; } fflush (stdout); } } int run (int cmd, char *arg) { int l, r, rc; switch (cmd) { case CMD_QUIT: exit (0); case CMD_INFO: if (fd < 0 && ! open_cd ()) return (0); return info (arg); case CMD_STATUS: if (fd < 0 && ! open_cd ()) return (0); return pstatus (arg); case CMD_PAUSE: if (fd < 0 && ! open_cd ()) return (0); return ioctl (fd, CDIOCPAUSE); case CMD_RESUME: if (fd < 0 && ! open_cd ()) return (0); return ioctl (fd, CDIOCRESUME); case CMD_STOP: if (fd < 0 && ! open_cd ()) return (0); rc = ioctl (fd, CDIOCSTOP); (void) ioctl (fd, CDIOCALLOW); return (rc); case CMD_RESET: if (fd < 0 && ! open_cd ()) return (0); rc = ioctl (fd, CDIOCRESET); if (rc < 0) return rc; close(fd); fd = -1; return (0); case CMD_DEBUG: if (fd < 0 && ! open_cd ()) return (0); if (! strcasecmp (arg, "on")) return ioctl (fd, CDIOCSETDEBUG); if (! strcasecmp (arg, "off")) return ioctl (fd, CDIOCCLRDEBUG); warnx("invalid command arguments"); return (0); case CMD_EJECT: if (fd < 0 && ! open_cd ()) return (0); (void) ioctl (fd, CDIOCALLOW); rc = ioctl (fd, CDIOCEJECT); if (rc < 0) return (rc); return (0); case CMD_CLOSE: if (fd < 0 && ! open_cd ()) return (0); (void) ioctl (fd, CDIOCALLOW); rc = ioctl (fd, CDIOCCLOSE); if (rc < 0) return (rc); close(fd); fd = -1; return (0); case CMD_PLAY: if (fd < 0 && ! open_cd ()) return (0); while (isspace (*arg)) arg++; return play (arg); case CMD_SET: if (! strcasecmp (arg, "msf")) msf = 1; else if (! strcasecmp (arg, "lba")) msf = 0; else warnx("invalid command arguments"); return (0); case CMD_VOLUME: if (fd < 0 && !open_cd ()) return (0); if (! strncasecmp (arg, "left", strlen(arg))) return ioctl (fd, CDIOCSETLEFT); if (! strncasecmp (arg, "right", strlen(arg))) return ioctl (fd, CDIOCSETRIGHT); if (! strncasecmp (arg, "mono", strlen(arg))) return ioctl (fd, CDIOCSETMONO); if (! strncasecmp (arg, "stereo", strlen(arg))) return ioctl (fd, CDIOCSETSTERIO); if (! strncasecmp (arg, "mute", strlen(arg))) return ioctl (fd, CDIOCSETMUTE); if (2 != sscanf (arg, "%d %d", &l, &r)) { warnx("invalid command arguments"); return (0); } return setvol (l, r); default: case CMD_HELP: help (); return (0); } } int play (char *arg) { struct ioc_toc_header h; int rc, n, start, end = 0, istart = 1, iend = 1; rc = ioctl (fd, CDIOREADTOCHEADER, &h); if (rc < 0) return (rc); n = h.ending_track - h.starting_track + 1; rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); if (rc < 0) return (rc); if (! arg || ! *arg) { /* Play the whole disc */ if (msf) return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute, toc_buffer[n].addr.msf.second, toc_buffer[n].addr.msf.frame)); else return play_blocks (0, ntohl(toc_buffer[n].addr.lba)); } if (strchr (arg, '#')) { /* Play block #blk [ len ] */ int blk, len = 0; if (2 != sscanf (arg, "#%d%d", &blk, &len) && 1 != sscanf (arg, "#%d", &blk)) goto Clean_up; if (len == 0) { if (msf) len = msf2lba (toc_buffer[n].addr.msf.minute, toc_buffer[n].addr.msf.second, toc_buffer[n].addr.msf.frame) - blk; else len = ntohl(toc_buffer[n].addr.lba) - blk; } return play_blocks (blk, len); } if (strchr (arg, ':')) { /* * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ] * * Will now also undestand timed addresses relative * to the beginning of a track in the form... * * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]] */ unsigned tr1, tr2; unsigned m1, m2, s1, s2, f1, f2; unsigned char tm, ts, tf; tr2 = m2 = s2 = f2 = f1 = 0; if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d", &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d", &tr1, &m1, &s1, &tr2, &m2, &s2, &f2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d", &tr1, &m1, &s1, &f1, &tr2, &m2, &s2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d", &tr1, &m1, &s1, &f1, &m2, &s2, &f2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (6 == sscanf (arg, "%d %d:%d.%d %d:%d", &tr1, &m1, &s1, &f1, &m2, &s2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (6 == sscanf (arg, "%d %d:%d %d:%d.%d", &tr1, &m1, &s1, &m2, &s2, &f2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (6 == sscanf (arg, "%d %d:%d.%d %d %d", &tr1, &m1, &s1, &f1, &tr2, &m2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (5 == sscanf (arg, "%d %d:%d %d %d", &tr1, &m1, &s1, &tr2, &m2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (5 == sscanf (arg, "%d %d:%d.%d %d", &tr1, &m1, &s1, &f1, &tr2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1)) goto Play_Relative_Addresses; tr2 = m2 = s2 = f2 = f1 = 0; goto Try_Absolute_Timed_Addresses; Play_Relative_Addresses: if (! tr1) tr1 = 1; else if (tr1 > n) tr1 = n; if (msf) { tm = toc_buffer[tr1].addr.msf.minute; ts = toc_buffer[tr1].addr.msf.second; tf = toc_buffer[tr1].addr.msf.frame; } else lba2msf(ntohl(toc_buffer[tr1].addr.lba), &tm, &ts, &tf); if ((m1 > tm) || ((m1 == tm) && ((s1 > ts) || ((s1 == ts) && (f1 > tf))))) { printf ("Track %d is not that long.\n", tr1); return (0); } tr1--; f1 += tf; if (f1 >= 75) { s1 += f1 / 75; f1 %= 75; } s1 += ts; if (s1 >= 60) { m1 += s1 / 60; s1 %= 60; } m1 += tm; if (! tr2) { if (m2 || s2 || f2) { tr2 = tr1; f2 += f1; if (f2 >= 75) { s2 += f2 / 75; f2 %= 75; } s2 += s1; if (s2 > 60) { m2 += s2 / 60; s2 %= 60; } m2 += m1; } else { tr2 = n; if (msf) { m2 = toc_buffer[n].addr.msf.minute; s2 = toc_buffer[n].addr.msf.second; f2 = toc_buffer[n].addr.msf.frame; } else { lba2msf(ntohl(toc_buffer[n].addr.lba), &tm, &ts, &tf); m2 = tm; s2 = ts; f2 = tf; } } } else if (tr2 > n) { tr2 = n; m2 = s2 = f2 = 0; } else { if (m2 || s2 || f2) tr2--; if (msf) { tm = toc_buffer[tr2].addr.msf.minute; ts = toc_buffer[tr2].addr.msf.second; tf = toc_buffer[tr2].addr.msf.frame; } else lba2msf(ntohl(toc_buffer[tr2].addr.lba), &tm, &ts, &tf); f2 += tf; if (f2 >= 75) { s2 += f2 / 75; f2 %= 75; } s2 += ts; if (s2 > 60) { m2 += s2 / 60; s2 %= 60; } m2 += tm; } if (msf) { tm = toc_buffer[n].addr.msf.minute; ts = toc_buffer[n].addr.msf.second; tf = toc_buffer[n].addr.msf.frame; } else lba2msf(ntohl(toc_buffer[n].addr.lba), &tm, &ts, &tf); if ((tr2 < n) && ((m2 > tm) || ((m2 == tm) && ((s2 > ts) || ((s2 == ts) && (f2 > tf)))))) { printf ("The playing time of the disc is not that long.\n"); return (0); } return (play_msf (m1, s1, f1, m2, s2, f2)); Try_Absolute_Timed_Addresses: if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d", &m1, &s1, &f1, &m2, &s2, &f2) && 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) && 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) && 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) && 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) && 2 != sscanf (arg, "%d:%d", &m1, &s1)) goto Clean_up; if (m2 == 0) { if (msf) { m2 = toc_buffer[n].addr.msf.minute; s2 = toc_buffer[n].addr.msf.second; f2 = toc_buffer[n].addr.msf.frame; } else { lba2msf(ntohl(toc_buffer[n].addr.lba), &tm, &ts, &tf); m2 = tm; s2 = ts; f2 = tf; } } return play_msf (m1, s1, f1, m2, s2, f2); } /* * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ] */ if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) && 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) && 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) && 2 != sscanf (arg, "%d.%d", &start, &istart) && 2 != sscanf (arg, "%d%d", &start, &end) && 1 != sscanf (arg, "%d", &start)) goto Clean_up; if (end == 0) end = n; return (play_track (start, istart, end, iend)); Clean_up: warnx("invalid command arguments"); return (0); } char *strstatus (int sts) { switch (sts) { case ASTS_INVALID: return ("invalid"); case ASTS_PLAYING: return ("playing"); case ASTS_PAUSED: return ("paused"); case ASTS_COMPLETED: return ("completed"); case ASTS_ERROR: return ("error"); case ASTS_VOID: return ("void"); default: return ("??"); } } int pstatus (char *arg) { struct ioc_vol v; struct ioc_read_subchannel ss; struct cd_sub_channel_info data; int rc, trk, m, s, f; + int what = 0; + char *p; - rc = status (&trk, &m, &s, &f); - if (rc >= 0) + while ((p = strtok(arg, " \t"))) { + arg = 0; + if (!strncasecmp(p, "audio", strlen(p))) + what |= STATUS_AUDIO; + else if (!strncasecmp(p, "media", strlen(p))) + what |= STATUS_MEDIA; + else if (!strncasecmp(p, "volume", strlen(p))) + what |= STATUS_VOLUME; + else { + warnx("invalid command arguments"); + return 0; + } + } + if (!what) + what = STATUS_AUDIO|STATUS_MEDIA|STATUS_VOLUME; + if (what & STATUS_AUDIO) { + rc = status (&trk, &m, &s, &f); + if (rc >= 0) if (verbose) - printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", - rc, strstatus (rc), trk, m, s, f); + printf ("Audio status = %d<%s>, current track = %d, current position = %d:%02d.%02d\n", + rc, strstatus (rc), trk, m, s, f); else - printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); - else + printf ("%d %d %d:%02d.%02d\n", rc, trk, m, s, f); + else printf ("No current status info available\n"); - - bzero (&ss, sizeof (ss)); - ss.data = &data; - ss.data_len = sizeof (data); - ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; - ss.data_format = CD_MEDIA_CATALOG; - rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); - if (rc >= 0) { + } + if (what & STATUS_MEDIA) { + bzero (&ss, sizeof (ss)); + ss.data = &data; + ss.data_len = sizeof (data); + ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; + ss.data_format = CD_MEDIA_CATALOG; + rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss); + if (rc >= 0) { printf("Media catalog is %sactive", - ss.data->what.media_catalog.mc_valid ? "": "in"); + ss.data->what.media_catalog.mc_valid ? "": "in"); if (ss.data->what.media_catalog.mc_valid && ss.data->what.media_catalog.mc_number[0]) - printf(", number \"%.15s\"", - ss.data->what.media_catalog.mc_number); + printf(", number \"%.15s\"", + ss.data->what.media_catalog.mc_number); putchar('\n'); - } else + } else printf("No media catalog info available\n"); - - rc = ioctl (fd, CDIOCGETVOL, &v); - if (rc >= 0) + } + if (what & STATUS_VOLUME) { + rc = ioctl (fd, CDIOCGETVOL, &v); + if (rc >= 0) if (verbose) - printf ("Left volume = %d, right volume = %d\n", - v.vol[0], v.vol[1]); + printf ("Left volume = %d, right volume = %d\n", + v.vol[0], v.vol[1]); else - printf ("%d %d\n", v.vol[0], v.vol[1]); - else + printf ("%d %d\n", v.vol[0], v.vol[1]); + else printf ("No volume level info available\n"); + } return(0); } int info (char *arg) { struct ioc_toc_header h; int rc, i, n; rc = ioctl (fd, CDIOREADTOCHEADER, &h); if (rc >= 0) { if (verbose) printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n", h.starting_track, h.ending_track, h.len); else printf ("%d %d %d\n", h.starting_track, h.ending_track, h.len); } else { warn("getting toc header"); return (rc); } n = h.ending_track - h.starting_track + 1; rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry)); if (rc < 0) return (rc); if (verbose) { printf ("track start duration block length type\n"); printf ("-------------------------------------------------\n"); } for (i = 0; i < n; i++) { printf ("%5d ", toc_buffer[i].track); prtrack (toc_buffer + i, 0); } printf ("%5d ", toc_buffer[n].track); prtrack (toc_buffer + n, 1); return (0); } void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f) { lba += 150; /* block start offset */ lba &= 0xffffff; /* negative lbas use only 24 bits */ *m = lba / (60 * 75); lba %= (60 * 75); *s = lba / 75; *f = lba % 75; } unsigned int msf2lba (u_char m, u_char s, u_char f) { return (((m * 60) + s) * 75 + f) - 150; } void prtrack (struct cd_toc_entry *e, int lastflag) { int block, next, len; u_char m, s, f; if (msf) { /* Print track start */ printf ("%2d:%02d.%02d ", e->addr.msf.minute, e->addr.msf.second, e->addr.msf.frame); block = msf2lba (e->addr.msf.minute, e->addr.msf.second, e->addr.msf.frame); } else { block = ntohl(e->addr.lba); lba2msf(block, &m, &s, &f); /* Print track start */ printf ("%2d:%02d.%02d ", m, s, f); } if (lastflag) { /* Last track -- print block */ printf (" - %6d - -\n", block); return; } if (msf) next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second, e[1].addr.msf.frame); else next = ntohl(e[1].addr.lba); len = next - block; lba2msf (len, &m, &s, &f); /* Print duration, block, length, type */ printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len, (e->control & 4) ? "data" : "audio"); } int play_track (int tstart, int istart, int tend, int iend) { struct ioc_play_track t; t.start_track = tstart; t.start_index = istart; t.end_track = tend; t.end_index = iend; return ioctl (fd, CDIOCPLAYTRACKS, &t); } int play_blocks (int blk, int len) { struct ioc_play_blocks t; t.blk = blk; t.len = len; return ioctl (fd, CDIOCPLAYBLOCKS, &t); } int setvol (int left, int right) { struct ioc_vol v; v.vol[0] = left; v.vol[1] = right; v.vol[2] = 0; v.vol[3] = 0; return ioctl (fd, CDIOCSETVOL, &v); } int read_toc_entrys (int len) { struct ioc_read_toc_entry t; t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; t.starting_track = 0; t.data_len = len; t.data = toc_buffer; return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t)); } int play_msf (int start_m, int start_s, int start_f, int end_m, int end_s, int end_f) { struct ioc_play_msf a; a.start_m = start_m; a.start_s = start_s; a.start_f = start_f; a.end_m = end_m; a.end_s = end_s; a.end_f = end_f; return ioctl (fd, CDIOCPLAYMSF, (char *) &a); } int status (int *trk, int *min, int *sec, int *frame) { struct ioc_read_subchannel s; struct cd_sub_channel_info data; u_char mm, ss, ff; bzero (&s, sizeof (s)); s.data = &data; s.data_len = sizeof (data); s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT; s.data_format = CD_CURRENT_POSITION; if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0) return -1; *trk = s.data->what.position.track_number; if (msf) { *min = s.data->what.position.reladdr.msf.minute; *sec = s.data->what.position.reladdr.msf.second; *frame = s.data->what.position.reladdr.msf.frame; } else { lba2msf(ntohl(s.data->what.position.reladdr.lba), &mm, &ss, &ff); *min = mm; *sec = ss; *frame = ff; } return s.data->header.audio_status; } char *input (int *cmd) { static char buf[80]; char *p; do { if (verbose) fprintf (stderr, "cdcontrol> "); if (! fgets (buf, sizeof (buf), stdin)) { *cmd = CMD_QUIT; fprintf (stderr, "\r\n"); return (0); } p = parse (buf, cmd); } while (! p); return (p); } char *parse (char *buf, int *cmd) { struct cmdtab *c; char *p; int len; for (p=buf; isspace (*p); p++) continue; if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) { *cmd = CMD_PLAY; return (p); } for (buf = p; *p && ! isspace (*p); p++) continue; len = p - buf; if (! len) return (0); if (*p) { /* It must be a spacing character! */ char *q; *p++ = 0; for (q=p; *q && *q != '\n' && *q != '\r'; q++) continue; *q = 0; } *cmd = -1; for (c=cmdtab; c->name; ++c) { /* Is it an exact match? */ if (! strcasecmp (buf, c->name)) { *cmd = c->command; break; } /* Try short hand forms then... */ if (len >= c->min && ! strncasecmp (buf, c->name, len)) { if (*cmd != -1 && *cmd != c->command) { warnx("ambiguous command"); return (0); } *cmd = c->command; } } if (*cmd == -1) { warnx("invalid command, enter ``help'' for commands"); return (0); } while (isspace (*p)) p++; return p; } int open_cd () { char devbuf[80]; if (fd > -1) return (1); if (*cdname == '/') strcpy (devbuf, cdname); else if (*cdname == 'r') sprintf (devbuf, "/dev/%s", cdname); else sprintf (devbuf, "/dev/r%s", cdname); fd = open (devbuf, O_RDONLY); if (fd < 0 && errno == ENOENT) { strcat (devbuf, DEFAULT_CD_PARTITION); fd = open (devbuf, O_RDONLY); } if (fd < 0) { if (errno == ENXIO) { /* ENXIO has an overloaded meaning here. * The original "Device not configured" should * be interpreted as "No disc in drive %s". */ warnx("no disc in drive %s", devbuf); return (0); } err(1, "%s", devbuf); } return (1); }