1 /* libcda; BeOS component.
2  *
3  * Partly based on CDButton sample, by Pavel Cisler.
4  *
5  * Peter Wang <tjaden@users.sf.net>
6  */
7 
8 #include <Entry.h>
9 #include <Directory.h>
10 #include <Path.h>
11 #include <errno.h>
12 #include <scsi.h>
13 #include <string.h>
14 #include <unistd.h>
15 #include "libcda.h"
16 
17 
18 #define MID(x,y,z)   MAX((x), MIN((y), (z)))
19 
20 
21 static int fd = -1;
22 
23 static char _cd_error[256];
24 const char *cd_error = _cd_error;
25 
26 
copy_cd_error(void)27 static void copy_cd_error(void)
28 {
29     strncpy(_cd_error, strerror(errno), sizeof _cd_error);
30     _cd_error[sizeof _cd_error - 1] = 0;
31 }
32 
33 
search_cd_drive(const char * root)34 static int search_cd_drive(const char *root)
35 {
36     BDirectory dir;
37     BEntry entry;
38     BPath path;
39     int fd;
40     device_geometry geo;
41 
42     dir.SetTo(root);
43     if (dir.InitCheck() != B_NO_ERROR)
44 	return -1;
45 
46     dir.Rewind();
47 
48     while (dir.GetNextEntry(&entry) >= 0) {
49 
50 	if (entry.GetPath(&path) != B_NO_ERROR)
51 	    continue;
52 
53 	if (entry.IsDirectory()) {
54 	    if (strcmp(path.Leaf(), "floppy") == 0)
55 		continue;
56 
57 	    fd = search_cd_drive(path.Path());
58 	    if (fd >= 0)
59 		return fd;
60 	}
61 	else if (strcmp(path.Leaf(), "raw") == 0) {
62 
63 	    fd = open(path.Path(), O_RDONLY);
64 	    if (fd < 0)
65 		continue;
66 
67 	    if (ioctl(fd, B_GET_GEOMETRY, &geo, sizeof geo) >= 0)
68 		if (geo.device_type == B_CD)
69 		    return fd;
70 
71 	    close(fd);
72 	}
73     }
74 
75     return -1;
76 }
77 
78 
cd_init(void)79 int cd_init(void)
80 {
81     fd = search_cd_drive("/dev/disk");
82     if (fd < 0) {
83 	copy_cd_error();
84 	return -1;
85     }
86     else {
87 	return 0;
88     }
89 }
90 
91 
cd_exit(void)92 void cd_exit(void)
93 {
94     if (fd >= 0) {
95 	close(fd);
96 	fd = -1;
97     }
98 }
99 
100 
101 /* prevents us from getting a stupid `media changed' error */
get_media_status(void)102 static void get_media_status(void)
103 {
104     status_t status;
105     ioctl(fd, B_GET_MEDIA_STATUS, &status, sizeof status);
106 }
107 
108 
play(int start,int end)109 static int play(int start, int end)
110 {
111     scsi_play_track p;
112     int first, last;
113 
114     get_media_status();
115 
116     cd_get_tracks(&first, &last);
117     if ((start > end) || (start < first) || (end > last)) {
118 	strcpy(_cd_error, "Invalid track range");
119 	return -1;
120     }
121 
122     p.start_track = start;
123     p.start_index = 1;
124     p.end_track = end;
125     p.end_index = 1;
126 
127     if (ioctl(fd, B_SCSI_PLAY_TRACK, &p) != B_NO_ERROR) {
128 	copy_cd_error();
129 	return -1;
130     }
131 
132     return 0;
133 }
134 
135 
cd_play(int track)136 int cd_play(int track)
137 {
138     return play(track, track);
139 }
140 
141 
cd_play_range(int start,int end)142 int cd_play_range(int start, int end)
143 {
144     return play(start, end);
145 }
146 
147 
cd_play_from(int track)148 int cd_play_from(int track)
149 {
150     return play(track, 100);
151 }
152 
153 
cd_current_track(void)154 int cd_current_track(void)
155 {
156     scsi_position pos;
157 
158     if ((ioctl(fd, B_SCSI_GET_POSITION, &pos) != B_NO_ERROR)
159 	|| (!pos.position[1]) || (pos.position[1] >= 0x13)
160 	|| ((pos.position[1] == 0x12) && (!pos.position[6])))
161 	return 0;
162 
163     return pos.position[6];
164 }
165 
166 
cd_pause(void)167 void cd_pause(void)
168 {
169     ioctl(fd, B_SCSI_PAUSE_AUDIO);
170 }
171 
172 
cd_resume(void)173 void cd_resume(void)
174 {
175     ioctl(fd, B_SCSI_RESUME_AUDIO);
176 }
177 
178 
cd_is_paused(void)179 int cd_is_paused(void)
180 {
181     scsi_position pos;
182 
183     if (ioctl(fd, B_SCSI_GET_POSITION, &pos) != B_NO_ERROR)
184 	return 0;
185 
186     if ((!pos.position[1]) || (pos.position[1] >= 0x13)
187 	|| ((pos.position[1] == 0x12) && (!pos.position[6]))
188 	|| (pos.position[1] == 0x11))
189 	return 0;
190 
191     return 1;
192 }
193 
194 
cd_stop(void)195 void cd_stop(void)
196 {
197     ioctl(fd, B_SCSI_STOP_AUDIO);
198 }
199 
200 
cd_get_tracks(int * first,int * last)201 int cd_get_tracks(int *first, int *last)
202 {
203     scsi_toc toc;
204 
205     get_media_status();
206 
207     if (ioctl(fd, B_SCSI_GET_TOC, &toc) != B_NO_ERROR) {
208 	copy_cd_error();
209 	return -1;
210     }
211 
212     if (first) *first = 1; /* I don't have the SCSI standard on me. */
213     if (last)  *last = toc.toc_data[3];
214 
215     return 0;
216 }
217 
218 
cd_is_audio(int track)219 int cd_is_audio(int track)
220 {
221     scsi_toc toc;
222     int first, last;
223 
224     get_media_status();
225 
226     if (ioctl(fd, B_SCSI_GET_TOC, &toc) != B_NO_ERROR) {
227 	copy_cd_error();
228 	return -1;
229     }
230 
231     if (cd_get_tracks(&first, &last) < 0)
232 	return -1;
233 
234     if ((track < first) || (track > last)) {
235 	strcpy(_cd_error, "Track out of range");
236 	return -1;
237     }
238 
239     /*
240      * I derived from a snippet from a mailing list archive somewhere.
241      * Don't ask me how it works :-)
242      */
243     return !(toc.toc_data[((track - toc.toc_data[2]) * 8) + 4 + 1] & 4);
244 }
245 
246 
cd_get_volume(int * c0,int * c1)247 void cd_get_volume(int *c0, int *c1)
248 {
249     scsi_volume vol;
250 
251     get_media_status();
252 
253     ioctl(fd, B_SCSI_GET_VOLUME, &vol, sizeof vol);
254     if (c0) *c0 = vol.port0_volume;
255     if (c1) *c1 = vol.port1_volume;
256 }
257 
258 
cd_set_volume(int c0,int c1)259 void cd_set_volume(int c0, int c1)
260 {
261     scsi_volume vol;
262 
263     get_media_status();
264 
265     vol.flags = B_SCSI_PORT0_VOLUME | B_SCSI_PORT1_VOLUME;
266     vol.port0_volume = MID(0, c0, 255);
267     vol.port1_volume = MID(0, c1, 255);
268 
269     ioctl(fd, B_SCSI_SET_VOLUME, &vol, sizeof vol);
270 }
271 
272 
door_status(void)273 static int door_status(void)
274 {
275     status_t status;
276 
277     if (ioctl(fd, B_GET_MEDIA_STATUS, &status, sizeof status) == B_NO_ERROR)
278 	return -1;
279 
280     return (status == B_DEV_DOOR_OPEN) ? 1 : 0;
281 }
282 
283 #define door_open()	(door_status() == 1)
284 #define door_closed()	(door_status() == 0)
285 
286 
cd_eject(void)287 void cd_eject(void)
288 {
289     if (!door_open())
290 	ioctl(fd, B_EJECT_DEVICE);
291 }
292 
293 
cd_close(void)294 void cd_close(void)
295 {
296     if (door_open())
297 	ioctl(fd, B_LOAD_MEDIA);
298 }
299