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