1 //**************************************************************************
2 //**
3 //**	##   ##    ##    ##   ##   ####     ####   ###     ###
4 //**	##   ##  ##  ##  ##   ##  ##  ##   ##  ##  ####   ####
5 //**	 ## ##  ##    ##  ## ##  ##    ## ##    ## ## ## ## ##
6 //**	 ## ##  ########  ## ##  ##    ## ##    ## ##  ###  ##
7 //**	  ###   ##    ##   ###    ##  ##   ##  ##  ##       ##
8 //**	   #    ##    ##    #      ####     ####   ##       ##
9 //**
10 //**	$Id: snd_audiocd_linux.cpp 3866 2008-11-17 18:58:49Z dj_jl $
11 //**
12 //**	Copyright (C) 1999-2006 Jānis Legzdiņš
13 //**
14 //**	This program is free software; you can redistribute it and/or
15 //**  modify it under the terms of the GNU General Public License
16 //**  as published by the Free Software Foundation; either version 2
17 //**  of the License, or (at your option) any later version.
18 //**
19 //**	This program is distributed in the hope that it will be useful,
20 //**  but WITHOUT ANY WARRANTY; without even the implied warranty of
21 //**  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22 //**  GNU General Public License for more details.
23 //**
24 //**************************************************************************
25 
26 // HEADER FILES ------------------------------------------------------------
27 
28 #include <unistd.h>
29 #include <sys/ioctl.h>
30 #include <sys/file.h>
31 #include <sys/types.h>
32 #include <fcntl.h>
33 #include <time.h>
34 #include <errno.h>
35 #include <linux/cdrom.h>
36 
37 #include "gamedefs.h"
38 #include "snd_local.h"
39 
40 // MACROS ------------------------------------------------------------------
41 
42 // TYPES -------------------------------------------------------------------
43 
44 class VLinuxCDAudioDevice : public VCDAudioDevice
45 {
46 public:
47 	int			CDFile;
48 
49 	void Init();
50 	void Update();
51 	void Shutdown();
52 	void GetInfo();
53 	void Play(int, bool);
54 	void Pause();
55 	void Resume();
56 	void Stop();
57 	void OpenDoor();
58 	void CloseDoor();
59 };
60 
61 // EXTERNAL FUNCTION PROTOTYPES --------------------------------------------
62 
63 // PUBLIC FUNCTION PROTOTYPES ----------------------------------------------
64 
65 // PRIVATE FUNCTION PROTOTYPES ---------------------------------------------
66 
67 // EXTERNAL DATA DECLARATIONS ----------------------------------------------
68 
69 IMPLEMENT_CD_AUDIO_DEVICE(VLinuxCDAudioDevice, CDDRV_Default, "Default",
70 	"Linux CD audio device", NULL);
71 
72 // PUBLIC DATA DEFINITIONS -------------------------------------------------
73 
74 // PRIVATE DATA DEFINITIONS ------------------------------------------------
75 
76 // CODE --------------------------------------------------------------------
77 
78 //==========================================================================
79 //
80 //	VLinuxCDAudioDevice::Init
81 //
82 //==========================================================================
83 
Init()84 void VLinuxCDAudioDevice::Init()
85 {
86 	guard(VLinuxCDAudioDevice::Init);
87 	int i;
88 
89 	if (GArgs.CheckParm("-nosound") || GArgs.CheckParm("-nocdaudio"))
90 	{
91 		return;
92 	}
93 
94 	VStr cd_dev = "/dev/cdrom";
95 	const char* p = GArgs.CheckValue("-cddev");
96 	if (p)
97 	{
98 		cd_dev = p;
99 	}
100 
101 	CDFile = open(*cd_dev, O_RDONLY);
102 	if (CDFile == -1)
103 	{
104 		GCon->Logf(NAME_Init, "CD_Init: open of \"%s\" failed (%d)",
105 			*cd_dev, errno);
106 		CDFile = -1;
107 		return;
108 	}
109 
110 	for (i = 0; i < 100; i++)
111 		Remap[i] = i;
112 	Initialised = true;
113 	Enabled = true;
114 
115 	GetInfo();
116 	if (!CDValid)
117 	{
118 		GCon->Log(NAME_Init, "CD_Init: No CD in player.");
119 	}
120 
121 	GCon->Log(NAME_Init, "CD Audio Initialised");
122 	unguard;
123 }
124 
125 //==========================================================================
126 //
127 //	VLinuxCDAudioDevice::Update
128 //
129 //==========================================================================
130 
Update()131 void VLinuxCDAudioDevice::Update()
132 {
133 	guard(VLinuxCDAudioDevice::Update);
134 	struct cdrom_subchnl	subchnl;
135 	static time_t			lastchk;
136 
137 	if (!Initialised)
138 		return;
139 
140 	if (!Enabled)
141 		return;
142 
143 	if (Playing && lastchk < time(NULL))
144 	{
145 		lastchk = time(NULL) + 2; //two seconds between chks
146 		subchnl.cdsc_format = CDROM_MSF;
147 		if (ioctl(CDFile, CDROMSUBCHNL, &subchnl) == -1 )
148 		{
149 			GCon->Log(NAME_Dev, "ioctl cdromsubchnl failed");
150 			Playing = false;
151 			return;
152 		}
153 		if (subchnl.cdsc_audiostatus != CDROM_AUDIO_PLAY &&
154 			subchnl.cdsc_audiostatus != CDROM_AUDIO_PAUSED)
155 		{
156 			Playing = false;
157 			if (PlayLooping)
158 				Play(PlayTrack, true);
159 		}
160 	}
161 	unguard;
162 }
163 
164 //==========================================================================
165 //
166 //	VLinuxCDAudioDevice::Shutdown
167 //
168 //==========================================================================
169 
Shutdown()170 void VLinuxCDAudioDevice::Shutdown()
171 {
172 	guard(VLinuxCDAudioDevice::Shutdown);
173 	if (!Initialised)
174 		return;
175 
176 	Stop();
177 	close(CDFile);
178 	CDFile = -1;
179 	Initialised = false;
180 	unguard;
181 }
182 
183 //==========================================================================
184 //
185 //	VLinuxCDAudioDevice::GetInfo
186 //
187 //==========================================================================
188 
GetInfo()189 void VLinuxCDAudioDevice::GetInfo()
190 {
191 	guard(VLinuxCDAudioDevice::GetInfo);
192 	struct cdrom_tochdr		tochdr;
193 
194 	CDValid = false;
195 
196 	if (ioctl(CDFile, CDROMREADTOCHDR, &tochdr) == -1)
197 	{
198 		GCon->Log(NAME_Dev, "ioctl cdromreadtochdr failed");
199 		return;
200 	}
201 
202 	if (tochdr.cdth_trk0 < 1)
203 	{
204 		GCon->Log(NAME_Dev, "CDAudio: no music tracks");
205 		return;
206 	}
207 
208 	CDValid = true;
209 	MaxTrack = tochdr.cdth_trk1;
210 	unguard;
211 }
212 
213 //==========================================================================
214 //
215 //	VLinuxCDAudioDevice::Play
216 //
217 //==========================================================================
218 
Play(int track,bool looping)219 void VLinuxCDAudioDevice::Play(int track, bool looping)
220 {
221 	guard(VLinuxCDAudioDevice::Play);
222 	struct cdrom_tocentry	entry;
223 	struct cdrom_ti			ti;
224 
225 	if (!CDValid)
226 	{
227 		GetInfo();
228 		if (!CDValid)
229 			return;
230 	}
231 
232 	track = Remap[track];
233 
234 	if (track < 1 || track > MaxTrack)
235 	{
236 		GCon->Logf(NAME_Dev, "CDAudio: Bad track number %d.", track);
237 		return;
238 	}
239 
240 	// don't try to play a non-audio track
241 	entry.cdte_track = track;
242 	entry.cdte_format = CDROM_MSF;
243 	if (ioctl(CDFile, CDROMREADTOCENTRY, &entry) == -1)
244 	{
245 		GCon->Log(NAME_Dev, "ioctl cdromreadtocentry failed");
246 		return;
247 	}
248 	if (entry.cdte_ctrl == CDROM_DATA_TRACK)
249 	{
250 		GCon->Logf("CDAudio: track %d is not audio", track);
251 		return;
252 	}
253 
254 	if (Playing)
255 	{
256 		if (PlayTrack == track)
257 			return;
258 		Stop();
259 	}
260 
261 	ti.cdti_trk0 = track;
262 	ti.cdti_trk1 = track;
263 	ti.cdti_ind0 = 1;
264 	ti.cdti_ind1 = 99;
265 
266 	if (ioctl(CDFile, CDROMPLAYTRKIND, &ti) == -1)
267 	{
268 		GCon->Log(NAME_Dev, "ioctl cdromplaytrkind failed");
269 		return;
270 	}
271 
272 	if (ioctl(CDFile, CDROMRESUME) == -1)
273 		GCon->Log(NAME_Dev, "ioctl cdromresume failed");
274 
275 	PlayLooping = looping;
276 	PlayTrack = track;
277 	Playing = true;
278 	unguard;
279 }
280 
281 //==========================================================================
282 //
283 //	VLinuxCDAudioDevice::Pause
284 //
285 //==========================================================================
286 
Pause()287 void VLinuxCDAudioDevice::Pause()
288 {
289 	guard(VLinuxCDAudioDevice::Pause);
290 	if (!Playing)
291 		return;
292 
293 	if (ioctl(CDFile, CDROMPAUSE) == -1)
294 		GCon->Log(NAME_Dev, "ioctl cdrompause failed");
295 
296 	WasPlaying = Playing;
297 	Playing = false;
298 	unguard;
299 }
300 
301 //==========================================================================
302 //
303 //	VLinuxCDAudioDevice::Resume
304 //
305 //==========================================================================
306 
Resume()307 void VLinuxCDAudioDevice::Resume()
308 {
309 	guard(VLinuxCDAudioDevice::Resume);
310 	if (!WasPlaying)
311 		return;
312 
313 	if (ioctl(CDFile, CDROMRESUME) == -1)
314 		GCon->Log(NAME_Dev, "ioctl cdromresume failed");
315 
316 	Playing = true;
317 	unguard;
318 }
319 
320 //==========================================================================
321 //
322 //	VLinuxCDAudioDevice::Stop
323 //
324 //==========================================================================
325 
Stop()326 void VLinuxCDAudioDevice::Stop()
327 {
328 	guard(VLinuxCDAudioDevice::Stop);
329 	if (!Playing)
330 		return;
331 
332 	if (ioctl(CDFile, CDROMSTOP) == -1)
333 		GCon->Log(NAME_Dev, "ioctl cdromstop failed");
334 
335 	WasPlaying = false;
336 	Playing = false;
337 	unguard;
338 }
339 
340 //==========================================================================
341 //
342 //	VLinuxCDAudioDevice::OpenDoor
343 //
344 //==========================================================================
345 
OpenDoor()346 void VLinuxCDAudioDevice::OpenDoor()
347 {
348 	guard(VLinuxCDAudioDevice::OpenDoor);
349 	if (ioctl(CDFile, CDROMEJECT) == -1)
350 		GCon->Log(NAME_Dev, "ioctl cdromeject failed");
351 	unguard;
352 }
353 
354 //==========================================================================
355 //
356 //	VLinuxCDAudioDevice::CloseDoor
357 //
358 //==========================================================================
359 
CloseDoor()360 void VLinuxCDAudioDevice::CloseDoor()
361 {
362 	guard(VLinuxCDAudioDevice::CloseDoor);
363 	if (ioctl(CDFile, CDROMCLOSETRAY) == -1)
364 		GCon->Log(NAME_Dev, "ioctl cdromclosetray failed");
365 	unguard;
366 }
367