1 /* $NetBSD: pad.c,v 1.10 2008/09/04 10:42:55 christos Exp $ */
4 * Copyright (c) 2007 Jared D. McNeill <jmcneill@invisible.ca>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
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.
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.
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: pad.c,v 1.10 2008/09/04 10:42:55 christos Exp $");
32 #include <sys/types.h>
33 #include <sys/param.h>
37 #include <sys/kernel.h>
38 #include <sys/device.h>
40 #include <sys/condvar.h>
41 #include <sys/select.h>
42 #include <sys/audioio.h>
43 #include <sys/vnode.h>
45 #include <dev/audio_if.h>
46 #include <dev/audiovar.h>
47 #include <dev/auconv.h>
49 #include <dev/pad/padvar.h>
50 #include <dev/pad/padvol.h>
52 #define PADUNIT(x) minor(x)
54 extern struct cfdriver pad_cd
;
56 static struct audio_device pad_device
= {
62 typedef struct pad_block
{
70 PAD_OUTPUT_MASTER_VOLUME
,
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
);
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
*);
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
,
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 } },
118 extern void padattach(int);
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);
123 dev_type_open(pad_open
);
124 dev_type_close(pad_close
);
125 dev_type_read(pad_read
);
127 const struct cdevsw pad_cdevsw
= {
129 .d_close
= pad_close
,
137 .d_kqfilter
= nokqfilter
,
141 CFATTACH_DECL2_NEW(pad
, sizeof(pad_softc_t
), pad_match
, pad_attach
, pad_detach
,
142 NULL
, NULL
, pad_childdet
);
150 aprint_debug("pad: requested %d units\n", n
);
152 err
= config_cfattach_attach(pad_cd
.cd_name
, &pad_ca
);
154 aprint_error("%s: couldn't register cfattach: %d\n",
155 pad_cd
.cd_name
, err
);
156 config_cfdriver_detach(&pad_cd
);
160 for (i
= 0; i
< n
; i
++) {
161 cf
= kmem_alloc(sizeof(struct cfdata
), KM_NOSLEEP
);
163 aprint_error("%s: couldn't allocate cfdata\n",
167 cf
->cf_name
= pad_cd
.cd_name
;
168 cf
->cf_atname
= pad_cd
.cd_name
;
170 cf
->cf_fstate
= FSTATE_STAR
;
172 (void)config_attach_pseudo(cf
);
179 pad_add_block(pad_softc_t
*sc
, uint8_t *blk
, int blksize
)
183 if (sc
->sc_buflen
+ blksize
> PAD_BUFSIZE
)
186 if (sc
->sc_wpos
+ blksize
<= PAD_BUFSIZE
)
187 memcpy(sc
->sc_audiobuf
+ sc
->sc_wpos
, blk
, blksize
);
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
);
194 sc
->sc_wpos
+= blksize
;
195 if (sc
->sc_wpos
> PAD_BUFSIZE
)
196 sc
->sc_wpos
-= PAD_BUFSIZE
;
198 sc
->sc_buflen
+= blksize
;
204 pad_get_block(pad_softc_t
*sc
, pad_block_t
*pb
, int blksize
)
210 if (sc
->sc_buflen
< blksize
)
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
;
218 l
= PAD_BUFSIZE
- sc
->sc_rpos
;
222 sc
->sc_buflen
-= pb
->pb_len
;
228 pad_match(device_t parent
, cfdata_t data
, void *opaque
)
234 pad_childdet(device_t self
, device_t child
)
236 pad_softc_t
*sc
= device_private(self
);
238 sc
->sc_audiodev
= NULL
;
242 pad_attach(device_t parent
, device_t self
, void *opaque
)
244 pad_softc_t
*sc
= device_private(self
);
246 aprint_normal_dev(self
, "outputs: 44100Hz, 16-bit, stereo\n");
250 if (auconv_create_encodings(pad_formats
, PAD_NFORMATS
,
251 &sc
->sc_encodings
) != 0) {
252 aprint_error_dev(self
, "couldn't create encodings\n");
256 cv_init(&sc
->sc_condvar
, device_xname(self
));
257 mutex_init(&sc
->sc_mutex
, MUTEX_DEFAULT
, IPL_SCHED
);
261 sc
->sc_rpos
= sc
->sc_wpos
= 0;
262 sc
->sc_audiodev
= (void *)audio_attach_mi(&pad_hw_if
, sc
, sc
->sc_dev
);
264 if (!pmf_device_register(self
, NULL
, NULL
))
265 aprint_error_dev(self
, "couldn't establish power handler\n");
271 pad_detach(device_t self
, int flags
)
273 pad_softc_t
*sc
= device_private(self
);
276 cmaj
= cdevsw_lookup_major(&pad_cdevsw
);
277 mn
= device_unit(self
);
278 vdevgone(cmaj
, mn
, mn
, VCHR
);
280 if ((rc
= config_detach_children(self
, flags
)) != 0)
283 pmf_device_deregister(self
);
285 mutex_destroy(&sc
->sc_mutex
);
286 cv_destroy(&sc
->sc_condvar
);
288 auconv_delete_encodings(sc
->sc_encodings
);
294 pad_open(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
298 sc
= device_lookup_private(&pad_cd
, PADUNIT(dev
));
311 pad_close(dev_t dev
, int flags
, int fmt
, struct lwp
*l
)
315 sc
= device_lookup_private(&pad_cd
, PADUNIT(dev
));
319 KASSERT(sc
->sc_open
> 0);
326 pad_read(dev_t dev
, struct uio
*uio
, int flags
)
330 void (*intr
)(void *);
334 sc
= device_lookup_private(&pad_cd
, PADUNIT(dev
));
341 intrarg
= sc
->sc_intrarg
;
343 while (uio
->uio_resid
> 0 && !err
) {
344 err
= pad_get_block(sc
, &pb
, min(uio
->uio_resid
, PAD_BLKSIZE
));
346 err
= uiomove(pb
.pb_ptr
, pb
.pb_len
, uio
);
351 intrarg
= sc
->sc_intrarg
;
356 mutex_enter(&sc
->sc_mutex
);
357 err
= cv_timedwait_sig(&sc
->sc_condvar
, &sc
->sc_mutex
,
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
);
366 intrarg
= sc
->sc_intrarg
;
367 mutex_exit(&sc
->sc_mutex
);
379 pad_query_encoding(void *opaque
, struct audio_encoding
*ae
)
383 sc
= (pad_softc_t
*)opaque
;
385 return auconv_query_encoding(sc
->sc_encodings
, ae
);
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
)
395 sc
= (pad_softc_t
*)opaque
;
397 if (auconv_set_converter(pad_formats
, PAD_NFORMATS
, AUMODE_PLAY
,
398 play
, true, pfil
) < 0)
400 if (auconv_set_converter(pad_formats
, PAD_NFORMATS
, AUMODE_RECORD
,
401 rec
, true, rfil
) < 0)
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
);
411 case AUDIO_ENCODING_SLINEAR_BE
:
412 if (play
->precision
== 16 && play
->validbits
== 16)
413 pfil
->prepend(pfil
, pad_vol_slinear16_be
, play
);
423 pad_start_output(void *opaque
, void *block
, int blksize
,
424 void (*intr
)(void *), void *intrarg
)
429 sc
= (pad_softc_t
*)opaque
;
431 mutex_enter(&sc
->sc_mutex
);
434 sc
->sc_intrarg
= intrarg
;
435 sc
->sc_blksize
= blksize
;
437 err
= pad_add_block(sc
, block
, blksize
);
439 cv_signal(&sc
->sc_condvar
);
441 mutex_exit(&sc
->sc_mutex
);
447 pad_start_input(void *opaque
, void *block
, int blksize
,
448 void (*intr
)(void *), void *intrarg
)
454 pad_halt_output(void *opaque
)
458 sc
= (pad_softc_t
*)opaque
;
460 sc
->sc_intrarg
= NULL
;
462 sc
->sc_rpos
= sc
->sc_wpos
= 0;
468 pad_halt_input(void *opaque
)
474 pad_getdev(void *opaque
, struct audio_device
*ret
)
483 pad_set_port(void *opaque
, mixer_ctrl_t
*mc
)
487 sc
= (pad_softc_t
*)opaque
;
490 case PAD_OUTPUT_MASTER_VOLUME
:
491 case PAD_INPUT_DAC_VOLUME
:
492 sc
->sc_swvol
= mc
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
];
500 pad_get_port(void *opaque
, mixer_ctrl_t
*mc
)
504 sc
= (pad_softc_t
*)opaque
;
507 case PAD_OUTPUT_MASTER_VOLUME
:
508 case PAD_INPUT_DAC_VOLUME
:
509 mc
->un
.value
.level
[AUDIO_MIXER_LEVEL_MONO
] = sc
->sc_swvol
;
517 pad_query_devinfo(void *opaque
, mixer_devinfo_t
*di
)
521 sc
= (pad_softc_t
*)opaque
;
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
;
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
;
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
);
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
);
558 pad_get_props(void *opaque
)
564 pad_round_blocksize(void *opaque
, int blksize
, int mode
,
565 const audio_params_t
*p
)