1 /* OpenCP Module Player
2  * copyright (c) '94-'10 Niklas Beisert <nbeisert@physik.tu-muenchen.de>
3  *
4  * Samples device for OSS input
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program 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
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  * revision history: (please note changes here)
21  *  -ss040906   Stian Skjelstad <stian@nixia.no>
22  *    -first release
23  */
24 
25 #include "config.h"
26 #include <errno.h>
27 #include <fcntl.h>
28 #ifdef HAVE_SYS_SOUNDCARD_H
29 #include <sys/soundcard.h>
30 #endif
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <sys/ioctl.h>
34 #include <sys/stat.h>
35 #include <sys/types.h>
36 #include <string.h>
37 #include <unistd.h>
38 #include "types.h"
39 #include "boot/plinkman.h"
40 #include "boot/psetting.h"
41 #include "dev/devigen.h"
42 #include "dev/imsdev.h"
43 #include "dev/sampler.h"
44 #include "stuff/imsrtns.h"
45 #ifdef SMP_DEBUG
46 #include "stuff/poutput.h"
47 #endif
48 
49 #define REVSTEREO 1
50 
51 extern struct sounddevice smpOSS;
52 static struct deviceinfo currentcard;
53 
54 static int fd_dsp=-1, fd_mixer=-1;
55 static unsigned char *sampbuf;
56 static int buflen, bufpos;
57 
58 static int stereo;
59 static int bit16;
60 
61 static int igain;
62 
getbufpos(void)63 static int getbufpos(void)
64 {
65 	while (1)
66 	{
67 		int target, result;
68 		if (bufpos==buflen)
69 			bufpos=0;
70 		target=buflen-bufpos;
71 		result=read(fd_dsp, sampbuf+bufpos, target);
72 		if (result>0)
73 			bufpos+=result;
74 		if (result!=target) /* We don't care if we overrun buffer */
75 			break;
76 	}
77 	return bufpos;
78 }
79 
SetOptions(int rate,int opt)80 static void SetOptions(int rate, int opt)
81 {
82 	int tmp;
83 
84 	int newopt;
85 
86 	int fd;
87 
88 	if (fd_dsp<0)
89 	{
90 		if ((fd=open(currentcard.path, O_RDONLY|O_NONBLOCK))<0)
91 		{
92 #ifdef OSS_DEBUG
93 			fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", currentcard.path, strerror(errno));
94 #endif
95 			smpRate=rate; /* damn it... device currently busy */
96 			smpOpt=opt;
97 			return;
98 		}
99 	} else
100 		fd=fd_dsp;
101 
102 	if (opt&SMP_16BIT)
103 		tmp=16;
104 	else
105 		tmp=8;
106 	if (ioctl(fd, SOUND_PCM_WRITE_BITS, &tmp)<0)
107 	{
108 #ifdef OSS_DEBUG
109 		perror("devsoss: ioctl(fd_dsp, SOUND_PCM_WRITE_BITS, &tmp)");
110 #endif
111 	}
112 	if ((bit16=(tmp==16)))
113 		newopt = SMP_16BIT | SMP_SIGNEDOUT;
114 	else
115 		newopt = 0;
116 
117 	if (opt&SMP_STEREO)
118 		tmp=2;
119 	else
120 		tmp=1;
121 	if (ioctl(fd, SOUND_PCM_WRITE_CHANNELS, &tmp)<0)
122 	{
123 #ifdef OSS_DEBUG
124 		perror("devsoss: ioctl(fd_dsp, SOUND_PCM_WRITE_CHANNELS, tmp)");
125 #endif
126 	}
127 	if ((stereo=(tmp==2)))
128 		newopt |= SMP_STEREO;
129 
130 	if (ioctl(fd, SOUND_PCM_WRITE_RATE, &rate)<0)
131 	{
132 #ifdef OSS_DEBUG
133 		perror("devposs: ioctl(fd_dsp, SOUND_PCM_WRITE_RATE, rate)");
134 #endif
135 	}
136 
137 	if (currentcard.opt&REVSTEREO)
138 		newopt|=SMP_REVERSESTEREO;
139 
140 	smpRate=rate;
141 	smpOpt=newopt;
142 
143 	if (fd_dsp<0) /* nasty hack */
144 		close(fd);
145 }
146 
ossSetSource(int src)147 static void ossSetSource(int src)
148 {
149 	int oss_src;
150 	if (fd_mixer<0)
151 		return;
152 	switch (src)
153 	{
154 		case SMP_MIC:
155 			oss_src=1<<SOUND_MIXER_MIC;
156 			break;
157 		case SMP_LINEIN:
158 			oss_src=1<<SOUND_MIXER_LINE;
159 			break;
160 		default: /* SMP_CD: */
161 			oss_src=1<<SOUND_MIXER_CD;
162 	}
163 	if ((ioctl(fd_mixer, SOUND_MIXER_WRITE_RECSRC, &oss_src)))
164 	{
165 #ifdef OSS_DEBUG
166 		perror("devsoss: ioctl(fd_mixer, SOUND_MIXER_WRITE_RECSRC, &oss_src)");
167 #endif
168 	}
169 }
170 
ossSample(unsigned char ** buf,int * len)171 static int ossSample(unsigned char **buf, int *len)
172 {
173 #ifdef OSS_DEBUG
174 	int tmp;
175 #endif
176 
177 	if (*len>65536)
178 		*len=65536;
179 	if (*len<4096)
180 		*len=4096;
181 
182 	*buf=sampbuf=malloc(*len);
183 
184 	memsetd(*buf, (smpOpt&SMP_SIGNEDOUT)?0:(smpOpt&SMP_16BIT)?0x80008000:0x80808080, (*len)>>2);
185 
186 	buflen=*len;
187 	bufpos=0;
188 	smpGetBufPos=getbufpos;
189 
190 	if ((fd_dsp=open(currentcard.path, O_RDONLY|O_NONBLOCK))<0)
191 	{
192 #ifdef OSS_DEBUG
193 		fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", currentcard.path, strerror(errno));
194 		sleep(3);
195 #endif
196 		return 0;
197 	}
198 	if (fcntl(fd_dsp, F_SETFD, FD_CLOEXEC)<0)
199 		perror("devsoss: fcntl(fd_dsp, F_SETFD, FD_CLOEXEC)");
200 #ifdef OSS_DEBUG
201  #if defined(OSS_GETVERSION)
202 	if (ioctl(fd_dsp, OSS_GETVERSION, &tmp)<0)
203 		tmp=0;
204 	if (tmp<361)
205 		tmp= ((tmp&0xf00)<<8) | ((tmp&0xf0)<<4) | (tmp&0xf);
206 
207 	fprintf(stderr, "devsoss: compiled agains OSS version %d.%d.%d, version %d.%d.%d detected\n", (SOUND_VERSION&0xff0000)>>16, (SOUND_VERSION&0xff00)>>8, SOUND_VERSION&0xff, (tmp&0xff0000)>>16, (tmp&0xff00)>>8, tmp&0xff);
208  #elif defined(SOUND_VERSION)
209 	fprintf(stderr, "devsoss: compiled agains OSS version %d.%d.%d\n", (SOUND_VERSION&0xff0000)>>16, (SOUND_VERSION&0xff00)>>8, SOUND_VERSION&0xff);
210  #endif
211 #endif
212 
213 #ifdef SMP_DEBUG
214 	smpDebug=ossDebug;
215 #endif
216 
217 	smpSetOptions(smpRate, smpOpt); /* nasty hack */
218 
219 	if ((fd_mixer>=0)&&igain>=0)
220 	{
221 		int tmp;
222 		if (igain>100)
223 			igain=100;
224 		tmp=((uint8_t)igain)|(((uint8_t)igain)<<8);
225 		if (ioctl(fd_mixer, SOUND_MIXER_WRITE_IGAIN, &tmp))
226 		{
227 #ifdef OSS_DEBUG
228 			perror("devsoss: ioctl(fd_mixer, SOUND_MIXER_WRITE_IGAIN, &tmp)");
229 #endif
230 		}
231 	}
232 
233 	return 1;
234 }
235 
ossStop(void)236 static void ossStop(void)
237 {
238 	free(sampbuf);
239 #ifdef SMP_DEBUG
240 	smpDebug=0;
241 #endif
242 	if (fd_dsp)
243 	{
244 		close(fd_dsp);
245 		fd_dsp=-1;
246 	}
247 }
248 
ossInit(const struct deviceinfo * card)249 static int ossInit(const struct deviceinfo *card)
250 {
251 	memcpy(&currentcard, card, sizeof(struct deviceinfo));
252 
253 	igain=(int8_t)(card->opt>>8);
254 
255 	smpSetOptions=SetOptions;
256 	smpSample=ossSample;
257 	smpStop=ossStop;
258 	smpSetSource=ossSetSource;
259 
260 	if (!card->mixer[0])
261 		fd_mixer=-1;
262 	else if ((fd_mixer=open(card->mixer, O_RDWR|O_NONBLOCK))<0)
263 	{
264 #ifdef OSS_DEBUG
265 		fprintf(stderr, "devsoss: open(%s, O_RDWR|O_NONBLOCK): %s\n", card->mixer, strerror(errno));
266 		sleep(3);
267 #endif
268 	} else {
269 		if (fcntl(fd_mixer, F_SETFD, FD_CLOEXEC))
270 			perror("fcntl(fd_mixer, F_SETFD, FD_CLOEXEC)");
271 	}
272 
273 	smpSetOptions(44100, SMP_STEREO|SMP_16BIT);
274 	smpSetSource(SMP_LINEIN);
275 
276 	return 1;
277 }
278 
ossClose(void)279 static void ossClose(void)
280 {
281 	smpSample=0;
282 	smpStop=0;
283 	smpSetOptions=0;
284 	smpSetSource=0;
285 	if (fd_dsp>=0)
286 		close(fd_dsp);
287 	fd_dsp=-1;
288 	if (fd_mixer>=0)
289 		close(fd_mixer);
290 	fd_mixer=-1;
291 }
292 
ossDetect(struct deviceinfo * card)293 static int ossDetect(struct deviceinfo *card)
294 {
295 	struct stat st;
296 #if defined(OSS_GETVERSION)
297 	int tmp;
298 #endif
299 	char *temp;
300 
301 	card->devtype=&smpOSS;
302 	card->port=-1;
303 	card->port2=-1;
304 	card->subtype=-1;
305 	card->mem=0;
306 	if ((card->chan<=0)||(card->chan>2))
307 		card->chan=2;
308 	if ((temp=getenv("DSP")))
309 	{
310 #ifdef OSS_DEBUG
311 		fprintf(stderr, "devsoss: $DSP found\n");
312 #endif
313 		strncpy(card->path, temp, DEVICE_NAME_MAX);
314 		card->path[DEVICE_NAME_MAX-1]=0;
315 	} else if (!card->path[0])
316 	{
317 #ifdef OSS_DEBUG
318 		fprintf(stderr, "devsoss: path not set, using /dev/dsp\n");
319 #endif
320 		strcpy(card->path, "/dev/dsp");
321 	}
322 	if ((temp=getenv("MIXER")))
323 	{
324 #ifdef OSS_DEBUG
325 		fprintf(stderr, "devsoss: $MIXER found\n");
326 #endif
327 		strncpy(card->mixer, temp, DEVICE_NAME_MAX);
328 		card->mixer[DEVICE_NAME_MAX-1]=0;
329 	}
330 	if (stat(card->path, &st))
331 	{
332 #ifdef OSS_DEBUG
333 		fprintf(stderr, "devsoss: stat(%s, &st): %s\n", card->path, strerror(errno));
334 #endif
335 		return 0;
336 	}
337 
338 	/* test if basic OSS functions exists */
339 	if ((fd_dsp=open(card->path, O_RDONLY|O_NONBLOCK))<0)
340 	{
341 		if (errno==EAGAIN)
342 		{
343 #ifdef OSS_DEBUG
344 			fprintf(stderr, "devsoss: OSS detected (EGAIN)\n");
345 #endif
346 			return 1;
347 		}
348 #ifdef OSS_DEBUG
349 		fprintf(stderr, "devsoss: open(%s, O_RDONLY|O_NONBLOCK): %s\n", card->path, strerror(errno));
350 #endif
351 		return 0;
352 	}
353 
354 #if defined(OSS_GETVERSION)
355 	if (ioctl(fd_dsp, OSS_GETVERSION, &tmp)<0)
356 	{
357 #ifdef OSS_DEBUG
358 		perror("devposs: (warning) ioctl(fd_dsp, OSS_GETVERSION, &tmp)");
359 #endif
360 	}
361 #endif
362 
363 #ifdef OSS_DEBUG
364 	fprintf(stderr, "devposs: OSS detected\n");
365 #endif
366 	close(fd_dsp);
367 	fd_dsp=-1;
368 
369 	return 1; /* device exists, so we are happy.. can do better tests later */
370 }
371 
ossGetOpt(const char * sec)372 static uint32_t ossGetOpt(const char *sec)
373 {
374 	uint32_t opt=0;
375 	int8_t igain;
376 
377 	if (cfGetProfileBool(sec, "revstereo", 0, 0))
378 		opt|=REVSTEREO;
379 	igain=cfGetProfileInt(sec, "igain", -1, 10);
380 	opt|=(((uint16_t)igain)<<8);
381 	return opt;
382 }
383 
384 struct devaddstruct smpOSSAdd = {ossGetOpt, 0, 0, 0};
385 struct sounddevice smpOSS={SS_SAMPLER, 0, "OSS Recorder", ossDetect, ossInit, ossClose, &smpOSSAdd};
386 
387 char *dllinfo="driver smpOSS";
388 struct linkinfostruct dllextinfo = {.name = "devsoss", .desc = "OpenCP Sampler Device: OSS (c) 2004-09 Stian Skjelstad", .ver = DLLVERSION, .size = 0};
389