xref: /netbsd-src/sys/dev/pad/pad.c (revision 7f21db1c0118155e0dd40b75182e30c589d9f63e)
1 /* $NetBSD: pad.c,v 1.12 2010/01/18 23:57:14 dyoung Exp $ */
2 
3 /*-
4  * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
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 NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26  * POSSIBILITY OF SUCH DAMAGE.
27  */
28 
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pad.c,v 1.12 2010/01/18 23:57:14 dyoung Exp $");
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/conf.h>
35 #include <sys/buf.h>
36 #include <sys/kmem.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
39 #include <sys/proc.h>
40 #include <sys/condvar.h>
41 #include <sys/select.h>
42 #include <sys/audioio.h>
43 #include <sys/vnode.h>
44 
45 #include <dev/audio_if.h>
46 #include <dev/audiovar.h>
47 #include <dev/auconv.h>
48 
49 #include <dev/pad/padvar.h>
50 #include <dev/pad/padvol.h>
51 
52 #define PADUNIT(x)	minor(x)
53 
54 extern struct cfdriver pad_cd;
55 
56 static struct audio_device pad_device = {
57 	"Pseudo Audio",
58 	"1.0",
59 	"pad",
60 };
61 
62 typedef struct pad_block {
63 	uint8_t		*pb_ptr;
64 	int		pb_len;
65 } pad_block_t;
66 
67 enum {
68 	PAD_OUTPUT_CLASS,
69 	PAD_INPUT_CLASS,
70 	PAD_OUTPUT_MASTER_VOLUME,
71 	PAD_INPUT_DAC_VOLUME,
72 	PAD_ENUM_LAST,
73 };
74 
75 static int	pad_match(device_t, cfdata_t, void *);
76 static void	pad_attach(device_t, device_t, void *);
77 static int	pad_detach(device_t, int);
78 static void	pad_childdet(device_t, device_t);
79 
80 static int	pad_query_encoding(void *, struct audio_encoding *);
81 static int	pad_set_params(void *, int, int,
82 				audio_params_t *, audio_params_t *,
83 				stream_filter_list_t *, stream_filter_list_t *);
84 static int	pad_start_output(void *, void *, int,
85 				    void (*)(void *), void *);
86 static int	pad_start_input(void *, void *, int,
87 				   void (*)(void *), void *);
88 static int	pad_halt_output(void *);
89 static int	pad_halt_input(void *);
90 static int	pad_getdev(void *, struct audio_device *);
91 static int	pad_set_port(void *, mixer_ctrl_t *);
92 static int	pad_get_port(void *, mixer_ctrl_t *);
93 static int	pad_query_devinfo(void *, mixer_devinfo_t *);
94 static int	pad_get_props(void *);
95 static int	pad_round_blocksize(void *, int, int, const audio_params_t *);
96 
97 static const struct audio_hw_if pad_hw_if = {
98 	.query_encoding = pad_query_encoding,
99 	.set_params = pad_set_params,
100 	.start_output = pad_start_output,
101 	.start_input = pad_start_input,
102 	.halt_output = pad_halt_output,
103 	.halt_input = pad_halt_input,
104 	.getdev = pad_getdev,
105 	.set_port = pad_set_port,
106 	.get_port = pad_get_port,
107 	.query_devinfo = pad_query_devinfo,
108 	.get_props = pad_get_props,
109 	.round_blocksize = pad_round_blocksize,
110 };
111 
112 #define PAD_NFORMATS	1
113 static const struct audio_format pad_formats[PAD_NFORMATS] = {
114 	{ NULL, AUMODE_PLAY|AUMODE_RECORD, AUDIO_ENCODING_SLINEAR_LE, 16, 16,
115 	  2, AUFMT_STEREO, 1, { 44100 } },
116 };
117 
118 extern void	padattach(int);
119 
120 static int		pad_add_block(pad_softc_t *, uint8_t *, int);
121 static int		pad_get_block(pad_softc_t *, pad_block_t *, int);
122 
123 dev_type_open(pad_open);
124 dev_type_close(pad_close);
125 dev_type_read(pad_read);
126 
127 const struct cdevsw pad_cdevsw = {
128 	.d_open = pad_open,
129 	.d_close = pad_close,
130 	.d_read = pad_read,
131 	.d_write = nowrite,
132 	.d_ioctl = noioctl,
133 	.d_stop = nostop,
134 	.d_tty = notty,
135 	.d_poll = nopoll,
136 	.d_mmap = nommap,
137 	.d_kqfilter = nokqfilter,
138 	.d_flag = D_OTHER,
139 };
140 
141 CFATTACH_DECL2_NEW(pad, sizeof(pad_softc_t), pad_match, pad_attach, pad_detach,
142     NULL, NULL, pad_childdet);
143 
144 void
145 padattach(int n)
146 {
147 	int i, err;
148 	cfdata_t cf;
149 
150 	aprint_debug("pad: requested %d units\n", n);
151 
152 	err = config_cfattach_attach(pad_cd.cd_name, &pad_ca);
153 	if (err) {
154 		aprint_error("%s: couldn't register cfattach: %d\n",
155 		    pad_cd.cd_name, err);
156 		config_cfdriver_detach(&pad_cd);
157 		return;
158 	}
159 
160 	for (i = 0; i < n; i++) {
161 		cf = kmem_alloc(sizeof(struct cfdata), KM_NOSLEEP);
162 		if (cf == NULL) {
163 			aprint_error("%s: couldn't allocate cfdata\n",
164 			    pad_cd.cd_name);
165 			continue;
166 		}
167 		cf->cf_name = pad_cd.cd_name;
168 		cf->cf_atname = pad_cd.cd_name;
169 		cf->cf_unit = i;
170 		cf->cf_fstate = FSTATE_STAR;
171 
172 		(void)config_attach_pseudo(cf);
173 	}
174 
175 	return;
176 }
177 
178 static int
179 pad_add_block(pad_softc_t *sc, uint8_t *blk, int blksize)
180 {
181 	int l;
182 
183 	if (sc->sc_buflen + blksize > PAD_BUFSIZE)
184 		return ENOBUFS;
185 
186 	if (sc->sc_wpos + blksize <= PAD_BUFSIZE)
187 		memcpy(sc->sc_audiobuf + sc->sc_wpos, blk, blksize);
188 	else {
189 		l = PAD_BUFSIZE - sc->sc_wpos;
190 		memcpy(sc->sc_audiobuf + sc->sc_wpos, blk, l);
191 		memcpy(sc->sc_audiobuf, blk + l, blksize - l);
192 	}
193 
194 	sc->sc_wpos += blksize;
195 	if (sc->sc_wpos > PAD_BUFSIZE)
196 		sc->sc_wpos -= PAD_BUFSIZE;
197 
198 	sc->sc_buflen += blksize;
199 
200 	return 0;
201 }
202 
203 static int
204 pad_get_block(pad_softc_t *sc, pad_block_t *pb, int blksize)
205 {
206 	int l;
207 
208 	KASSERT(pb != NULL);
209 
210 	if (sc->sc_buflen < blksize)
211 		return ERESTART;
212 
213 	pb->pb_ptr = (sc->sc_audiobuf + sc->sc_rpos);
214 	if (sc->sc_rpos + blksize < PAD_BUFSIZE) {
215 		pb->pb_len = blksize;
216 		sc->sc_rpos += blksize;
217 	} else {
218 		l = PAD_BUFSIZE - sc->sc_rpos;
219 		pb->pb_len = l;
220 		sc->sc_rpos = 0;
221 	}
222 	sc->sc_buflen -= pb->pb_len;
223 
224 	return 0;
225 }
226 
227 static int
228 pad_match(device_t parent, cfdata_t data, void *opaque)
229 {
230 	return 1;
231 }
232 
233 static void
234 pad_childdet(device_t self, device_t child)
235 {
236 	pad_softc_t *sc = device_private(self);
237 
238 	sc->sc_audiodev = NULL;
239 }
240 
241 static void
242 pad_attach(device_t parent, device_t self, void *opaque)
243 {
244 	pad_softc_t *sc = device_private(self);
245 
246 	aprint_normal_dev(self, "outputs: 44100Hz, 16-bit, stereo\n");
247 
248 	sc->sc_dev = self;
249 	sc->sc_open = 0;
250 	if (auconv_create_encodings(pad_formats, PAD_NFORMATS,
251 	    &sc->sc_encodings) != 0) {
252 		aprint_error_dev(self, "couldn't create encodings\n");
253 		return;
254 	}
255 
256 	cv_init(&sc->sc_condvar, device_xname(self));
257 	mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_SCHED);
258 
259 	sc->sc_swvol = 255;
260 	sc->sc_buflen = 0;
261 	sc->sc_rpos = sc->sc_wpos = 0;
262 	sc->sc_audiodev = (void *)audio_attach_mi(&pad_hw_if, sc, sc->sc_dev);
263 
264 	if (!pmf_device_register(self, NULL, NULL))
265 		aprint_error_dev(self, "couldn't establish power handler\n");
266 
267 	return;
268 }
269 
270 static int
271 pad_detach(device_t self, int flags)
272 {
273 	pad_softc_t *sc = device_private(self);
274 	int cmaj, mn, rc;
275 
276 	cmaj = cdevsw_lookup_major(&pad_cdevsw);
277 	mn = device_unit(self);
278 	vdevgone(cmaj, mn, mn, VCHR);
279 
280 	if ((rc = config_detach_children(self, flags)) != 0)
281 		return rc;
282 
283 	pmf_device_deregister(self);
284 
285 	mutex_destroy(&sc->sc_mutex);
286 	cv_destroy(&sc->sc_condvar);
287 
288 	auconv_delete_encodings(sc->sc_encodings);
289 
290 	return 0;
291 }
292 
293 int
294 pad_open(dev_t dev, int flags, int fmt, struct lwp *l)
295 {
296 	pad_softc_t *sc;
297 
298 	sc = device_lookup_private(&pad_cd, PADUNIT(dev));
299 	if (sc == NULL)
300 		return ENXIO;
301 
302 	if (sc->sc_open++) {
303 		sc->sc_open--;
304 		return EBUSY;
305 	}
306 
307 	return 0;
308 }
309 
310 int
311 pad_close(dev_t dev, int flags, int fmt, struct lwp *l)
312 {
313 	pad_softc_t *sc;
314 
315 	sc = device_lookup_private(&pad_cd, PADUNIT(dev));
316 	if (sc == NULL)
317 		return ENXIO;
318 
319 	KASSERT(sc->sc_open > 0);
320 	sc->sc_open--;
321 
322 	return 0;
323 }
324 
325 int
326 pad_read(dev_t dev, struct uio *uio, int flags)
327 {
328 	pad_softc_t *sc;
329 	pad_block_t pb;
330 	void (*intr)(void *);
331 	void *intrarg;
332 	int err;
333 
334 	sc = device_lookup_private(&pad_cd, PADUNIT(dev));
335 	if (sc == NULL)
336 		return ENXIO;
337 
338 	err = 0;
339 
340 	intr = sc->sc_intr;
341 	intrarg = sc->sc_intrarg;
342 
343 	while (uio->uio_resid > 0 && !err) {
344 		err = pad_get_block(sc, &pb, min(uio->uio_resid, PAD_BLKSIZE));
345 		if (!err)
346 			err = uiomove(pb.pb_ptr, pb.pb_len, uio);
347 		else {
348 			if (intr) {
349 				(*intr)(intrarg);
350 				intr = sc->sc_intr;
351 				intrarg = sc->sc_intrarg;
352 				err = 0;
353 				continue;
354 			}
355 
356 			mutex_enter(&sc->sc_mutex);
357 			err = cv_timedwait_sig(&sc->sc_condvar, &sc->sc_mutex,
358 			    hz/100);
359 			if (err != 0 && err != EWOULDBLOCK) {
360 				mutex_exit(&sc->sc_mutex);
361 				aprint_error_dev(sc->sc_dev,
362 				    "cv_timedwait_sig returned %d\n", err);
363 				return EINTR;
364 			}
365 			intr = sc->sc_intr;
366 			intrarg = sc->sc_intrarg;
367 			mutex_exit(&sc->sc_mutex);
368 			err = 0;
369 		}
370 	}
371 
372 	if (intr)
373 		(*intr)(intrarg);
374 
375 	return err;
376 }
377 
378 static int
379 pad_query_encoding(void *opaque, struct audio_encoding *ae)
380 {
381 	pad_softc_t *sc;
382 
383 	sc = (pad_softc_t *)opaque;
384 
385 	return auconv_query_encoding(sc->sc_encodings, ae);
386 }
387 
388 static int
389 pad_set_params(void *opaque, int setmode, int usemode,
390     audio_params_t *play, audio_params_t *rec,
391     stream_filter_list_t *pfil, stream_filter_list_t *rfil)
392 {
393 	pad_softc_t *sc;
394 
395 	sc = (pad_softc_t *)opaque;
396 
397 	if (auconv_set_converter(pad_formats, PAD_NFORMATS, AUMODE_PLAY,
398 	    play, true, pfil) < 0)
399 		return EINVAL;
400 	if (auconv_set_converter(pad_formats, PAD_NFORMATS, AUMODE_RECORD,
401 	    rec, true, rfil) < 0)
402 		return EINVAL;
403 
404 	if (pfil->req_size > 0)
405 		play = &pfil->filters[0].param;
406 	switch (play->encoding) {
407 	case AUDIO_ENCODING_SLINEAR_LE:
408 		if (play->precision == 16 && play->validbits == 16)
409 			pfil->prepend(pfil, pad_vol_slinear16_le, play);
410 		break;
411 	case AUDIO_ENCODING_SLINEAR_BE:
412 		if (play->precision == 16 && play->validbits == 16)
413 			pfil->prepend(pfil, pad_vol_slinear16_be, play);
414 		break;
415 	default:
416 		break;
417 	}
418 
419 	return 0;
420 }
421 
422 static int
423 pad_start_output(void *opaque, void *block, int blksize,
424     void (*intr)(void *), void *intrarg)
425 {
426 	pad_softc_t *sc;
427 	int err;
428 
429 	sc = (pad_softc_t *)opaque;
430 
431 	mutex_enter(&sc->sc_mutex);
432 
433 	sc->sc_intr = intr;
434 	sc->sc_intrarg = intrarg;
435 	sc->sc_blksize = blksize;
436 
437 	err = pad_add_block(sc, block, blksize);
438 
439 	cv_signal(&sc->sc_condvar);
440 
441 	mutex_exit(&sc->sc_mutex);
442 
443 	return err;
444 }
445 
446 static int
447 pad_start_input(void *opaque, void *block, int blksize,
448     void (*intr)(void *), void *intrarg)
449 {
450 	return EINVAL;
451 }
452 
453 static int
454 pad_halt_output(void *opaque)
455 {
456 	pad_softc_t *sc;
457 
458 	sc = (pad_softc_t *)opaque;
459 	sc->sc_intr = NULL;
460 	sc->sc_intrarg = NULL;
461 	sc->sc_buflen = 0;
462 	sc->sc_rpos = sc->sc_wpos = 0;
463 
464 	return 0;
465 }
466 
467 static int
468 pad_halt_input(void *opaque)
469 {
470 	return 0;
471 }
472 
473 static int
474 pad_getdev(void *opaque, struct audio_device *ret)
475 {
476 
477 	*ret = pad_device;
478 
479 	return 0;
480 }
481 
482 static int
483 pad_set_port(void *opaque, mixer_ctrl_t *mc)
484 {
485 	pad_softc_t *sc;
486 
487 	sc = (pad_softc_t *)opaque;
488 
489 	switch (mc->dev) {
490 	case PAD_OUTPUT_MASTER_VOLUME:
491 	case PAD_INPUT_DAC_VOLUME:
492 		sc->sc_swvol = mc->un.value.level[AUDIO_MIXER_LEVEL_MONO];
493 		return 0;
494 	}
495 
496 	return ENXIO;
497 }
498 
499 static int
500 pad_get_port(void *opaque, mixer_ctrl_t *mc)
501 {
502 	pad_softc_t *sc;
503 
504 	sc = (pad_softc_t *)opaque;
505 
506 	switch (mc->dev) {
507 	case PAD_OUTPUT_MASTER_VOLUME:
508 	case PAD_INPUT_DAC_VOLUME:
509 		mc->un.value.level[AUDIO_MIXER_LEVEL_MONO] = sc->sc_swvol;
510 		return 0;
511 	}
512 
513 	return ENXIO;
514 }
515 
516 static int
517 pad_query_devinfo(void *opaque, mixer_devinfo_t *di)
518 {
519 	pad_softc_t *sc;
520 
521 	sc = (pad_softc_t *)opaque;
522 
523 	switch (di->index) {
524 	case PAD_OUTPUT_CLASS:
525 		di->mixer_class = PAD_OUTPUT_CLASS;
526 		strcpy(di->label.name, AudioCoutputs);
527 		di->type = AUDIO_MIXER_CLASS;
528 		di->next = di->prev = AUDIO_MIXER_LAST;
529 		return 0;
530 	case PAD_INPUT_CLASS:
531 		di->mixer_class = PAD_INPUT_CLASS;
532 		strcpy(di->label.name, AudioCinputs);
533 		di->type = AUDIO_MIXER_CLASS;
534 		di->next = di->prev = AUDIO_MIXER_LAST;
535 		return 0;
536 	case PAD_OUTPUT_MASTER_VOLUME:
537 		di->mixer_class = PAD_OUTPUT_CLASS;
538 		strcpy(di->label.name, AudioNmaster);
539 		di->type = AUDIO_MIXER_VALUE;
540 		di->next = di->prev = AUDIO_MIXER_LAST;
541 		di->un.v.num_channels = 1;
542 		strcpy(di->un.v.units.name, AudioNvolume);
543 		return 0;
544 	case PAD_INPUT_DAC_VOLUME:
545 		di->mixer_class = PAD_INPUT_CLASS;
546 		strcpy(di->label.name, AudioNdac);
547 		di->type = AUDIO_MIXER_VALUE;
548 		di->next = di->prev = AUDIO_MIXER_LAST;
549 		di->un.v.num_channels = 1;
550 		strcpy(di->un.v.units.name, AudioNvolume);
551 		return 0;
552 	}
553 
554 	return ENXIO;
555 }
556 
557 static int
558 pad_get_props(void *opaque)
559 {
560 	return 0;
561 }
562 
563 static int
564 pad_round_blocksize(void *opaque, int blksize, int mode,
565     const audio_params_t *p)
566 {
567 	return PAD_BLKSIZE;
568 }
569