2
* ddksample_audio.c - OSS DDK sample driver - Audio functionality
6
* This simple audio driver implements an OSS audio device similar to
7
* /dev/zero. The output will be just discarded. Recording will return
8
* silence. In addition the sample parameters (rate, channels and format)
9
* is emulated. Timeout callbacks are used to keep the device running
10
* at the right rate (approximately).
12
* To demonstrate mixer functionality (together with ddksample_mixer.c) this
13
* driver also computes the peak levels from the output data. Also output
14
* volume controls are implemented.
18
* This file is part of Open Sound System.
20
* Copyright (C) 4Front Technologies 1996-2008.
22
* This this source file is released under GPL v2 license (no other versions).
23
* See the COPYING file included in the main directory of this source
24
* distribution for the license terms and conditions.
30
* Solaris DDI includes
32
#include <sys/types.h>
33
#include <sys/modctl.h>
37
#include <sys/sunddi.h>
40
* OSS specific includes
42
#include <sys/soundcard.h>
43
#include <sys/ossddk/ossddk.h>
45
/***************************************************
46
* Private types and variables
49
#include "ddksample.h"
51
/**************************************************/
54
* This driver emulates periodic interrupts on fragment boundaries using
55
* the timeout(9f) mechanism. Ordinary drivers use device interrupts
60
ddksample_intr (void *arg)
62
ddksample_portc *portc = arg;
64
if (portc->audio_active & PCM_ENABLE_OUTPUT)
68
* We will first do some computations on the output data. This is not
69
* necessary in normal drivers. It's here just to make the mixer section
70
* to behave like the mixer of some real device.
76
dmap = ossddk_adev_get_dmapout (portc->dev);
77
buf = ossddk_dmap_get_dmabuf (dmap);
80
* Compute the current playback position (beginning of the
81
* fragment we have just finished).
83
pos = ossddk_dmap_get_qhead (dmap) * ossddk_dmap_get_fragsize (dmap);
86
ddksample_do_math (portc, buf, ossddk_dmap_get_fragsize (dmap));
92
ossddk_audio_outputintr (portc->dev, 0); /* Acknowledge one fragment */
95
if (portc->audio_active & PCM_ENABLE_INPUT)
97
ossddk_audio_inputintr (portc->dev); /* Acknowledge one fragment */
100
portc->timeout_id = timeout (ddksample_intr, portc, portc->timeout_value);
104
ddksample_set_rate (int dev, int arg)
106
ddksample_portc *portc = ossddk_adev_get_portc (dev);
108
if (arg == 0) /* Query - return the current rate */
112
* If the requested rate is not supported the driver may just return the
113
* current rate. Or preferably it may select the rate that is closest to the
114
* requested one. In this sample driver we use the easy way.
117
if (arg != 16000 && arg != 24000 && arg != 48000)
121
* The rate was OK so write it down and report it back.
124
return portc->speed = arg;
128
ddksample_set_channels (int dev, short arg)
130
ddksample_portc *portc = ossddk_adev_get_portc (dev);
132
if (arg == 0) /* Query - return the current setting */
133
return portc->channels;
136
* Assume 2 channels (stereo) if something incorrect is
140
if (arg != 1 && arg != 2)
143
return portc->channels = arg;
147
ddksample_set_format (int dev, unsigned int arg)
149
ddksample_portc *portc = ossddk_adev_get_portc (dev);
151
if (arg == 0) /* Query */
152
return portc->format;
155
* Fall back to 16 bits (native endianess) if some unsupported
159
if (arg != AFMT_S16_NE && arg != AFMT_S32_NE)
162
if (arg == AFMT_S32_NE)
167
return portc->format = arg;
171
ddksample_ioctl (int dev, unsigned int cmd, int *arg)
177
static void ddksample_trigger (int dev, int state);
180
ddksample_reset (int dev)
182
ddksample_trigger (dev, 0);
186
ddksample_open (int dev, int mode, int open_flags)
188
ddksample_portc *portc = ossddk_adev_get_portc (dev);
189
ddksample_devc *devc = ossddk_adev_get_devc (dev);
191
mutex_enter (&devc->mutex);
193
if (portc->open_mode != 0) /* Device busy */
195
mutex_exit (&devc->mutex);
199
portc->open_mode = mode;
200
devc->audio_open_count++;
201
mutex_exit (&devc->mutex);
207
ddksample_close (int dev, int mode)
209
ddksample_portc *portc = ossddk_adev_get_portc (dev);
210
ddksample_devc *devc = ossddk_adev_get_devc (dev);
212
ddksample_reset (dev);
214
mutex_enter (&devc->mutex);
215
portc->open_mode = 0;
216
devc->audio_open_count--;
217
mutex_exit (&devc->mutex);
221
ddksample_trigger (int dev, int state)
223
ddksample_portc *portc = ossddk_adev_get_portc (dev);
225
if (portc->open_mode & OPEN_WRITE)
227
if ((state & PCM_ENABLE_OUTPUT)
228
&& (portc->prepare_flags & PCM_ENABLE_OUTPUT))
230
portc->prepare_flags &= ~PCM_ENABLE_OUTPUT;
233
* Arm the timeout if it was not started yet.
235
if (portc->audio_active == 0)
237
timeout (ddksample_intr, portc, portc->timeout_value);
238
portc->audio_active |= PCM_ENABLE_OUTPUT;
241
if (!(state & PCM_ENABLE_OUTPUT)
242
&& (portc->audio_active & PCM_ENABLE_OUTPUT))
244
portc->audio_active &= ~PCM_ENABLE_OUTPUT;
245
if (portc->audio_active == 0)
246
untimeout (portc->timeout_id);
247
portc->timeout_id = 0;
251
if (portc->open_mode & OPEN_READ)
253
if ((state & PCM_ENABLE_INPUT)
254
&& (portc->prepare_flags & PCM_ENABLE_INPUT))
256
portc->prepare_flags &= ~PCM_ENABLE_INPUT;
259
* Arm the timeout if it was not started yet.
261
if (portc->audio_active == 0)
263
timeout (ddksample_intr, portc, portc->timeout_value);
264
portc->audio_active |= PCM_ENABLE_INPUT;
267
if (!(state & PCM_ENABLE_INPUT)
268
&& (portc->audio_active & PCM_ENABLE_INPUT))
270
portc->audio_active &= ~PCM_ENABLE_INPUT;
271
if (portc->audio_active == 0)
272
untimeout (portc->timeout_id);
273
portc->timeout_id = 0;
279
ddksample_prepare_for_input (int dev, int fragsize, int numfrags)
281
ddksample_portc *portc = ossddk_adev_get_portc (dev);
282
int datarate, intrate, tmout;
284
cmn_err (CE_CONT, "Input, rate=%d, channels=%d, format=0x%x, bits=%d\n",
285
portc->speed, portc->channels, portc->format, portc->bits);
288
* Compute the number of lbolt ticks between interrupts. This value is needed
292
datarate = portc->speed * portc->channels * portc->bits / 8; /* Bytes per second */
293
intrate = datarate * 10 / fragsize;
294
tmout = hz * 1000 / intrate;
296
cmn_err (CE_CONT, "Datarate %d, fragsize %d\n", datarate, fragsize);
297
cmn_err (CE_CONT, "Intrate %d.%d interrupts / sec.\n", intrate / 10,
299
cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
301
tmout = (tmout + 50) / 100; /* Round to the nearest integer value */
305
portc->timeout_value = tmout;
306
portc->prepare_flags |= PCM_ENABLE_INPUT;
311
ddksample_prepare_for_output (int dev, int fragsize, int numfrags)
313
ddksample_portc *portc = ossddk_adev_get_portc (dev);
314
int datarate, intrate, tmout;
316
cmn_err (CE_CONT, "Output, rate=%d, channels=%d, format=0x%x, bits=%d\n",
317
portc->speed, portc->channels, portc->format, portc->bits);
320
* Compute the number of lbolt ticks between interrupts. This value is needed
324
datarate = portc->speed * portc->channels * portc->bits / 8; /* Bytes per second */
325
intrate = datarate * 10 / fragsize;
326
tmout = hz * 1000 / intrate;
328
cmn_err (CE_CONT, "Datarate %d, fragsize %d\n", datarate, fragsize);
329
cmn_err (CE_CONT, "Intrate %d.%d interrupts / sec.\n", intrate / 10,
331
cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
333
tmout = (tmout + 50) / 100; /* Round to the nearest integer value */
337
portc->timeout_value = tmout;
338
portc->prepare_flags |= PCM_ENABLE_OUTPUT;
343
ddksample_alloc_buffer (int dev, dmap_t * dmap, int direction)
345
#define MY_BUFFSIZE (64*1024)
346
if (ossddk_dmap_get_dmabuf (dmap) != NULL)
348
ossddk_dmap_set_phys (dmap, 0); /* Not mmap() capable */
349
ossddk_dmap_set_dmabuf (dmap, kmem_zalloc (MY_BUFFSIZE, KM_SLEEP));
350
if (ossddk_dmap_get_dmabuf (dmap) == NULL) /* Alloc failed */
352
ossddk_dmap_set_buffsize (dmap, MY_BUFFSIZE);
358
ddksample_free_buffer (int dev, dmap_t * dmap, int direction)
360
if (ossddk_dmap_get_dmabuf (dmap) == NULL)
362
kmem_free (ossddk_dmap_get_dmabuf (dmap), MY_BUFFSIZE);
364
ossddk_dmap_set_dmabuf (dmap, NULL);
370
ddksample_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
376
static audiodrv_t ddksample_audio_driver = {
379
NULL, // output_block
382
ddksample_prepare_for_input,
383
ddksample_prepare_for_output,
391
ddksample_set_format,
392
ddksample_set_channels,
397
ddksample_alloc_buffer,
398
ddksample_free_buffer,
401
NULL /* ddksample_get_buffer_pointer */
405
ddksample_audio_init (ddksample_devc * devc)
409
static int rates[] = { 16000, 24000, 48000 };
411
for (i = 0; i < MAX_SUBDEVICE; i++)
413
ddksample_portc *portc;
416
flags = ADEV_VIRTUAL | ADEV_DUPLEX;
419
flags |= ADEV_SHADOW;
421
if ((dev = ossddk_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
424
"OSS DDK sample device",
425
&ddksample_audio_driver,
428
AFMT_S16_NE | AFMT_S32_NE,
432
portc = &devc->portc[devc->num_portc++];
435
portc->speed = 48000;
437
portc->format = AFMT_S16_NE;
439
portc->open_mode = 0;
440
portc->timeout_id = 0;
441
portc->left_volume = DDKSAMPLE_MAX_VOL;
442
portc->right_volume = DDKSAMPLE_MAX_VOL;
443
portc->left_peak = 0;
444
portc->right_peak = 0;
447
ossddk_adev_set_portc (dev, portc);
448
ossddk_adev_set_portnum (dev, i);
449
ossddk_adev_set_mixer (dev, devc->mixer_dev);
451
ossddk_adev_set_rates (dev, 16000, 48000, 3, rates);
452
ossddk_adev_set_channels (dev, 1, 2); /* Mono and stereo are supported */
455
* Usually the list of supported audio formats is set when calling
456
* ossddk_install_audiodev(). However in some cases it may be
457
* necessary to change the format list later. Changing it is
458
* also necessary if recording formats are fdifferent than the
461
* The next lines demonstrates how to do that. Normal drivers should
464
ossddk_adev_set_formats (dev, AFMT_S32_NE | AFMT_S16_NE, // 16 and 32 bit playback
465
AFMT_S16_NE); // Only 16 bit recording
469
* Report stereo as the preferred channel configuration.
471
ossddk_adev_set_caps (dev, DSP_CH_STEREO);
475
* Many devices have some limits for the fragment size. For
476
* example it's common that the fragment must fit in a single
477
* memory page. In such cases setting the fragment size limits is
478
* necessary. Otherwise the driver should not care about them.
480
* Both the upper and lower limits are set using the same call.
481
* Value of 0 means no limit.
483
* Here we set the upper limit to PAGE_SIZE and no lower limit.
485
ossddk_adev_set_buflimits (dev, 0, PAGE_SIZE);
490
* In some cases the device may be rate clocked with some other
491
* device that provides the sample clock. For example there may
492
* be some special cable connected between the devices. When the
493
* driver knows this it can report the device which controls
494
* the sampling rate. Otherwise this information must be left
497
* Knowing that two or more devices have their rates locked will
498
* make work of some future OSS subsystems more reliable. However
499
* false information will probably break things in the hard way.
501
ossddk_adev_set_ratesource (dev, devc->some_known_device_number);
506
* If the device requires some kind of proprietary control program
507
* it's necessary to assign some magic number for the device.
508
* The control program can find the right device by looking at
509
* the magic field returned by SNDCTL_AUDIOINFO.
511
* Normal devices must not report any kind of magic numbers. In
512
* particular this feature must not be used to help "client"
513
* programs to detect what kind of device is being used.
515
* CAUTION! To avoid conflicts between two drivers using the same
516
* magic it's necessary to request the magic number from
517
* 4Front Technologies (support@opensound.com).
519
ossddk_adev_set_magic (dev, magic_number_assigned_for_this_driver);