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

« back to all changes in this revision

Viewing changes to kernel/drv/oss_userdev/oss_userdev_devicepair.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
 * Purpose: Client/server audio device pair for oss_userdev
 
3
 *
 
4
 * This file implements the actual client/server device pair. There will be
 
5
 * separate oss_userdev instance for each process that has opened the
 
6
 * client side.
 
7
 */
 
8
/*
 
9
 *
 
10
 * This file is part of Open Sound System.
 
11
 *
 
12
 * Copyright (C) 4Front Technologies 1996-2008.
 
13
 *
 
14
 * This this source file is released under GPL v2 license (no other versions).
 
15
 * See the COPYING file included in the main directory of this source
 
16
 * distribution for the license terms and conditions.
 
17
 *
 
18
 */
 
19
 
 
20
#include "oss_userdev_cfg.h"
 
21
#include <oss_userdev_exports.h>
 
22
#include "userdev.h"
 
23
static void userdev_free_device_pair (userdev_devc_t *devc);
 
24
 
 
25
extern int userdev_visible_clientnodes;
 
26
 
 
27
static void
 
28
transfer_audio (userdev_portc_t * server_portc, dmap_t * dmap_from,
 
29
                dmap_t * dmap_to)
 
30
{
 
31
  int l = dmap_from->fragment_size;
 
32
  unsigned char *fromp, *top;
 
33
 
 
34
  if (dmap_to->fragment_size != l)
 
35
    {
 
36
      cmn_err (CE_WARN, "Fragment size mismatch (%d != %d)\n",
 
37
               dmap_to->fragment_size, l);
 
38
 
 
39
      /* Perform emergency stop */
 
40
      server_portc->input_triggered = 0;
 
41
      server_portc->output_triggered = 0;
 
42
      server_portc->peer->input_triggered = 0;
 
43
      server_portc->peer->output_triggered = 0;
 
44
      return;
 
45
    }
 
46
 
 
47
  fromp =
 
48
    dmap_from->dmabuf + (dmap_from->byte_counter % dmap_from->bytes_in_use);
 
49
  top = dmap_to->dmabuf + (dmap_to->byte_counter % dmap_to->bytes_in_use);
 
50
 
 
51
  memcpy (top, fromp, l);
 
52
 
 
53
}
 
54
 
 
55
static void
 
56
handle_input (userdev_portc_t * server_portc)
 
57
{
 
58
  userdev_portc_t *client_portc = server_portc->peer;
 
59
 
 
60
  if (client_portc->output_triggered)
 
61
    {
 
62
      transfer_audio (server_portc,
 
63
                      audio_engines[client_portc->audio_dev]->dmap_out,
 
64
                      audio_engines[server_portc->audio_dev]->dmap_in);
 
65
      oss_audio_outputintr (client_portc->audio_dev, 0);
 
66
    }
 
67
 
 
68
  oss_audio_inputintr (server_portc->audio_dev, 0);
 
69
}
 
70
 
 
71
static void
 
72
handle_output (userdev_portc_t * server_portc)
 
73
{
 
74
  userdev_portc_t *client_portc = server_portc->peer;
 
75
 
 
76
  if (client_portc->input_triggered)
 
77
    {
 
78
      transfer_audio (server_portc,
 
79
                      audio_engines[server_portc->audio_dev]->dmap_out,
 
80
                      audio_engines[client_portc->audio_dev]->dmap_in);
 
81
      oss_audio_inputintr (client_portc->audio_dev, 0);
 
82
    }
 
83
 
 
84
  oss_audio_outputintr (server_portc->audio_dev, 0);
 
85
}
 
86
 
 
87
static void
 
88
userdev_cb (void *pc)
 
89
{
 
90
/*
 
91
 * This timer callback routine will get called 100 times/second. It handles
 
92
 * movement of audio data between the client and server sides.
 
93
 */
 
94
  userdev_portc_t *server_portc = pc;
 
95
  userdev_devc_t *devc = server_portc->devc;
 
96
  int tmout = devc->poll_ticks;
 
97
 
 
98
  if (tmout < 1)
 
99
    tmout = 1;
 
100
 
 
101
  devc->timeout_id = 0; /* No longer valid */
 
102
 
 
103
  if (server_portc->input_triggered)
 
104
    handle_input (server_portc);
 
105
 
 
106
  if (server_portc->output_triggered)
 
107
    handle_output (server_portc);
 
108
 
 
109
  /* Retrigger timer callback */
 
110
  if (server_portc->input_triggered || server_portc->output_triggered)
 
111
    devc->timeout_id = timeout (userdev_cb, server_portc, tmout);
 
112
}
 
113
 
 
114
static int
 
115
userdev_check_input (int dev)
 
116
{
 
117
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
118
  if (!portc->peer->output_triggered)
 
119
    {
 
120
      return OSS_ECONNRESET;
 
121
    }
 
122
  return 0;
 
123
}
 
124
 
 
125
static int
 
126
userdev_check_output (int dev)
 
127
{
 
128
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
129
 
 
130
  if (!portc->peer->input_triggered)
 
131
    {
 
132
      return OSS_ECONNRESET;
 
133
    }
 
134
 
 
135
  if (portc->peer->open_mode == 0)
 
136
    return OSS_EIO;
 
137
  return 0;
 
138
}
 
139
 
 
140
static void
 
141
setup_sample_format (userdev_portc_t * portc)
 
142
{
 
143
  adev_t *adev;
 
144
  userdev_devc_t *devc = portc->devc;
 
145
  int fragsize, frame_size;
 
146
 
 
147
  frame_size = devc->channels * devc->fmt_bytes;
 
148
  if (frame_size == 0)
 
149
    frame_size = 4;
 
150
 
 
151
  fragsize = (devc->rate * frame_size * devc->poll_ticks) / OSS_HZ;     /* Number of bytes/fragment */
 
152
  devc->rate = fragsize * 100 / frame_size;
 
153
 
 
154
/* Setup the server side */
 
155
  adev = audio_engines[portc->audio_dev];
 
156
  adev->min_block = adev->max_block = fragsize;
 
157
 
 
158
/* Setup the client side */
 
159
  adev = audio_engines[portc->peer->audio_dev];
 
160
  adev->min_block = adev->max_block = fragsize;
 
161
 
 
162
  adev->max_rate = adev->min_rate = devc->rate;
 
163
  adev->iformat_mask = devc->fmt;
 
164
  adev->oformat_mask = devc->fmt;
 
165
  adev->xformat_mask = devc->fmt;
 
166
  adev->min_channels = adev->max_channels = devc->channels;
 
167
}
 
168
 
 
169
static int
 
170
userdev_server_set_rate (int dev, int arg)
 
171
{
 
172
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
173
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
174
  adev_t *client_adev = audio_engines[portc->peer->audio_dev];
 
175
 
 
176
  if (arg == 0)
 
177
    return devc->rate;
 
178
 
 
179
  if (portc->peer->input_triggered || portc->peer->output_triggered)
 
180
    return devc->rate;
 
181
 
 
182
  if (arg < 5000)
 
183
    arg = 5000;
 
184
  if (arg > MAX_RATE)
 
185
    arg = MAX_RATE;
 
186
 
 
187
  /* Force the sample rate to be multiple of 100 */
 
188
  arg = (arg / 100) * 100;
 
189
 
 
190
  devc->rate = arg;
 
191
 
 
192
  client_adev->min_rate = arg;
 
193
  client_adev->max_rate = arg;
 
194
 
 
195
  setup_sample_format (portc);
 
196
 
 
197
  return devc->rate = arg;
 
198
}
 
199
 
 
200
/*ARGSUSED*/
 
201
static int
 
202
userdev_client_set_rate (int dev, int arg)
 
203
{
 
204
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
205
 
 
206
  return devc->rate;
 
207
}
 
208
 
 
209
static short
 
210
userdev_server_set_channels (int dev, short arg)
 
211
{
 
212
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
213
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
214
  adev_t *client_adev = audio_engines[portc->peer->audio_dev];
 
215
 
 
216
  if (arg == 0)
 
217
    return devc->channels;
 
218
 
 
219
  if (portc->peer->input_triggered || portc->peer->output_triggered)
 
220
    return devc->channels;
 
221
 
 
222
  if (arg < 1)
 
223
    arg = 1;
 
224
  if (arg > MAX_CHANNELS)
 
225
    arg = MAX_CHANNELS;
 
226
 
 
227
  devc->channels = arg;
 
228
  client_adev->min_channels=client_adev->max_channels=arg;
 
229
 
 
230
  setup_sample_format (portc);
 
231
 
 
232
  return devc->channels;
 
233
}
 
234
 
 
235
/*ARGSUSED*/
 
236
static short
 
237
userdev_client_set_channels (int dev, short arg)
 
238
{
 
239
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
240
 
 
241
  return devc->channels;        /* Server side channels */
 
242
}
 
243
 
 
244
static unsigned int
 
245
userdev_server_set_format (int dev, unsigned int arg)
 
246
{
 
247
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
248
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
249
  adev_t *client_adev = audio_engines[portc->peer->audio_dev];
 
250
 
 
251
  if (arg == 0)
 
252
    return devc->fmt;
 
253
 
 
254
  if (portc->peer->input_triggered || portc->peer->output_triggered)
 
255
    return devc->fmt;
 
256
 
 
257
  switch (arg)
 
258
    {
 
259
    case AFMT_S16_NE:
 
260
      devc->fmt_bytes = 2;
 
261
      break;
 
262
 
 
263
    case AFMT_S32_NE:
 
264
      devc->fmt_bytes = 4;
 
265
      break;
 
266
 
 
267
    default:                    /* Unsupported format */
 
268
      arg = AFMT_S16_NE;
 
269
      devc->fmt_bytes = 2;
 
270
 
 
271
    }
 
272
 
 
273
  devc->fmt = arg;
 
274
 
 
275
  client_adev->oformat_mask = arg;
 
276
  client_adev->iformat_mask = arg;
 
277
 
 
278
  setup_sample_format (portc);
 
279
 
 
280
  return devc->fmt;
 
281
}
 
282
 
 
283
/*ARGSUSED*/
 
284
static unsigned int
 
285
userdev_client_set_format (int dev, unsigned int arg)
 
286
{
 
287
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
288
 
 
289
  return devc->fmt;     /* Server side sample format */
 
290
}
 
291
 
 
292
static void userdev_trigger (int dev, int state);
 
293
 
 
294
static void
 
295
userdev_reset (int dev)
 
296
{
 
297
  userdev_trigger (dev, 0);
 
298
}
 
299
 
 
300
/*ARGSUSED*/
 
301
static int
 
302
userdev_server_open (int dev, int mode, int open_flags)
 
303
{
 
304
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
305
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
306
  oss_native_word flags;
 
307
 
 
308
  if (portc == NULL || portc->peer == NULL)
 
309
    return OSS_ENXIO;
 
310
 
 
311
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
312
 
 
313
  if (portc->open_mode)
 
314
    {
 
315
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
316
      return OSS_EBUSY;
 
317
    }
 
318
 
 
319
  portc->open_mode = mode;
 
320
 
 
321
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
322
 
 
323
  devc->open_count++;
 
324
 
 
325
  return 0;
 
326
}
 
327
 
 
328
/*ARGSUSED*/
 
329
static int
 
330
userdev_client_open (int dev, int mode, int open_flags)
 
331
{
 
332
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
333
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
334
  oss_native_word flags;
 
335
 
 
336
  if (portc == NULL || portc->peer == NULL)
 
337
    return OSS_ENXIO;
 
338
 
 
339
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
340
 
 
341
  if (portc->open_mode)
 
342
    {
 
343
      MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
344
      return OSS_EBUSY;
 
345
    }
 
346
 
 
347
  portc->open_mode = mode;
 
348
  devc->open_count++;
 
349
 
 
350
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
351
  return 0;
 
352
}
 
353
 
 
354
static void
 
355
wipe_audio_buffers(userdev_devc_t *devc)
 
356
{
 
357
/*
 
358
 * Write silence to the audio buffers when only one of the sides
 
359
 * is open. This prevents the device from looping the last fragments
 
360
 * written to the device.
 
361
 */
 
362
        dmap_t *dmap;
 
363
 
 
364
        dmap = audio_engines[devc->client_portc.audio_dev]->dmap_out;
 
365
        if (dmap != NULL && dmap->dmabuf != NULL)
 
366
           memset(dmap->dmabuf, 0, dmap->buffsize);
 
367
 
 
368
        dmap = audio_engines[devc->client_portc.audio_dev]->dmap_in;
 
369
        if (dmap != NULL && dmap->dmabuf != NULL)
 
370
           memset(dmap->dmabuf, 0, dmap->buffsize);
 
371
 
 
372
        dmap = audio_engines[devc->server_portc.audio_dev]->dmap_out;
 
373
        if (dmap != NULL && dmap->dmabuf != NULL)
 
374
           memset(dmap->dmabuf, 0, dmap->buffsize);
 
375
 
 
376
        dmap = audio_engines[devc->server_portc.audio_dev]->dmap_in;
 
377
        if (dmap != NULL && dmap->dmabuf != NULL)
 
378
           memset(dmap->dmabuf, 0, dmap->buffsize);
 
379
}
 
380
 
 
381
/*ARGSUSED*/
 
382
static void
 
383
userdev_server_close (int dev, int mode)
 
384
{
 
385
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
386
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
387
  oss_native_word flags;
 
388
  int open_count;
 
389
 
 
390
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
391
  portc->open_mode = 0;
 
392
 
 
393
  /* Stop the client side because there is no server */
 
394
  portc->peer->input_triggered = 0;
 
395
  portc->peer->output_triggered = 0;
 
396
  open_count = --devc->open_count;
 
397
 
 
398
  if (open_count == 0)
 
399
     userdev_free_device_pair (devc);
 
400
  else
 
401
     wipe_audio_buffers(devc);
 
402
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
403
}
 
404
 
 
405
/*ARGSUSED*/
 
406
static void
 
407
userdev_client_close (int dev, int mode)
 
408
{
 
409
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
410
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
411
  oss_native_word flags;
 
412
  int open_count;
 
413
 
 
414
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
415
  portc->open_mode = 0;
 
416
 
 
417
  open_count = --devc->open_count;
 
418
 
 
419
  if (open_count == 0)
 
420
     userdev_free_device_pair (devc);
 
421
  else
 
422
     wipe_audio_buffers(devc);
 
423
 
 
424
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
425
}
 
426
 
 
427
/*ARGSUSED*/
 
428
static int
 
429
userdev_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
 
430
{
 
431
  switch (cmd)
 
432
    {
 
433
    case SNDCTL_GETLABEL:
 
434
      {
 
435
        /*
 
436
         * Return an empty string so that this feature can be tested.
 
437
         * Complete functionality is to be implemented later.
 
438
         */
 
439
        oss_label_t *s = (oss_label_t *) arg;
 
440
        memset (s, 0, sizeof (oss_label_t));
 
441
        return 0;
 
442
      }
 
443
      break;
 
444
 
 
445
    case SNDCTL_GETSONG:
 
446
      {
 
447
        /*
 
448
         * Return an empty string so that this feature can be tested.
 
449
         * Complete functionality is to be implemented later.
 
450
         */
 
451
        oss_longname_t *s = (oss_longname_t *) arg;
 
452
        memset (s, 0, sizeof (oss_longname_t));
 
453
        return 0;
 
454
      }
 
455
      break;
 
456
    }
 
457
 
 
458
  return OSS_EINVAL;
 
459
}
 
460
 
 
461
static void
 
462
set_adev_name(int dev, const char *name)
 
463
{
 
464
  adev_t *adev = audio_engines[dev];
 
465
 
 
466
  strcpy(adev->name, name);
 
467
 
 
468
#ifdef CONFIG_OSS_VMIX
 
469
  if (adev->vmix_mixer != NULL)
 
470
     vmix_change_devnames(adev->vmix_mixer, name);
 
471
#endif
 
472
        
 
473
}
 
474
 
 
475
static int
 
476
create_instance(int dev, userdev_create_t *crea)
 
477
{
 
478
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
479
  char tmp_name[64];
 
480
 
 
481
  devc->match_method = crea->match_method;
 
482
  devc->match_key = crea->match_key;
 
483
  devc->create_flags = crea->flags;
 
484
 
 
485
  devc->poll_ticks = (crea->poll_interval * OSS_HZ) / 1000;
 
486
 
 
487
  if (devc->poll_ticks < 1) 
 
488
     devc->poll_ticks = 1;
 
489
 
 
490
  crea->poll_interval = (1000*devc->poll_ticks) / OSS_HZ;
 
491
 
 
492
  if (crea->poll_interval<1)
 
493
     crea->poll_interval = 1;
 
494
 
 
495
  crea->name[sizeof(crea->name)-1]=0; /* Overflow protectgion */
 
496
 
 
497
  sprintf(tmp_name, "%s (server)", crea->name);
 
498
  tmp_name[49]=0;
 
499
  set_adev_name (devc->client_portc.audio_dev, crea->name);
 
500
  set_adev_name (devc->server_portc.audio_dev, tmp_name);
 
501
 
 
502
  strcpy(crea->devnode, audio_engines[devc->client_portc.audio_dev]->devnode);
 
503
 
 
504
  return 0;
 
505
}
 
506
 
 
507
static int
 
508
userdev_set_control (int dev, int ctl, unsigned int cmd, int value)
 
509
{
 
510
        userdev_devc_t *devc = mixer_devs[dev]->devc;
 
511
 
 
512
        if (ctl < 0 || ctl >= USERDEV_MAX_MIXERS)
 
513
           return OSS_EINVAL;
 
514
 
 
515
        if (cmd == SNDCTL_MIX_READ)
 
516
           {
 
517
                   return devc->mixer_values[ctl];
 
518
           }
 
519
 
 
520
        devc->mixer_values[ctl] = value;
 
521
        devc->modify_counter++;
 
522
 
 
523
        return 0;
 
524
}
 
525
 
 
526
/*ARGSUSED*/
 
527
static int
 
528
userdev_server_ioctl (int dev, unsigned int cmd, ioctl_arg arg)
 
529
{
 
530
  switch (cmd)
 
531
    {
 
532
    case USERDEV_CREATE_INSTANCE:
 
533
            {
 
534
                userdev_create_t *crea = (userdev_create_t *)arg;
 
535
 
 
536
                return create_instance(dev, crea);
 
537
            }
 
538
            break;
 
539
 
 
540
    case USERDEV_GET_CLIENTCOUNT:
 
541
            {
 
542
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
543
 
 
544
                return *arg = (devc->client_portc.open_mode != 0);
 
545
            }
 
546
            break;
 
547
 
 
548
   /*
 
549
    * Mixer related ioctl calls
 
550
    */
 
551
 
 
552
    case USERDEV_CREATE_MIXGROUP:
 
553
            {
 
554
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
555
                userdev_mixgroup_t *grp=(userdev_mixgroup_t*)arg;
 
556
                int group;
 
557
 
 
558
                grp->name[sizeof(grp->name)-1]=0; /* Buffer overflow protection */
 
559
                if ((group=mixer_ext_create_group(devc->mixer_dev, grp->parent, grp->name))<0)
 
560
                        return group;
 
561
                grp->num = group;
 
562
                return 0;
 
563
            }
 
564
            break;
 
565
 
 
566
    case USERDEV_CREATE_MIXCTL:
 
567
            {
 
568
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
569
                userdev_mixctl_t *c=(userdev_mixctl_t*)arg;
 
570
                oss_mixext *ext;
 
571
                int ctl;
 
572
 
 
573
                c->name[sizeof(c->name)-1]=0; /* Buffer overflow protection */
 
574
 
 
575
                if (c->index < 0 || c->index >= USERDEV_MAX_MIXERS)
 
576
                   return OSS_EINVAL;
 
577
 
 
578
                if ((ctl = mixer_ext_create_control (devc->mixer_dev,
 
579
                               c->parent,
 
580
                               c->index,
 
581
                               userdev_set_control,
 
582
                               c->type,
 
583
                               c->name,
 
584
                               c->maxvalue,
 
585
                               c->flags)) < 0)
 
586
                        return ctl;
 
587
 
 
588
                c->num = ctl;
 
589
                ext = mixer_find_ext (devc->mixer_dev, ctl);
 
590
 
 
591
                ext->minvalue = c->offset;
 
592
                ext->control_no= c->control_no;
 
593
                ext->rgbcolor = c->rgbcolor;
 
594
 
 
595
                if (c->type == MIXT_ENUM)
 
596
                {
 
597
                        memcpy(ext->enum_present, c->enum_present, sizeof(ext->enum_present));
 
598
                        mixer_ext_set_strings (devc->mixer_dev, ctl, c->enum_choises, 0);
 
599
                }
 
600
 
 
601
                return 0;
 
602
            }
 
603
            break;
 
604
 
 
605
    case USERDEV_GET_MIX_CHANGECOUNT:
 
606
            {
 
607
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
608
 
 
609
                return *arg = devc->modify_counter;
 
610
            }
 
611
            break;
 
612
 
 
613
    case USERDEV_SET_MIXERS:
 
614
            {
 
615
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
616
 
 
617
                memcpy(devc->mixer_values, arg, sizeof(devc->mixer_values));
 
618
                mixer_devs[devc->mixer_dev]->modify_counter++;
 
619
//cmn_err(CE_CONT, "Set %08x %08x\n", devc->mixer_values[0], devc->mixer_values[1]);
 
620
                return 0;
 
621
            }
 
622
            break;
 
623
 
 
624
    case USERDEV_GET_MIXERS:
 
625
            {
 
626
                userdev_devc_t *devc = audio_engines[dev]->devc;
 
627
 
 
628
                memcpy(arg, devc->mixer_values, sizeof(devc->mixer_values));
 
629
                return 0;
 
630
            }
 
631
            break;
 
632
 
 
633
    }
 
634
 
 
635
  return userdev_ioctl(dev, cmd, arg);
 
636
}
 
637
 
 
638
/*ARGSUSED*/
 
639
static void
 
640
userdev_output_block (int dev, oss_native_word buf, int count, int fragsize,
 
641
                        int intrflag)
 
642
{
 
643
}
 
644
 
 
645
/*ARGSUSED*/
 
646
static void
 
647
userdev_start_input (int dev, oss_native_word buf, int count, int fragsize,
 
648
                       int intrflag)
 
649
{
 
650
}
 
651
 
 
652
static void
 
653
userdev_trigger (int dev, int state)
 
654
{
 
655
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
656
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
657
 
 
658
  if (portc->open_mode & OPEN_READ)     /* Handle input */
 
659
    {
 
660
      portc->input_triggered = !!(state & OPEN_READ);
 
661
    }
 
662
 
 
663
  if (portc->open_mode & OPEN_WRITE)    /* Handle output */
 
664
    {
 
665
      portc->output_triggered = !!(state & OPEN_WRITE);
 
666
    }
 
667
 
 
668
  if (portc->output_triggered || portc->input_triggered)        /* Something is going on */
 
669
    {
 
670
      int tmout = devc->poll_ticks;
 
671
 
 
672
      if (tmout < 1)
 
673
        tmout = 1;
 
674
 
 
675
      if (portc->port_type != PT_SERVER)
 
676
        portc = portc->peer;    /* Switch to the server side */
 
677
 
 
678
      if (portc->output_triggered || portc->input_triggered)    /* Something is going on */
 
679
        if (devc->timeout_id == 0)
 
680
        {
 
681
          devc->timeout_id = timeout (userdev_cb, portc, tmout);
 
682
        }
 
683
    }
 
684
  else
 
685
    {
 
686
      if (portc->port_type == PT_SERVER)
 
687
        if (devc->timeout_id != 0)
 
688
          {
 
689
            untimeout (devc->timeout_id);
 
690
            devc->timeout_id = 0;
 
691
          }
 
692
    }
 
693
}
 
694
 
 
695
/*ARGSUSED*/
 
696
static int
 
697
userdev_server_prepare_for_input (int dev, int bsize, int bcount)
 
698
{
 
699
  oss_native_word flags;
 
700
 
 
701
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
702
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
703
 
 
704
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
705
  portc->input_triggered = 0;
 
706
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
707
 
 
708
  return 0;
 
709
}
 
710
 
 
711
/*ARGSUSED*/
 
712
static int
 
713
userdev_server_prepare_for_output (int dev, int bsize, int bcount)
 
714
{
 
715
  oss_native_word flags;
 
716
 
 
717
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
718
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
719
 
 
720
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
721
  portc->output_triggered = 0;
 
722
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
723
 
 
724
  return 0;
 
725
}
 
726
 
 
727
/*ARGSUSED*/
 
728
static int
 
729
userdev_client_prepare_for_input (int dev, int bsize, int bcount)
 
730
{
 
731
  oss_native_word flags;
 
732
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
733
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
734
 
 
735
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
736
  portc->input_triggered = 0;
 
737
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
738
 
 
739
  return 0;
 
740
}
 
741
 
 
742
/*ARGSUSED*/
 
743
static int
 
744
userdev_client_prepare_for_output (int dev, int bsize, int bcount)
 
745
{
 
746
  oss_native_word flags;
 
747
  userdev_portc_t *portc = audio_engines[dev]->portc;
 
748
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
749
 
 
750
  MUTEX_ENTER_IRQDISABLE (devc->mutex, flags);
 
751
  portc->output_triggered = 0;
 
752
  MUTEX_EXIT_IRQRESTORE (devc->mutex, flags);
 
753
 
 
754
  return 0;
 
755
}
 
756
 
 
757
/*ARGSUSED*/
 
758
static int
 
759
userdev_alloc_buffer (int dev, dmap_t * dmap, int direction)
 
760
{
 
761
#define MY_BUFFSIZE (64*1024)
 
762
  if (dmap->dmabuf != NULL)
 
763
    return 0;
 
764
  dmap->dmabuf_phys = 0;        /* Not mmap() capable */
 
765
  dmap->dmabuf = KERNEL_MALLOC (MY_BUFFSIZE);
 
766
  if (dmap->dmabuf == NULL)
 
767
    return OSS_ENOSPC;
 
768
  dmap->buffsize = MY_BUFFSIZE;
 
769
 
 
770
  return 0;
 
771
}
 
772
 
 
773
/*ARGSUSED*/
 
774
static int
 
775
userdev_free_buffer (int dev, dmap_t * dmap, int direction)
 
776
{
 
777
  if (dmap->dmabuf == NULL)
 
778
    return 0;
 
779
  KERNEL_FREE (dmap->dmabuf);
 
780
 
 
781
  dmap->dmabuf = NULL;
 
782
  return 0;
 
783
}
 
784
 
 
785
#if 0
 
786
static int
 
787
userdev_get_buffer_pointer (int dev, dmap_t * dmap, int direction)
 
788
{
 
789
}
 
790
#endif
 
791
 
 
792
/*ARGSUSED*/
 
793
static int
 
794
userdev_ioctl_override (int dev, unsigned int cmd, ioctl_arg arg)
 
795
{
 
796
/*
 
797
 * Purpose of this ioctl override function is to intercept mixer
 
798
 * ioctl calls made on the client side and to hide everything
 
799
 * outside the userdev instance from the application.
 
800
 *
 
801
 * Note that this ioctl is related with the client side audio device. However
 
802
 * if /dev/mixer points to this (audio) device then all mixer acess will
 
803
 * be redirected too. Also the vmix driver will redirect mixer/system ioctl
 
804
 * calls to this function.
 
805
 */
 
806
  int err;
 
807
  userdev_devc_t *devc = audio_engines[dev]->devc;
 
808
  adev_t *adev = audio_engines[devc->client_portc.audio_dev];
 
809
 
 
810
  switch (cmd)
 
811
  {
 
812
  case SNDCTL_MIX_NRMIX:
 
813
          return *arg=1;
 
814
          break;
 
815
 
 
816
  case SNDCTL_MIX_NREXT:
 
817
          *arg = devc->mixer_dev;
 
818
          return OSS_EAGAIN; /* Continue with the default handler */
 
819
          break;
 
820
 
 
821
  case SNDCTL_SYSINFO:
 
822
          {
 
823
          /*
 
824
           * Fake SNDCTL_SYSINFO to report just one mixer device which is
 
825
           * the one associated with the client.
 
826
           */
 
827
            oss_sysinfo *info = (oss_sysinfo *) arg;
 
828
            int i;
 
829
 
 
830
            if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
 
831
               return err;
 
832
 
 
833
            /*
 
834
             * Hide all non-oss_userdev devices
 
835
             */
 
836
            strcpy (info->product, "OSS (userdev)");
 
837
            info->nummixers = 1;
 
838
            info->numcards = 1;
 
839
            info->numaudios = 1;
 
840
            for (i = 0; i < 8; i++)
 
841
               info->openedaudio[i] = 0;
 
842
 
 
843
            return 0;
 
844
          }
 
845
          break;
 
846
 
 
847
  case SNDCTL_MIXERINFO:
 
848
        {
 
849
                oss_mixerinfo *info = (oss_mixerinfo *) arg;
 
850
 
 
851
                info->dev = devc->mixer_dev; /* Redirect to oss_userdev mixer */
 
852
 
 
853
                if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
 
854
                   return err;
 
855
 
 
856
                strcpy(info->name, adev->name);
 
857
                info->card_number = 0;
 
858
                return 0;
 
859
        }
 
860
 
 
861
  case SNDCTL_AUDIOINFO:
 
862
  case SNDCTL_AUDIOINFO_EX:
 
863
        {
 
864
                oss_audioinfo *info = (oss_audioinfo *) arg;
 
865
 
 
866
                info->dev = devc->client_portc.audio_dev;
 
867
 
 
868
                cmd = SNDCTL_ENGINEINFO;
 
869
 
 
870
                if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
 
871
                   return err;
 
872
 
 
873
                info->card_number = 0;
 
874
                return 0;
 
875
        }
 
876
 
 
877
    case SNDCTL_CARDINFO:
 
878
      {
 
879
        oss_card_info *info = (oss_card_info *) arg;
 
880
 
 
881
        info->card = adev->card_number; /* Redirect to oss_userdev0 */
 
882
 
 
883
        if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
 
884
           return err;
 
885
 
 
886
        info->card = 0;
 
887
        return 0;
 
888
      }
 
889
 
 
890
  case SNDCTL_MIX_EXTINFO:
 
891
      {
 
892
              oss_mixext *ext = (oss_mixext*)arg;
 
893
 
 
894
              ext->dev = devc->mixer_dev;
 
895
 
 
896
              if ((err=oss_mixer_ext(dev, OSS_DEV_DSP_ENGINE, cmd, arg))<0)
 
897
                   return err;
 
898
 
 
899
              if (ext->type == MIXT_DEVROOT)
 
900
              {
 
901
                oss_mixext_root *root = (oss_mixext_root *) ext->data;
 
902
                strncpy(root->name, adev->name, 48);
 
903
                root->name[47]=0;
 
904
              }
 
905
 
 
906
              return 0;
 
907
      }
 
908
      break;
 
909
 
 
910
  case SNDCTL_MIX_READ:
 
911
  case SNDCTL_MIX_WRITE:
 
912
      {
 
913
              oss_mixer_value *ent = (oss_mixer_value*)arg;
 
914
 
 
915
              ent->dev = devc->mixer_dev;
 
916
 
 
917
              return OSS_EAGAIN;        /* Redirect */
 
918
      }
 
919
      break;
 
920
 
 
921
  default:
 
922
          return OSS_EAGAIN;
 
923
  }
 
924
}
 
925
 
 
926
static audiodrv_t userdev_server_driver = {
 
927
  userdev_server_open,
 
928
  userdev_server_close,
 
929
  userdev_output_block,
 
930
  userdev_start_input,
 
931
  userdev_server_ioctl,
 
932
  userdev_server_prepare_for_input,
 
933
  userdev_server_prepare_for_output,
 
934
  userdev_reset,
 
935
  NULL,
 
936
  NULL,
 
937
  NULL,
 
938
  NULL,
 
939
  userdev_trigger,
 
940
  userdev_server_set_rate,
 
941
  userdev_server_set_format,
 
942
  userdev_server_set_channels,
 
943
  NULL,
 
944
  NULL,
 
945
  userdev_check_input,
 
946
  userdev_check_output,
 
947
  userdev_alloc_buffer,
 
948
  userdev_free_buffer,
 
949
  NULL,
 
950
  NULL,
 
951
  NULL                          /* userdev_get_buffer_pointer */
 
952
};
 
953
 
 
954
static audiodrv_t userdev_client_driver = {
 
955
  userdev_client_open,
 
956
  userdev_client_close,
 
957
  userdev_output_block,
 
958
  userdev_start_input,
 
959
  userdev_ioctl,
 
960
  userdev_client_prepare_for_input,
 
961
  userdev_client_prepare_for_output,
 
962
  userdev_reset,
 
963
  NULL,
 
964
  NULL,
 
965
  NULL,
 
966
  NULL,
 
967
  userdev_trigger,
 
968
  userdev_client_set_rate,
 
969
  userdev_client_set_format,
 
970
  userdev_client_set_channels,
 
971
  NULL,
 
972
  NULL,
 
973
  userdev_check_input,
 
974
  userdev_check_output,
 
975
  userdev_alloc_buffer,
 
976
  userdev_free_buffer,
 
977
  NULL,
 
978
  NULL,
 
979
  NULL, // userdev_get_buffer_pointer
 
980
  NULL, // userdev_calibrate_speed
 
981
  NULL, // userdev_sync_control
 
982
  NULL, // userdev_prepare_to_stop
 
983
  NULL, // userdev_get_input_pointer
 
984
  NULL, // userdev_get_output_pointer
 
985
  NULL, // userdev_bind
 
986
  NULL, // userdev_setup_fragments
 
987
  NULL, // userdev_redirect
 
988
  userdev_ioctl_override
 
989
};
 
990
 
 
991
static int
 
992
userdev_mixer_ioctl (int dev, int audiodev, unsigned int cmd, ioctl_arg arg)
 
993
{
 
994
 
 
995
  if (cmd == SOUND_MIXER_READ_CAPS)
 
996
    return *arg = SOUND_CAP_NOLEGACY;
 
997
 
 
998
#if 0
 
999
  if (cmd == SOUND_MIXER_READ_DEVMASK ||
 
1000
      cmd == SOUND_MIXER_READ_RECMASK || cmd == SOUND_MIXER_READ_RECSRC)
 
1001
    return *arg = 0;
 
1002
 
 
1003
  if (cmd == SOUND_MIXER_READ_VOLUME || cmd == SOUND_MIXER_READ_PCM)
 
1004
    return *arg = 100 | (100 << 8);
 
1005
  if (cmd == SOUND_MIXER_WRITE_VOLUME || cmd == SOUND_MIXER_WRITE_PCM)
 
1006
    return *arg = 100 | (100 << 8);
 
1007
#endif
 
1008
  return OSS_EINVAL;
 
1009
}
 
1010
 
 
1011
static mixer_driver_t userdev_mixer_driver = {
 
1012
  userdev_mixer_ioctl
 
1013
};
 
1014
 
 
1015
static int
 
1016
install_server (userdev_devc_t * devc)
 
1017
{
 
1018
  userdev_portc_t *portc = &devc->server_portc;
 
1019
  int adev;
 
1020
 
 
1021
  int opts =
 
1022
    ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL |
 
1023
    ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_HIDDEN | ADEV_DUPLEX;
 
1024
 
 
1025
  memset (portc, 0, sizeof (*portc));
 
1026
 
 
1027
  portc->devc = devc;
 
1028
  portc->port_type = PT_SERVER;
 
1029
 
 
1030
  if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
 
1031
                                    devc->osdev,
 
1032
                                    devc->osdev,
 
1033
                                    "User space audio device server side",
 
1034
                                    &userdev_server_driver,
 
1035
                                    sizeof (audiodrv_t),
 
1036
                                    opts, SUPPORTED_FORMATS, devc, -1)) < 0)
 
1037
    {
 
1038
      return adev;
 
1039
    }
 
1040
 
 
1041
  audio_engines[adev]->portc = portc;
 
1042
  audio_engines[adev]->min_rate = 5000;
 
1043
  audio_engines[adev]->max_rate = MAX_RATE;
 
1044
  audio_engines[adev]->min_channels = 1;
 
1045
  audio_engines[adev]->max_channels = MAX_CHANNELS;
 
1046
  audio_engines[adev]->vmix_mixer=NULL;
 
1047
  strcpy(audio_engines[adev]->devnode, userdev_server_devnode);
 
1048
 
 
1049
  portc->audio_dev = adev;
 
1050
 
 
1051
  return adev;
 
1052
}
 
1053
 
 
1054
static int 
 
1055
null_mixer_init(int de)
 
1056
{
 
1057
        return 0;
 
1058
}
 
1059
 
 
1060
static void
 
1061
userdev_create_mixer(userdev_devc_t * devc)
 
1062
{
 
1063
  if ((devc->mixer_dev = oss_install_mixer (OSS_MIXER_DRIVER_VERSION,
 
1064
                                     devc->osdev,
 
1065
                                     devc->osdev,
 
1066
                                     "OSS userdev mixer",
 
1067
                                     &userdev_mixer_driver,
 
1068
                                     sizeof (mixer_driver_t), devc)) < 0)
 
1069
    {
 
1070
        devc->mixer_dev = -1;
 
1071
        return;
 
1072
    }
 
1073
  mixer_ext_set_init_fn (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2);
 
1074
}
 
1075
 
 
1076
static int
 
1077
install_client (userdev_devc_t * devc)
 
1078
{
 
1079
  userdev_portc_t *portc = &devc->client_portc;
 
1080
  int adev;
 
1081
 
 
1082
  int opts =
 
1083
    ADEV_STEREOONLY | ADEV_16BITONLY | ADEV_VIRTUAL | ADEV_DUPLEX |
 
1084
    ADEV_FIXEDRATE | ADEV_SPECIAL | ADEV_LOOP;
 
1085
 
 
1086
  memset (portc, 0, sizeof (*portc));
 
1087
 
 
1088
  userdev_create_mixer(devc);
 
1089
 
 
1090
  portc->devc = devc;
 
1091
  portc->port_type = PT_CLIENT;
 
1092
 
 
1093
  if (!userdev_visible_clientnodes && !(devc->create_flags & USERDEV_F_VMIX_PRIVATENODE))
 
1094
  {
 
1095
     opts |= ADEV_HIDDEN;
 
1096
  }
 
1097
 
 
1098
  if ((adev = oss_install_audiodev (OSS_AUDIO_DRIVER_VERSION,
 
1099
                                    devc->osdev,
 
1100
                                    devc->osdev,
 
1101
                                    "User space audio device",
 
1102
                                    &userdev_client_driver,
 
1103
                                    sizeof (audiodrv_t),
 
1104
                                    opts, SUPPORTED_FORMATS, devc, -1)) < 0)
 
1105
    {
 
1106
      return adev;
 
1107
    }
 
1108
 
 
1109
  if (!userdev_visible_clientnodes) /* Invisible client device nodes */
 
1110
     strcpy(audio_engines[adev]->devnode, userdev_client_devnode);
 
1111
 
 
1112
  audio_engines[adev]->portc = portc;
 
1113
  audio_engines[adev]->mixer_dev = devc->mixer_dev;
 
1114
  audio_engines[adev]->min_rate = 5000;
 
1115
  audio_engines[adev]->max_rate = MAX_RATE;
 
1116
  audio_engines[adev]->min_channels = 1;
 
1117
  audio_engines[adev]->max_channels = MAX_CHANNELS;
 
1118
 
 
1119
  portc->audio_dev = adev;
 
1120
#ifdef CONFIG_OSS_VMIX
 
1121
  vmix_attach_audiodev(devc->osdev, adev, -1, 0);
 
1122
#endif
 
1123
 
 
1124
  return adev;
 
1125
}
 
1126
 
 
1127
int
 
1128
userdev_create_device_pair(void)
 
1129
{
 
1130
  int client_engine, server_engine;
 
1131
  userdev_devc_t *devc;
 
1132
  oss_native_word flags;
 
1133
 
 
1134
  if ((devc=PMALLOC(userdev_osdev, sizeof (*devc))) == NULL)
 
1135
     return OSS_ENOMEM;
 
1136
  memset(devc, 0, sizeof(*devc));
 
1137
 
 
1138
  devc->osdev = userdev_osdev;
 
1139
  MUTEX_INIT (devc->osdev, devc->mutex, MH_DRV);
 
1140
  devc->active=1;
 
1141
 
 
1142
  devc->rate = 48000;
 
1143
  devc->fmt = AFMT_S16_NE;
 
1144
  devc->fmt_bytes = 2;
 
1145
  devc->channels = 2;
 
1146
  devc->poll_ticks = 10;
 
1147
 
 
1148
  if ((server_engine=install_server (devc)) < 0)
 
1149
     return server_engine;
 
1150
 
 
1151
  if ((client_engine=install_client (devc)) < 0)
 
1152
        return client_engine;
 
1153
 
 
1154
  devc->client_portc.peer = &devc->server_portc;
 
1155
  devc->server_portc.peer = &devc->client_portc;
 
1156
 
 
1157
  /*
 
1158
   * Insert the device to the list of available devices
 
1159
   */
 
1160
  MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
 
1161
  devc->next_instance = userdev_active_device_list;
 
1162
  userdev_active_device_list = devc;
 
1163
  MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
 
1164
 
 
1165
  return server_engine;
 
1166
}
 
1167
 
 
1168
static void
 
1169
userdev_free_device_pair (userdev_devc_t *devc)
 
1170
{
 
1171
  oss_native_word flags;
 
1172
 
 
1173
  set_adev_name(devc->client_portc.audio_dev, "User space audio device");
 
1174
  set_adev_name(devc->server_portc.audio_dev, "User space audio device server side");
 
1175
 
 
1176
  MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
 
1177
 
 
1178
  devc->match_method = 0;
 
1179
  devc->match_key = 0;
 
1180
 
 
1181
  /*
 
1182
   * Add to the free device pair list.
 
1183
   */
 
1184
  devc->next_instance = userdev_free_device_list;
 
1185
  userdev_free_device_list = devc;
 
1186
 
 
1187
  /*
 
1188
   * Remove the device pair from the active device list.
 
1189
   */
 
1190
 
 
1191
  if (userdev_active_device_list == devc) /* First device in the list */
 
1192
     {
 
1193
             userdev_active_device_list = userdev_active_device_list->next_instance;
 
1194
     }
 
1195
  else
 
1196
     {
 
1197
             userdev_devc_t *this = userdev_active_device_list, *prev = NULL;
 
1198
 
 
1199
             while (this != NULL)
 
1200
             {
 
1201
                     if (this == devc)
 
1202
                        {
 
1203
                                prev->next_instance = this->next_instance; /* Remove */
 
1204
                                break;
 
1205
                        }
 
1206
 
 
1207
                     prev = this;
 
1208
                     this = this->next_instance;
 
1209
             }
 
1210
     }
 
1211
  MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
 
1212
}
 
1213
 
 
1214
void
 
1215
userdev_reinit_instance(userdev_devc_t *devc)
 
1216
{
 
1217
        if (devc->mixer_dev < 0)
 
1218
           return;
 
1219
 
 
1220
  mixer_ext_rebuild_all (devc->mixer_dev, null_mixer_init, USERDEV_MAX_MIXERS*2);
 
1221
}
 
1222
 
 
1223
void
 
1224
userdev_delete_device_pair(userdev_devc_t *devc)
 
1225
{
 
1226
  if (!devc->active)
 
1227
     return;
 
1228
 
 
1229
  devc->active = 0;
 
1230
  MUTEX_CLEANUP(devc->mutex);
 
1231
}
 
1232
 
 
1233
int
 
1234
usrdev_find_free_device_pair(void)
 
1235
{
 
1236
  oss_native_word flags;
 
1237
  userdev_devc_t *devc;
 
1238
 
 
1239
  MUTEX_ENTER_IRQDISABLE(userdev_global_mutex, flags);
 
1240
 
 
1241
  if (userdev_free_device_list != NULL)
 
1242
     {
 
1243
        devc = userdev_free_device_list;
 
1244
        userdev_free_device_list = userdev_free_device_list->next_instance;
 
1245
 
 
1246
        devc->next_instance = userdev_active_device_list;
 
1247
        userdev_active_device_list = devc;
 
1248
 
 
1249
        MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
 
1250
        return devc->server_portc.audio_dev;
 
1251
     }
 
1252
  MUTEX_EXIT_IRQRESTORE(userdev_global_mutex, flags);
 
1253
 
 
1254
  return OSS_ENXIO;
 
1255
}