~ubuntu-branches/ubuntu/oneiric/oss4/oneiric-proposed

« back to all changes in this revision

Viewing changes to misc/samples/ddksample/ddksample_audio.c

  • Committer: Bazaar Package Importer
  • Author(s): Stefano Rivera
  • Date: 2011-06-16 20:37:48 UTC
  • mfrom: (5.1.3 sid)
  • Revision ID: james.westby@ubuntu.com-20110616203748-jbrxik6ql33z54co
Tags: 4.2-build2004-1ubuntu1
* Merge from Debian unstable.
  - Supports our current kernel (LP: #746048)
  Remaining changes:
  - debian/oss4-dkms.dkms.in: s/source/build/ in Kernel headers paths.
* ld-as-needed.patch: Re-order CC arguments to enable building with ld
  --as-needed (LP: #770972)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * ddksample_audio.c - OSS DDK sample driver - Audio functionality
 
3
 *
 
4
 * Description:
 
5
 *
 
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).
 
11
 *
 
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.
 
15
 */
 
16
/*
 
17
 *
 
18
 * This file is part of Open Sound System.
 
19
 *
 
20
 * Copyright (C) 4Front Technologies 1996-2008.
 
21
 *
 
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.
 
25
 *
 
26
 */
 
27
 
 
28
 
 
29
/*
 
30
 * Solaris DDI includes
 
31
 */
 
32
#include <sys/types.h>
 
33
#include <sys/modctl.h>
 
34
#include <sys/kmem.h>
 
35
#include <sys/conf.h>
 
36
#include <sys/ddi.h>
 
37
#include <sys/sunddi.h>
 
38
 
 
39
/*
 
40
 * OSS specific includes
 
41
 */
 
42
#include <sys/soundcard.h>
 
43
#include <sys/ossddk/ossddk.h>
 
44
 
 
45
/***************************************************
 
46
 * Private types and variables
 
47
 */
 
48
 
 
49
#include "ddksample.h"
 
50
 
 
51
/**************************************************/
 
52
 
 
53
/*
 
54
 * This driver emulates periodic interrupts on fragment boundaries using
 
55
 * the timeout(9f) mechanism. Ordinary drivers use device interrupts
 
56
 * for this.
 
57
 */
 
58
 
 
59
static void
 
60
ddksample_intr (void *arg)
 
61
{
 
62
  ddksample_portc *portc = arg;
 
63
 
 
64
  if (portc->audio_active & PCM_ENABLE_OUTPUT)
 
65
    {
 
66
#if 1
 
67
/*
 
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.
 
71
 */
 
72
      int pos;
 
73
      unsigned char *buf;
 
74
      dmap_t *dmap;
 
75
 
 
76
      dmap = ossddk_adev_get_dmapout (portc->dev);
 
77
      buf = ossddk_dmap_get_dmabuf (dmap);
 
78
 
 
79
      /*
 
80
       * Compute the current playback position (beginning of the
 
81
       * fragment we have just finished).
 
82
       */
 
83
      pos = ossddk_dmap_get_qhead (dmap) * ossddk_dmap_get_fragsize (dmap);
 
84
 
 
85
      buf = buf + pos;
 
86
      ddksample_do_math (portc, buf, ossddk_dmap_get_fragsize (dmap));
 
87
#endif
 
88
 
 
89
 
 
90
 
 
91
 
 
92
      ossddk_audio_outputintr (portc->dev, 0);  /* Acknowledge one fragment */
 
93
    }
 
94
 
 
95
  if (portc->audio_active & PCM_ENABLE_INPUT)
 
96
    {
 
97
      ossddk_audio_inputintr (portc->dev);      /* Acknowledge one fragment */
 
98
    }
 
99
 
 
100
  portc->timeout_id = timeout (ddksample_intr, portc, portc->timeout_value);
 
101
}
 
102
 
 
103
static int
 
104
ddksample_set_rate (int dev, int arg)
 
105
{
 
106
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
107
 
 
108
  if (arg == 0)                 /* Query - return the current rate */
 
109
    return portc->speed;
 
110
 
 
111
/*
 
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.
 
115
 */
 
116
 
 
117
  if (arg != 16000 && arg != 24000 && arg != 48000)
 
118
    return portc->speed;
 
119
 
 
120
/*
 
121
 * The rate was OK so write it down and report it back.
 
122
 */
 
123
 
 
124
  return portc->speed = arg;
 
125
}
 
126
 
 
127
static short
 
128
ddksample_set_channels (int dev, short arg)
 
129
{
 
130
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
131
 
 
132
  if (arg == 0)                 /* Query - return the current setting */
 
133
    return portc->channels;
 
134
 
 
135
/*
 
136
 * Assume 2 channels (stereo) if something incorrect is
 
137
 * requested.
 
138
 */
 
139
 
 
140
  if (arg != 1 && arg != 2)
 
141
    return 2;
 
142
 
 
143
  return portc->channels = arg;
 
144
}
 
145
 
 
146
static unsigned int
 
147
ddksample_set_format (int dev, unsigned int arg)
 
148
{
 
149
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
150
 
 
151
  if (arg == 0)                 /* Query */
 
152
    return portc->format;
 
153
 
 
154
/*
 
155
 * Fall back to 16 bits (native endianess) if some unsupported
 
156
 * rate is requested.
 
157
 */
 
158
 
 
159
  if (arg != AFMT_S16_NE && arg != AFMT_S32_NE)
 
160
    return AFMT_S16_NE;
 
161
 
 
162
  if (arg == AFMT_S32_NE)
 
163
    portc->bits = 32;
 
164
  else
 
165
    portc->bits = 16;
 
166
 
 
167
  return portc->format = arg;
 
168
}
 
169
 
 
170
static int
 
171
ddksample_ioctl (int dev, unsigned int cmd, int *arg)
 
172
{
 
173
// TODO 
 
174
  return -EINVAL;
 
175
}
 
176
 
 
177
static void ddksample_trigger (int dev, int state);
 
178
 
 
179
static void
 
180
ddksample_reset (int dev)
 
181
{
 
182
  ddksample_trigger (dev, 0);
 
183
}
 
184
 
 
185
static int
 
186
ddksample_open (int dev, int mode, int open_flags)
 
187
{
 
188
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
189
  ddksample_devc *devc = ossddk_adev_get_devc (dev);
 
190
 
 
191
  mutex_enter (&devc->mutex);
 
192
 
 
193
  if (portc->open_mode != 0)    /* Device busy */
 
194
    {
 
195
      mutex_exit (&devc->mutex);
 
196
      return -EBUSY;
 
197
    }
 
198
 
 
199
  portc->open_mode = mode;
 
200
  devc->audio_open_count++;
 
201
  mutex_exit (&devc->mutex);
 
202
 
 
203
  return 0;
 
204
}
 
205
 
 
206
static void
 
207
ddksample_close (int dev, int mode)
 
208
{
 
209
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
210
  ddksample_devc *devc = ossddk_adev_get_devc (dev);
 
211
 
 
212
  ddksample_reset (dev);
 
213
 
 
214
  mutex_enter (&devc->mutex);
 
215
  portc->open_mode = 0;
 
216
  devc->audio_open_count--;
 
217
  mutex_exit (&devc->mutex);
 
218
}
 
219
 
 
220
static void
 
221
ddksample_trigger (int dev, int state)
 
222
{
 
223
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
224
 
 
225
  if (portc->open_mode & OPEN_WRITE)
 
226
    {
 
227
      if ((state & PCM_ENABLE_OUTPUT)
 
228
          && (portc->prepare_flags & PCM_ENABLE_OUTPUT))
 
229
        {
 
230
          portc->prepare_flags &= ~PCM_ENABLE_OUTPUT;
 
231
 
 
232
          /* 
 
233
           * Arm the timeout if it was not started yet.
 
234
           */
 
235
          if (portc->audio_active == 0)
 
236
            portc->timeout_id =
 
237
              timeout (ddksample_intr, portc, portc->timeout_value);
 
238
          portc->audio_active |= PCM_ENABLE_OUTPUT;
 
239
        }
 
240
      else
 
241
        if (!(state & PCM_ENABLE_OUTPUT)
 
242
            && (portc->audio_active & PCM_ENABLE_OUTPUT))
 
243
        {
 
244
          portc->audio_active &= ~PCM_ENABLE_OUTPUT;
 
245
          if (portc->audio_active == 0)
 
246
            untimeout (portc->timeout_id);
 
247
          portc->timeout_id = 0;
 
248
        }
 
249
    }
 
250
 
 
251
  if (portc->open_mode & OPEN_READ)
 
252
    {
 
253
      if ((state & PCM_ENABLE_INPUT)
 
254
          && (portc->prepare_flags & PCM_ENABLE_INPUT))
 
255
        {
 
256
          portc->prepare_flags &= ~PCM_ENABLE_INPUT;
 
257
 
 
258
          /* 
 
259
           * Arm the timeout if it was not started yet.
 
260
           */
 
261
          if (portc->audio_active == 0)
 
262
            portc->timeout_id =
 
263
              timeout (ddksample_intr, portc, portc->timeout_value);
 
264
          portc->audio_active |= PCM_ENABLE_INPUT;
 
265
        }
 
266
      else
 
267
        if (!(state & PCM_ENABLE_INPUT)
 
268
            && (portc->audio_active & PCM_ENABLE_INPUT))
 
269
        {
 
270
          portc->audio_active &= ~PCM_ENABLE_INPUT;
 
271
          if (portc->audio_active == 0)
 
272
            untimeout (portc->timeout_id);
 
273
          portc->timeout_id = 0;
 
274
        }
 
275
    }
 
276
}
 
277
 
 
278
static int
 
279
ddksample_prepare_for_input (int dev, int fragsize, int numfrags)
 
280
{
 
281
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
282
  int datarate, intrate, tmout;
 
283
 
 
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);
 
286
 
 
287
/*
 
288
 * Compute the number of lbolt ticks between interrupts. This value is needed
 
289
 * for timeout(9f).
 
290
 */
 
291
 
 
292
  datarate = portc->speed * portc->channels * portc->bits / 8;  /* Bytes per second */
 
293
  intrate = datarate * 10 / fragsize;
 
294
  tmout = hz * 1000 / intrate;
 
295
 
 
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,
 
298
           intrate % 10);
 
299
  cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
 
300
 
 
301
  tmout = (tmout + 50) / 100;   /* Round to the nearest integer value */
 
302
  if (tmout < 1)
 
303
    tmout = 1;
 
304
 
 
305
  portc->timeout_value = tmout;
 
306
  portc->prepare_flags |= PCM_ENABLE_INPUT;
 
307
  return 0;
 
308
}
 
309
 
 
310
static int
 
311
ddksample_prepare_for_output (int dev, int fragsize, int numfrags)
 
312
{
 
313
  ddksample_portc *portc = ossddk_adev_get_portc (dev);
 
314
  int datarate, intrate, tmout;
 
315
 
 
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);
 
318
 
 
319
/*
 
320
 * Compute the number of lbolt ticks between interrupts. This value is needed
 
321
 * for timeout(9f).
 
322
 */
 
323
 
 
324
  datarate = portc->speed * portc->channels * portc->bits / 8;  /* Bytes per second */
 
325
  intrate = datarate * 10 / fragsize;
 
326
  tmout = hz * 1000 / intrate;
 
327
 
 
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,
 
330
           intrate % 10);
 
331
  cmn_err (CE_CONT, "Timeout %d.%02d ticks\n", tmout / 100, tmout % 100);
 
332
 
 
333
  tmout = (tmout + 50) / 100;   /* Round to the nearest integer value */
 
334
  if (tmout < 1)
 
335
    tmout = 1;
 
336
 
 
337
  portc->timeout_value = tmout;
 
338
  portc->prepare_flags |= PCM_ENABLE_OUTPUT;
 
339
  return 0;
 
340
}
 
341
 
 
342
static int
 
343
ddksample_alloc_buffer (int dev, dmap_t * dmap, int direction)
 
344
{
 
345
#define MY_BUFFSIZE (64*1024)
 
346
  if (ossddk_dmap_get_dmabuf (dmap) != NULL)
 
347
    return 0;
 
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 */
 
351
    return -ENOSPC;
 
352
  ossddk_dmap_set_buffsize (dmap, MY_BUFFSIZE);
 
353
 
 
354
  return 0;
 
355
}
 
356
 
 
357
static int
 
358
ddksample_free_buffer (int dev, dmap_t * dmap, int direction)
 
359
{
 
360
  if (ossddk_dmap_get_dmabuf (dmap) == NULL)
 
361
    return 0;
 
362
  kmem_free (ossddk_dmap_get_dmabuf (dmap), MY_BUFFSIZE);
 
363
 
 
364
  ossddk_dmap_set_dmabuf (dmap, NULL);
 
365
  return 0;
 
366
}
 
367
 
 
368
#if 0
 
369
static int
 
370
ddksample_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
 
371
{
 
372
  return 0;
 
373
}
 
374
#endif
 
375
 
 
376
static audiodrv_t ddksample_audio_driver = {
 
377
  ddksample_open,
 
378
  ddksample_close,
 
379
  NULL,                         // output_block
 
380
  NULL,                         // start_input
 
381
  ddksample_ioctl,
 
382
  ddksample_prepare_for_input,
 
383
  ddksample_prepare_for_output,
 
384
  ddksample_reset,
 
385
  NULL,
 
386
  NULL,
 
387
  NULL,
 
388
  NULL,
 
389
  ddksample_trigger,
 
390
  ddksample_set_rate,
 
391
  ddksample_set_format,
 
392
  ddksample_set_channels,
 
393
  NULL,
 
394
  NULL,
 
395
  NULL,
 
396
  NULL,
 
397
  ddksample_alloc_buffer,
 
398
  ddksample_free_buffer,
 
399
  NULL,
 
400
  NULL,
 
401
  NULL                          /* ddksample_get_buffer_pointer */
 
402
};
 
403
 
 
404
int
 
405
ddksample_audio_init (ddksample_devc * devc)
 
406
{
 
407
  int dev;
 
408
  int i;
 
409
  static int rates[] = { 16000, 24000, 48000 };
 
410
 
 
411
  for (i = 0; i < MAX_SUBDEVICE; i++)
 
412
    {
 
413
      ddksample_portc *portc;
 
414
      int flags;
 
415
 
 
416
      flags = ADEV_VIRTUAL | ADEV_DUPLEX;
 
417
 
 
418
      if (i > 0)
 
419
        flags |= ADEV_SHADOW;
 
420
 
 
421
      if ((dev = ossddk_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
 
422
                                          devc->osdev,
 
423
                                          devc->osdev,
 
424
                                          "OSS DDK sample device",
 
425
                                          &ddksample_audio_driver,
 
426
                                          sizeof (audiodrv_t),
 
427
                                          flags,
 
428
                                          AFMT_S16_NE | AFMT_S32_NE,
 
429
                                          devc, -1)) < 0)
 
430
        return dev;
 
431
 
 
432
      portc = &devc->portc[devc->num_portc++];
 
433
 
 
434
      portc->dev = dev;
 
435
      portc->speed = 48000;
 
436
      portc->channels = 2;
 
437
      portc->format = AFMT_S16_NE;
 
438
      portc->bits = 16;
 
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;
 
445
      portc->mute = 0;
 
446
 
 
447
      ossddk_adev_set_portc (dev, portc);
 
448
      ossddk_adev_set_portnum (dev, i);
 
449
      ossddk_adev_set_mixer (dev, devc->mixer_dev);
 
450
 
 
451
      ossddk_adev_set_rates (dev, 16000, 48000, 3, rates);
 
452
      ossddk_adev_set_channels (dev, 1, 2);     /* Mono and stereo are supported */
 
453
 
 
454
      /*
 
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
 
459
       * playback formats.
 
460
       *
 
461
       * The next lines demonstrates how to do that. Normal drivers should
 
462
       * never do that.
 
463
       */
 
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
 
466
 
 
467
 
 
468
      /*
 
469
       * Report stereo as the preferred channel configuration.
 
470
       */
 
471
      ossddk_adev_set_caps (dev, DSP_CH_STEREO);
 
472
 
 
473
#if 0
 
474
      /*
 
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.
 
479
       *
 
480
       * Both the upper and lower limits are set using the same call.
 
481
       * Value of 0 means no limit.
 
482
       *
 
483
       * Here we set the upper limit to PAGE_SIZE and no lower limit.
 
484
       */
 
485
      ossddk_adev_set_buflimits (dev, 0, PAGE_SIZE);
 
486
#endif
 
487
 
 
488
#if 0
 
489
      /*
 
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 
 
495
       * untouched.
 
496
       *
 
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.
 
500
       */
 
501
      ossddk_adev_set_ratesource (dev, devc->some_known_device_number);
 
502
#endif
 
503
 
 
504
#if 0
 
505
      /*
 
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.
 
510
       *
 
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.
 
514
       *
 
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).
 
518
       */
 
519
      ossddk_adev_set_magic (dev, magic_number_assigned_for_this_driver);
 
520
#endif
 
521
    }
 
522
 
 
523
  return 0;
 
524
}