1 /*
2 * This file is part of WorkMan, the civilized CD player library
3 * Copyright (C) 1991-1997 by Steven Grimm <koreth@midwinter.com>
4 * Copyright (C) by Dirk Försterling <milliByte@DeathsDoor.com>
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public
17 * License along with this library; if not, write to the Free
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 *
21 * ULTRIX 4.2 drive control routines.
22 */
23
24 #if defined(ultrix) || defined(__ultrix)
25
26 #include <errno.h>
27 #include <stdio.h>
28 #include <sys/types.h>
29 #include <fcntl.h>
30 #include <sys/param.h>
31 #include <sys/stat.h>
32 #include <sys/time.h>
33 #include <ustat.h>
34 #include <string.h>
35 #include <sys/rzdisk.h>
36 #include <sys/cdrom.h>
37
38 #include "include/wm_config.h"
39 #include "include/wm_cdtext.h"
40 #include "include/wm_struct.h"
41
42 #define WM_MSG_CLASS WM_MSG_CLASS_PLATFORM
43
44 /*
45 * This structure will be filled with the TOC header and all entries.
46 * Ultrix doesn't seem to allow getting single TOC entries.
47 * - Chris Ross (cross@eng.umd.edu)
48 */
49 struct cd_toc_header_and_entries
50 {
51 struct cd_toc_header cdth;
52 struct cd_toc_entry cdte[CDROM_MAX_TRACK+1];
53 };
54
55 void *malloc();
56 char *strchr();
57
58 int min_volume = 128;
59 int max_volume = 255;
60
61 char * ultrix_cd_device = NULL;
62 /*
63 * fgetline()
64 *
65 * Simulate fgets, but joining continued lines in the output of uerf.
66 */
67
68 #define BUF_SIZE 85 /* Max length of a (real) line */
69
70 char *
fgetline(FILE * fp)71 fgetline( FILE *fp )
72 {
73 static char *retval = NULL;
74 static char holdbuf[BUF_SIZE + 1];
75 char tmp[BUF_SIZE + 1];
76 char *stmp;
77
78 if (!retval) {
79 retval = malloc(BUF_SIZE * 3); /* 3 lines can be joined */
80 if (!retval)
81 return(NULL);
82 else
83 *retval = '\0';
84 }
85
86 if (*holdbuf) {
87 strcpy(retval, holdbuf);
88 retval[strlen(retval)-1] = '\0';
89 memset(holdbuf, 0, BUF_SIZE+1);
90 }
91
92 while (fgets(tmp, BUF_SIZE, fp)) {
93 stmp = tmp + strspn(tmp, " \t");
94 if (*stmp == '_') { /* Continuation line */
95 retval[strlen(retval)-1] = '\0'; /* Trim off C/R */
96 strcat(retval, stmp+1);
97 } else {
98 if (*retval) {
99 strcpy(holdbuf, tmp);
100 holdbuf[strlen(holdbuf)-1] = -1;
101 return retval;
102 } else { /* First line read, keep reading */
103 strcat(retval, stmp);
104 retval[strlen(retval)-1] = '\0';
105 }
106 }
107 }
108
109 return NULL;
110 } /* fgetline() */
111
112 /*
113 * find_cdrom
114 *
115 * Determine the name of the CD-ROM device.
116 *
117 * Read through the boot records (via a call to uerf) and find the SCSI
118 * address of the CD-ROM. If the "CDROM" environment variable is set,
119 * use that instead.
120 */
121 const char*
find_cdrom()122 find_cdrom()
123 {
124 char *data;
125 FILE *uerf;
126 int fds[2];
127 int pid;
128 const char* device = NULL;
129
130 device = getenv("CDROM");
131
132 if (device != NULL) {
133 if(strncmp("/dev/", device, 5) || strstr(device, "/../"))
134 return NULL;
135 }
136
137 pipe(fds);
138
139 if ((pid = fork()) == 0) {
140 close(fds[0]);
141 dup2(fds[1], 1);
142 execl("/etc/uerf", "uerf", "-R", "-r", "300", (void *)0);
143 execl("/usr/sbin/uerf", "uerf", "-R", "-r", "300", (void *)0);
144 return NULL; /* _exit(1); */
145 } else if (pid < 0) {
146 perror("fork");
147 return NULL; /* exit(1); */
148 }
149
150 close(fds[1]);
151 uerf = fdopen(fds[0], "r");
152
153 while (data = fgetline(uerf)) {
154 if (strstr(data, "RRD42")) {
155 char *device_p;
156
157 ultrix_cd_device = (char *)malloc(sizeof("/dev/rrz##c"));
158 strcpy(ultrix_cd_device, "/dev/r");
159 device_p = strstr(data, "rz");
160 device_p[(int)(strchr(device_p, ' ') - device_p)] = '\0';
161 strcat(ultrix_cd_device, device_p);
162 strcat(ultrix_cd_device, "c");
163 device = ultrix_cd_device;
164 break;
165 }
166 }
167
168 fclose(uerf);
169
170 if (device == NULL) {
171 fprintf(stderr, "No cdrom (RRD42) is installed on this system\n");
172 return NULL; /* exit(1); */
173 }
174
175 kill(pid, 15);
176 (void)wait((int *)NULL);
177 return device;
178 } /* find_cdrom() */
179
180 /*
181 * initialize the drive. a no-op for the generic driver.
182 */
183 int
gen_init(struct wm_drive * d)184 gen_init( struct wm_drive *d )
185 {
186 return 0;
187 } /* gen_init() */
188
189
190 /*
191 * Open the CD device and figure out what kind of drive is attached.
192 */
193 int
gen_open(struct wm_drive * d)194 gen_open( struct wm_drive *d )
195 {
196 if (d->fd >= 0) { /* Device already open? */
197 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "gen_open(): [device is open (fd=%d)]\n", d->fd);
198 return 0;
199 }
200
201 d->fd = open(d->cd_device, 0);
202 if (d->fd < 0) {
203 if (errno == EACCES)
204 return -EACCES;
205 else if (errno != EINTR)
206 return -6;
207
208 /* No CD in drive. */
209 return 1;
210 }
211
212 return 0;
213 } /* gen_open() */
214
215 /*
216 * Send an arbitrary SCSI command to a device.
217 */
218 int
gen_scsi(struct wm_drive * d,unsigned char * cdb,int cdblen,void * retbuf,int retbuflen,int getreply)219 gen_scsi( struct wm_drive *d, unsigned char *cdb, int cdblen,
220 void *retbuf, int retbuflen, int getreply )
221 {
222 /* ULTRIX doesn't have a SCSI passthrough interface, does it? */
223 return -1;
224 } /* gen_scsi() */
225
226 int
gen_close(struct wm_drive * d)227 gen_close( struct wm_drive *d )
228 {
229 if(d->fd > -1) {
230 wm_lib_message(WM_MSG_LEVEL_DEBUG|WM_MSG_CLASS, "closing the device\n");
231 close(d->fd);
232 d->fd = -1;
233 }
234 return 0;
235 }
236
237 /*
238 * Get the current status of the drive: the current play mode, the absolute
239 * position from start of disc (in frames), and the current track and index
240 * numbers if the CD is playing or paused.
241 */
242 int
gen_get_drive_status(struct wm_drive * d,int oldmode,int * mode,int * pos,int * track,int * index)243 gen_get_drive_status( struct wm_drive *d, int oldmode,
244 int *mode, int *pos, int *track, int *index)
245 {
246 struct cd_sub_channel sc;
247 struct cd_subc_channel_data scd;
248
249 /* If we can't get status, the CD is ejected, so default to that. */
250 *mode = WM_CDM_EJECTED;
251
252 sc.sch_address_format = CDROM_MSF_FORMAT;
253 sc.sch_data_format = CDROM_CURRENT_POSITION;
254 sc.sch_track_number = 0;
255 sc.sch_alloc_length = sizeof(scd);
256 sc.sch_buffer = (caddr_t)&scd;
257
258 /* Is the device open? */
259 if (d->fd < 0) {
260 switch (d->proto.open(d)) {
261 case -1: /* error */
262 return -1;
263
264 case 1: /* retry */
265 return 0;
266 }
267 }
268
269 if (ioctl(d->fd, CDROM_READ_SUBCHANNEL, &sc))
270 return 0; /* ejected */
271
272 switch (scd.scd_header.sh_audio_status) {
273 case AS_PLAY_IN_PROGRESS:
274 *mode = WM_CDM_PLAYING;
275 break;
276
277 case AS_PLAY_PAUSED:
278 if (oldmode == WM_CDM_PLAYING || oldmode == WM_CDM_PAUSED)
279 *mode = WM_CDM_PAUSED;
280 else
281 *mode = WM_CDM_STOPPED;
282 break;
283
284 case AS_PLAY_COMPLETED:
285 *mode = WM_CDM_TRACK_DONE; /* waiting for next track. */
286 break;
287
288 case AS_NO_STATUS:
289 *mode = WM_CDM_STOPPED;
290 break;
291 }
292
293 switch(*mode) {
294 case WM_CDM_PLAYING:
295 case WM_CDM_PAUSED:
296 *pos = scd.scd_position_data.scp_absaddr.msf.m_units * 60 * 75 +
297 scd.scd_position_data.scp_absaddr.msf.s_units * 75 +
298 scd.scd_position_data.scp_absaddr.msf.f_units;
299 *track = scd.scd_position_data.scp_track_number;
300 *index = scd.scd_position_data.scp_index_number;
301 break;
302 }
303
304 return 0;
305 } /* gen_get_drive_status() */
306
307 /*
308 * Get the number of tracks on the CD.
309 */
310 int
gen_get_trackcount(struct wm_drive * d,int * tracks)311 gen_get_trackcount( struct wm_drive *d, int *tracks )
312 {
313 struct cd_toc_header hdr;
314
315 if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
316 return (-1);
317
318 *tracks = hdr.th_ending_track;
319
320 return 0;
321 } /* gen_get_trackcount() */
322
323 /*
324 * Get the start time and mode (data or audio) of a track.
325 *
326 * XXX - this should get cached, but that means keeping track of ejects.
327 */
328 int
gen_get_trackinfo(struct wm_drive * d,int track,int * data,int * startframe)329 gen_get_trackinfo(struct wm_drive *d, int track, int *data, int *startframe)
330 {
331 struct cd_toc toc;
332 struct cd_toc_header hdr;
333 struct cd_toc_header_and_entries toc_buffer;
334
335 if (ioctl(d->fd, CDROM_TOC_HEADER, &hdr))
336 return (-1);
337
338 bzero((char *)&toc_buffer, sizeof(toc_buffer));
339 toc.toc_address_format = CDROM_MSF_FORMAT;
340 toc.toc_starting_track = 0;
341 toc.toc_alloc_length = (u_short)(((hdr.th_data_len1 << 8) +
342 hdr.th_data_len0) & 0xfff) + 2;
343 toc.toc_buffer = (caddr_t)&toc_buffer;
344
345 if (ioctl(d->fd, CDROM_TOC_ENTRYS, &toc))
346 return (-1);
347
348 if (track == 0)
349 track = hdr.th_ending_track + 1;
350
351 *data = (toc_buffer.cdte[track-1].te_control & CDROM_DATA_TRACK) ? 1:0;
352 *startframe = toc_buffer.cdte[track - 1].te_absaddr.msf.m_units*60*75 +
353 toc_buffer.cdte[track - 1].te_absaddr.msf.s_units * 75 +
354 toc_buffer.cdte[track - 1].te_absaddr.msf.f_units;
355
356 return 0;
357 } /* gen_get_trackinfo() */
358
359 /*
360 * Get the number of frames on the CD.
361 */
362 int
gen_get_cdlen(struct wm_drive * d,int * frames)363 gen_get_cdlen(struct wm_drive *d, int *frames)
364 {
365 int tmp;
366 return gen_get_trackinfo(d, 0, &tmp, frames);
367 } /* gen_get_cdlen() */
368
369 /*
370 * Play the CD from one position to another (both in frames.)
371 */
372 int
gen_play(struct wm_drive * d,int start,int end)373 gen_play( struct wm_drive *d, int start, int end )
374 {
375 struct cd_play_audio_msf msf;
376
377 msf.msf_starting_M_unit = start / (60*75);
378 msf.msf_starting_S_unit = (start % (60*75)) / 75;
379 msf.msf_starting_F_unit = start % 75;
380 msf.msf_ending_M_unit = end / (60*75);
381 msf.msf_ending_S_unit = (end % (60*75)) / 75;
382 msf.msf_ending_F_unit = end % 75;
383
384 if (ioctl(d->fd, SCSI_START_UNIT))
385 return -1;
386
387 if (ioctl(d->fd, CDROM_PLAY_MSF, &msf))
388 return -2;
389
390 return 0;
391 } /* gen_play() */
392
393 /*
394 * Pause the CD.
395 */
396 int
gen_pause(struct wm_drive * d)397 gen_pause( struct wm_drive *d )
398 {
399 return ioctl(d->fd, CDROM_PAUSE_PLAY);
400 } /* gen_pause() */
401
402 /*
403 * Resume playing the CD (assuming it was paused.)
404 */
405 int
gen_resume(struct wm_drive * d)406 gen_resume( struct wm_drive *d )
407 {
408 return ioctl(d->fd, CDROM_RESUME_PLAY);
409 } /* gen_resume() */
410
411 /*
412 * Stop the CD.
413 */
414 int
gen_stop(struct wm_drive * d)415 gen_stop( struct wm_drive *d )
416 {
417 return ioctl(d->fd, SCSI_STOP_UNIT);
418 } /* gen_stop() */
419
420 /*
421 * Eject the current CD, if there is one.
422 */
423 int
gen_eject(struct wm_drive * d)424 gen_eject(struct wm_drive *d)
425 {
426 /* On some systems, we can check to see if the CD is mounted. */
427 struct stat stbuf;
428 struct ustat ust;
429
430 if (fstat(d->fd, &stbuf) != 0)
431 return -2;
432
433 /* Is this a mounted filesystem? */
434 if (ustat(stbuf.st_rdev, &ust) == 0)
435 return -3;
436
437 return ioctl(d->fd, CDROM_EJECT_CADDY);
438 } /* gen_eject() */
439
440 /*----------------------------------------*
441 * Close the CD tray
442 *
443 * Please edit and send changes to
444 * milliByte@DeathsDoor.com
445 *----------------------------------------*/
446
447 int
gen_closetray(struct wm_drive * d)448 gen_closetray(struct wm_drive *d)
449 {
450 return -1;
451 } /* gen_closetray() */
452
453
454 /*
455 * scale_volume(vol, max)
456 *
457 * Return a volume value suitable for passing to the CD-ROM drive. "vol"
458 * is a volume slider setting; "max" is the slider's maximum value.
459 *
460 * On Sun and DEC CD-ROM drives, the amount of sound coming out the jack
461 * increases much faster toward the top end of the volume scale than it
462 * does at the bottom. To make up for this, we make the volume scale look
463 * sort of logarithmic (actually an upside-down inverse square curve) so
464 * that the volume value passed to the drive changes less and less as you
465 * approach the maximum slider setting. The actual formula looks like
466 *
467 * (max^2 - (max - vol)^2) * (max_volume - min_volume)
468 * v = --------------------------------------------------- + min_volume
469 * max^2
470 *
471 * If your system's volume settings aren't broken in this way, something
472 * like the following should work:
473 *
474 * return ((vol * (max_volume - min_volume)) / max + min_volume);
475 */
scale_volume(int vol,int max)476 scale_volume( int vol, int max )
477 {
478 return ((max * max - (max - vol) * (max - vol)) *
479 (max_volume - min_volume) / (max * max) + min_volume);
480 } /* scale_volume() */
481
482
483 /*
484 * unscale_volume(cd_vol, max)
485 *
486 * Given a value between min_volume and max_volume, return the volume slider
487 * value needed to achieve that value.
488 *
489 * Rather than perform floating-point calculations to reverse the above
490 * formula, we simply do a binary search of scale_volume()'s return values.
491 */
492 static int
unscale_volume(int cd_vol,int max)493 unscale_volume( int cd_vol, int max )
494 {
495 int vol = 0, top = max, bot = 0, scaled;
496
497 while (bot <= top) {
498 vol = (top + bot) / 2;
499 scaled = scale_volume(vol, max);
500 if (cd_vol == scaled)
501 break;
502 if (cd_vol < scaled)
503 top = vol - 1;
504 else
505 bot = vol + 1;
506 }
507
508 if (vol < 0)
509 vol = 0;
510 else if (vol > max)
511 vol = max;
512
513 return vol;
514 } /* unscale_volume() */
515
516 /*
517 * Set the volume level for the left and right channels. Their values
518 * range from 0 to 100.
519 */
520 int
gen_set_volume(struct wm_drive * d,int left,int right)521 gen_set_volume( struct wm_drive *d, int left, int right )
522 {
523 struct cd_playback pb;
524 struct cd_playback_status ps;
525 struct cd_playback_control pc;
526
527 left = scale_volume(left, 100);
528 right = scale_volume(right, 100);
529
530 bzero((char *)&pb, sizeof(pb));
531 bzero((char *)&ps, sizeof(ps));
532 bzero((char *)&pc, sizeof(pc));
533
534 pb.pb_alloc_length = sizeof(ps);
535 pb.pb_buffer = (caddr_t)&ps;
536
537 if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
538 return -1;
539
540 pc.pc_chan0_select = ps.ps_chan0_select;
541 pc.pc_chan0_volume = (left < CDROM_MIN_VOLUME) ?
542 CDROM_MIN_VOLUME : (left > CDROM_MAX_VOLUME) ?
543 CDROM_MAX_VOLUME : left;
544 pc.pc_chan1_select = ps.ps_chan1_select;
545 pc.pc_chan1_volume = (right < CDROM_MIN_VOLUME) ?
546 CDROM_MIN_VOLUME : (right > CDROM_MAX_VOLUME) ?
547 CDROM_MAX_VOLUME : right;
548
549 pb.pb_alloc_length = sizeof(pc);
550 pb.pb_buffer = (caddr_t)&pc;
551
552 if (ioctl(d->fd, CDROM_PLAYBACK_CONTROL, &pb))
553 return -1;
554
555 return 0;
556 } /* gen_set_volume() */
557
558 /*
559 * Read the initial volume from the drive, if available. Each channel
560 * ranges from 0 to 100, with -1 indicating data not available.
561 */
562 int
gen_get_volume(struct wm_drive * d,int * left,int * right)563 gen_get_volume(struct wm_drive *d, int *left, int *right)
564 {
565 struct cd_playback pb;
566 struct cd_playback_status ps;
567
568 bzero((char *)&pb, sizeof(pb));
569 bzero((char *)&ps, sizeof(ps));
570
571 pb.pb_alloc_length = sizeof(ps);
572 pb.pb_buffer = (caddr_t)&ps;
573
574 if (d->fd >= 0) {
575 if (ioctl(d->fd, CDROM_PLAYBACK_STATUS, &pb))
576 *left = *right = -1;
577 else {
578 *left = unscale_volume(ps.ps_chan0_volume, 100);
579 *right = unscale_volume(ps.ps_chan1_volume, 100);
580 }
581 } else
582 *left = *right = -1;
583
584 return 0;
585 } /* gen_get_volume() */
586
587 #endif
588