OSDN Git Service

Added ommited break
[android-x86/external-alsa-lib.git] / src / pcm / pcm_hw.c
1 /**
2  * \file pcm/pcm_hw.c
3  * \ingroup PCM_Plugins
4  * \brief PCM HW Plugin Interface
5  * \author Abramo Bagnara <abramo@alsa-project.org>
6  * \author Jaroslav Kysela <perex@suse.cz>
7  * \date 2000-2001
8  */
9 /*
10  *  PCM - Hardware
11  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
12  *
13  *
14  *   This library is free software; you can redistribute it and/or modify
15  *   it under the terms of the GNU Lesser General Public License as
16  *   published by the Free Software Foundation; either version 2.1 of
17  *   the License, or (at your option) any later version.
18  *
19  *   This program is distributed in the hope that it will be useful,
20  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
21  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  *   GNU Lesser General Public License for more details.
23  *
24  *   You should have received a copy of the GNU Lesser General Public
25  *   License along with this library; if not, write to the Free Software
26  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
27  *
28  */
29   
30 #include <stdio.h>
31 #include <stdlib.h>
32 #include <unistd.h>
33 #include <signal.h>
34 #include <string.h>
35 #include <fcntl.h>
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/shm.h>
39 #include "pcm_local.h"
40 #include "../control/control_local.h"
41
42 #ifndef PIC
43 /* entry for static linking */
44 const char *_snd_module_pcm_hw = "";
45 #endif
46
47 #ifndef DOC_HIDDEN
48
49 #ifndef F_SETSIG
50 #define F_SETSIG 10
51 #endif
52
53 #define SND_PCM_IOCTL_XRUN _IO('A', 0x48)
54
55 typedef struct {
56         int version;
57         int fd;
58         int card, device, subdevice;
59         int mmap_emulation;
60         volatile struct sndrv_pcm_mmap_status *mmap_status;
61         struct sndrv_pcm_mmap_control *mmap_control;
62         int shadow_appl_ptr: 1,
63             avail_update_flag: 1,
64             mmap_shm: 1;
65         snd_pcm_uframes_t appl_ptr;
66         int shmid;
67 } snd_pcm_hw_t;
68
69 #define SNDRV_FILE_PCM_STREAM_PLAYBACK          "/dev/snd/pcmC%iD%ip"
70 #define SNDRV_FILE_PCM_STREAM_CAPTURE           "/dev/snd/pcmC%iD%ic"
71 #define SNDRV_PCM_VERSION_MAX                   SNDRV_PROTOCOL_VERSION(2, 0, 1)
72
73 /* update appl_ptr with driver */
74 #define UPDATE_SHADOW_PTR(hw) \
75         do { if (hw->shadow_appl_ptr && !hw->avail_update_flag) \
76                hw->appl_ptr = hw->mmap_control->appl_ptr; } while (0)
77 #define FAST_PCM_STATE(hw) \
78         ((enum sndrv_pcm_state) (hw)->mmap_status->state)
79
80 #endif /* DOC_HIDDEN */
81
82 static int snd_pcm_hw_nonblock(snd_pcm_t *pcm, int nonblock)
83 {
84         long flags;
85         snd_pcm_hw_t *hw = pcm->private_data;
86         int fd = hw->fd;
87
88         if ((flags = fcntl(fd, F_GETFL)) < 0) {
89                 SYSERR("F_GETFL failed");
90                 return -errno;
91         }
92         if (nonblock)
93                 flags |= O_NONBLOCK;
94         else
95                 flags &= ~O_NONBLOCK;
96         if (fcntl(fd, F_SETFL, flags) < 0) {
97                 SYSERR("F_SETFL for O_NONBLOCK failed");
98                 return -errno;
99         }
100         return 0;
101 }
102
103 static int snd_pcm_hw_async(snd_pcm_t *pcm, int sig, pid_t pid)
104 {
105         long flags;
106         snd_pcm_hw_t *hw = pcm->private_data;
107         int fd = hw->fd;
108
109         if ((flags = fcntl(fd, F_GETFL)) < 0) {
110                 SYSERR("F_GETFL failed");
111                 return -errno;
112         }
113         if (sig >= 0)
114                 flags |= O_ASYNC;
115         else
116                 flags &= ~O_ASYNC;
117         if (fcntl(fd, F_SETFL, flags) < 0) {
118                 SYSERR("F_SETFL for O_ASYNC failed");
119                 return -errno;
120         }
121         if (sig < 0)
122                 return 0;
123         if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
124                 SYSERR("F_SETSIG failed");
125                 return -errno;
126         }
127         if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
128                 SYSERR("F_SETOWN failed");
129                 return -errno;
130         }
131         return 0;
132 }
133
134 static int snd_pcm_hw_info(snd_pcm_t *pcm, snd_pcm_info_t * info)
135 {
136         snd_pcm_hw_t *hw = pcm->private_data;
137         int fd = hw->fd;
138         if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, info) < 0) {
139                 SYSERR("SNDRV_PCM_IOCTL_INFO failed");
140                 return -errno;
141         }
142         return 0;
143 }
144
145 static int snd_pcm_hw_hw_refine(snd_pcm_t *pcm, snd_pcm_hw_params_t *params)
146 {
147         snd_pcm_hw_t *hw = pcm->private_data;
148         int fd = hw->fd;
149         if (hw->mmap_emulation) {
150                 int err = 0;
151                 snd_pcm_access_mask_t oldmask = *snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
152                 snd_pcm_access_mask_t mask = { 0 };
153                 const snd_mask_t *pmask;
154
155                 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params) < 0)
156                         err = -errno;
157                 if (err < 0) {
158                         snd_pcm_hw_params_t new = *params;
159
160                         if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) &&
161                             !snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_INTERLEAVED))
162                                 snd_pcm_access_mask_set(&mask, SND_PCM_ACCESS_RW_INTERLEAVED);
163                         if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) &&
164                             !snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
165                                 snd_pcm_access_mask_set(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
166                         if (snd_pcm_access_mask_empty(&mask))
167                                 return err;
168                         pmask = snd_pcm_hw_param_get_mask(&new, SND_PCM_HW_PARAM_ACCESS);
169                         ((snd_mask_t *)pmask)->bits = mask.bits;
170                         if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, &new) < 0)
171                                 return -errno;
172                         *params = new;
173                 }
174                 pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
175                 if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED) ||
176                     snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED) ||
177                     snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_MMAP_COMPLEX))
178                         return 0;
179                 if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_INTERLEAVED)) {
180                         if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_INTERLEAVED))
181                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
182                         snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_INTERLEAVED);
183                 }
184                 if (snd_pcm_access_mask_test(&mask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
185                         if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
186                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
187                         snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
188                 }
189                 if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_INTERLEAVED)) {
190                         if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_INTERLEAVED)) {
191                                 if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_INTERLEAVED))
192                                         snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
193                         } else {
194                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
195                         }
196                 }
197                 if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED)) {
198                         if (snd_pcm_access_mask_test(&oldmask, SND_PCM_ACCESS_RW_NONINTERLEAVED)) {
199                                 if (snd_pcm_access_mask_test(pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED))
200                                         snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
201                         } else {
202                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
203                         }
204                 }
205         } else {
206                 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_REFINE, params) < 0) {
207                         // SYSERR("SNDRV_PCM_IOCTL_HW_REFINE failed");
208                         return -errno;
209                 }
210         }
211         
212         return 0;
213 }
214
215 static int snd_pcm_hw_hw_params(snd_pcm_t *pcm, snd_pcm_hw_params_t * params)
216 {
217         snd_pcm_hw_t *hw = pcm->private_data;
218         int fd = hw->fd;
219         if (hw->mmap_emulation) {
220                 snd_pcm_hw_params_t old = *params;
221                 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0) {
222                         snd_pcm_access_mask_t oldmask;
223                         const snd_mask_t *pmask;
224
225                         *params = old;
226                         pmask = snd_pcm_hw_param_get_mask(params, SND_PCM_HW_PARAM_ACCESS);
227                         oldmask = *(snd_pcm_access_mask_t *)pmask;
228                         switch (snd_pcm_hw_params_get_access(params)) {
229                         case SND_PCM_ACCESS_MMAP_INTERLEAVED:
230                                 snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_INTERLEAVED);
231                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_INTERLEAVED);
232                                 break;
233                         case SND_PCM_ACCESS_MMAP_NONINTERLEAVED:
234                                 snd_pcm_access_mask_reset((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_MMAP_NONINTERLEAVED);
235                                 snd_pcm_access_mask_set((snd_pcm_access_mask_t *)pmask, SND_PCM_ACCESS_RW_NONINTERLEAVED);
236                                 break;
237                         default:
238                                 goto _err;
239                         }
240                         if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0)
241                                 goto _err;
242                         hw->mmap_shm = 1;
243                         *(snd_pcm_access_mask_t *)pmask = oldmask;
244                 }
245         } else {
246                 if (ioctl(fd, SNDRV_PCM_IOCTL_HW_PARAMS, params) < 0) {
247                       _err:
248                         SYSERR("SNDRV_PCM_IOCTL_HW_PARAMS failed");
249                         return -errno;
250                 }
251         }
252         if (pcm->stream == SND_PCM_STREAM_CAPTURE) {
253                 if (hw->mmap_shm) {
254                         hw->shadow_appl_ptr = 1;
255                         hw->appl_ptr = 0;
256                         pcm->appl_ptr = &hw->appl_ptr;
257                 } else {
258                         hw->shadow_appl_ptr = 0;
259                         pcm->appl_ptr = &hw->mmap_control->appl_ptr;
260                 }
261         }
262         return 0;
263 }
264
265 static int snd_pcm_hw_hw_free(snd_pcm_t *pcm)
266 {
267         snd_pcm_hw_t *hw = pcm->private_data;
268         int fd = hw->fd;
269         if (ioctl(fd, SNDRV_PCM_IOCTL_HW_FREE) < 0) {
270                 SYSERR("SNDRV_PCM_IOCTL_HW_FREE failed");
271                 return -errno;
272         }
273         return 0;
274 }
275
276 static int snd_pcm_hw_sw_params(snd_pcm_t *pcm, snd_pcm_sw_params_t * params)
277 {
278         snd_pcm_hw_t *hw = pcm->private_data;
279         int fd = hw->fd;
280         if ((snd_pcm_tstamp_t) params->tstamp_mode == pcm->tstamp_mode &&
281             params->period_step == pcm->period_step &&
282             params->sleep_min == pcm->sleep_min &&
283             params->xfer_align == pcm->xfer_align &&
284             params->start_threshold == pcm->start_threshold &&
285             params->stop_threshold == pcm->stop_threshold &&
286             params->silence_threshold == pcm->silence_threshold &&
287             params->silence_size == pcm->silence_size) {
288                 hw->mmap_control->avail_min = params->avail_min;
289                 return 0;
290         }
291         if (ioctl(fd, SNDRV_PCM_IOCTL_SW_PARAMS, params) < 0) {
292                 SYSERR("SNDRV_PCM_IOCTL_SW_PARAMS failed");
293                 return -errno;
294         }
295         return 0;
296 }
297
298 static int snd_pcm_hw_channel_info(snd_pcm_t *pcm, snd_pcm_channel_info_t * info)
299 {
300         snd_pcm_hw_t *hw = pcm->private_data;
301         struct sndrv_pcm_channel_info i;
302         int fd = hw->fd;
303         i.channel = info->channel;
304         if (ioctl(fd, SNDRV_PCM_IOCTL_CHANNEL_INFO, &i) < 0) {
305                 SYSERR("SNDRV_PCM_IOCTL_CHANNEL_INFO failed");
306                 return -errno;
307         }
308         info->channel = i.channel;
309         if (!hw->mmap_shm) {
310                 info->addr = 0;
311                 info->first = i.first;
312                 info->step = i.step;
313                 info->type = SND_PCM_AREA_MMAP;
314                 info->u.mmap.fd = fd;
315                 info->u.mmap.offset = i.offset;
316                 return 0;
317         }
318         return snd_pcm_channel_info_shm(pcm, info, hw->shmid);
319 }
320
321 static int snd_pcm_hw_status(snd_pcm_t *pcm, snd_pcm_status_t * status)
322 {
323         snd_pcm_hw_t *hw = pcm->private_data;
324         int fd = hw->fd;
325         if (ioctl(fd, SNDRV_PCM_IOCTL_STATUS, status) < 0) {
326                 SYSERR("SNDRV_PCM_IOCTL_STATUS failed");
327                 return -errno;
328         }
329         return 0;
330 }
331
332 static snd_pcm_state_t snd_pcm_hw_state(snd_pcm_t *pcm)
333 {
334         snd_pcm_hw_t *hw = pcm->private_data;
335         return (snd_pcm_state_t) hw->mmap_status->state;
336 }
337
338 static int snd_pcm_hw_delay(snd_pcm_t *pcm, snd_pcm_sframes_t *delayp)
339 {
340         snd_pcm_hw_t *hw = pcm->private_data;
341         int fd = hw->fd;
342         if (ioctl(fd, SNDRV_PCM_IOCTL_DELAY, delayp) < 0) {
343                 // SYSERR("SNDRV_PCM_IOCTL_DELAY failed");
344                 return -errno;
345         }
346         return 0;
347 }
348
349 static int snd_pcm_hw_prepare(snd_pcm_t *pcm)
350 {
351         snd_pcm_hw_t *hw = pcm->private_data;
352         int fd = hw->fd;
353         if (ioctl(fd, SNDRV_PCM_IOCTL_PREPARE) < 0) {
354                 SYSERR("SNDRV_PCM_IOCTL_PREPARE failed");
355                 return -errno;
356         }
357         return 0;
358 }
359
360 static int snd_pcm_hw_reset(snd_pcm_t *pcm)
361 {
362         snd_pcm_hw_t *hw = pcm->private_data;
363         int fd = hw->fd;
364         if (ioctl(fd, SNDRV_PCM_IOCTL_RESET) < 0) {
365                 SYSERR("SNDRV_PCM_IOCTL_RESET failed");
366                 return -errno;
367         }
368         return 0;
369 }
370
371 static int snd_pcm_hw_start(snd_pcm_t *pcm)
372 {
373         snd_pcm_hw_t *hw = pcm->private_data;
374         int fd = hw->fd;
375 #if 0
376         assert(pcm->stream != SND_PCM_STREAM_PLAYBACK ||
377                snd_pcm_mmap_playback_hw_avail(pcm) > 0);
378 #endif
379         if (ioctl(fd, SNDRV_PCM_IOCTL_START) < 0) {
380                 SYSERR("SNDRV_PCM_IOCTL_START failed");
381 #if 0
382                 if (errno == EBADFD)
383                         SNDERR("PCM state = %s", snd_pcm_state_name(snd_pcm_hw_state(pcm)));
384 #endif
385                 return -errno;
386         }
387         return 0;
388 }
389
390 static int snd_pcm_hw_drop(snd_pcm_t *pcm)
391 {
392         snd_pcm_hw_t *hw = pcm->private_data;
393         int fd = hw->fd;
394         if (ioctl(fd, SNDRV_PCM_IOCTL_DROP) < 0) {
395                 SYSERR("SNDRV_PCM_IOCTL_DROP failed");
396                 return -errno;
397         }
398         return 0;
399 }
400
401 static int snd_pcm_hw_drain(snd_pcm_t *pcm)
402 {
403         snd_pcm_hw_t *hw = pcm->private_data;
404         int fd = hw->fd;
405         if (ioctl(fd, SNDRV_PCM_IOCTL_DRAIN) < 0) {
406                 if (errno != EAGAIN)
407                         SYSERR("SNDRV_PCM_IOCTL_DRAIN failed");
408                 return -errno;
409         }
410         return 0;
411 }
412
413 static int snd_pcm_hw_pause(snd_pcm_t *pcm, int enable)
414 {
415         snd_pcm_hw_t *hw = pcm->private_data;
416         int fd = hw->fd;
417         if (ioctl(fd, SNDRV_PCM_IOCTL_PAUSE, enable) < 0) {
418                 SYSERR("SNDRV_PCM_IOCTL_PAUSE failed");
419                 return -errno;
420         }
421         return 0;
422 }
423
424 static snd_pcm_sframes_t snd_pcm_hw_rewind(snd_pcm_t *pcm, snd_pcm_uframes_t frames)
425 {
426         snd_pcm_hw_t *hw = pcm->private_data;
427         int fd = hw->fd;
428         if (ioctl(fd, SNDRV_PCM_IOCTL_REWIND, &frames) < 0) {
429                 SYSERR("SNDRV_PCM_IOCTL_REWIND failed");
430                 return -errno;
431         }
432         return frames;
433 }
434
435 static int snd_pcm_hw_resume(snd_pcm_t *pcm)
436 {
437         snd_pcm_hw_t *hw = pcm->private_data;
438         int fd = hw->fd;
439         if (ioctl(fd, SNDRV_PCM_IOCTL_RESUME) < 0) {
440                 if (errno != ENXIO && errno != ENOSYS)
441                         SYSERR("SNDRV_PCM_IOCTL_RESUME failed");
442                 return -errno;
443         }
444         return 0;
445 }
446
447 static snd_pcm_sframes_t snd_pcm_hw_writei(snd_pcm_t *pcm, const void *buffer, snd_pcm_uframes_t size)
448 {
449         snd_pcm_sframes_t result;
450         snd_pcm_hw_t *hw = pcm->private_data;
451         int fd = hw->fd;
452         struct sndrv_xferi xferi;
453         xferi.buf = (char*) buffer;
454         xferi.frames = size;
455         result = ioctl(fd, SNDRV_PCM_IOCTL_WRITEI_FRAMES, &xferi);
456         if (result < 0)
457                 return -errno;
458         return xferi.result;
459 }
460
461 static snd_pcm_sframes_t snd_pcm_hw_writen(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
462 {
463         snd_pcm_sframes_t result;
464         snd_pcm_hw_t *hw = pcm->private_data;
465         int fd = hw->fd;
466         struct sndrv_xfern xfern;
467         xfern.bufs = bufs;
468         xfern.frames = size;
469         result = ioctl(fd, SNDRV_PCM_IOCTL_WRITEN_FRAMES, &xfern);
470         if (result < 0)
471                 return -errno;
472         return xfern.result;
473 }
474
475 static snd_pcm_sframes_t snd_pcm_hw_readi(snd_pcm_t *pcm, void *buffer, snd_pcm_uframes_t size)
476 {
477         snd_pcm_sframes_t result;
478         snd_pcm_hw_t *hw = pcm->private_data;
479         int fd = hw->fd;
480         struct sndrv_xferi xferi;
481         xferi.buf = buffer;
482         xferi.frames = size;
483         result = ioctl(fd, SNDRV_PCM_IOCTL_READI_FRAMES, &xferi);
484         if (result < 0)
485                 return -errno;
486         UPDATE_SHADOW_PTR(hw);
487         return xferi.result;
488 }
489
490 static snd_pcm_sframes_t snd_pcm_hw_readn(snd_pcm_t *pcm, void **bufs, snd_pcm_uframes_t size)
491 {
492         snd_pcm_sframes_t result;
493         snd_pcm_hw_t *hw = pcm->private_data;
494         int fd = hw->fd;
495         struct sndrv_xfern xfern;
496         xfern.bufs = bufs;
497         xfern.frames = size;
498         result = ioctl(fd, SNDRV_PCM_IOCTL_READN_FRAMES, &xfern);
499         if (result < 0)
500                 return -errno;
501         UPDATE_SHADOW_PTR(hw);
502         return xfern.result;
503 }
504
505 static int snd_pcm_hw_mmap_status(snd_pcm_t *pcm)
506 {
507         snd_pcm_hw_t *hw = pcm->private_data;
508         void *ptr;
509         ptr = mmap(NULL, page_align(sizeof(struct sndrv_pcm_mmap_status)),
510                    PROT_READ, MAP_FILE|MAP_SHARED, 
511                    hw->fd, SNDRV_PCM_MMAP_OFFSET_STATUS);
512         if (ptr == MAP_FAILED || ptr == NULL) {
513                 SYSERR("status mmap failed");
514                 return -errno;
515         }
516         hw->mmap_status = ptr;
517         pcm->hw_ptr = &hw->mmap_status->hw_ptr;
518         return 0;
519 }
520
521 static int snd_pcm_hw_mmap_control(snd_pcm_t *pcm)
522 {
523         snd_pcm_hw_t *hw = pcm->private_data;
524         void *ptr;
525         ptr = mmap(NULL, page_align(sizeof(struct sndrv_pcm_mmap_control)),
526                    PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, 
527                    hw->fd, SNDRV_PCM_MMAP_OFFSET_CONTROL);
528         if (ptr == MAP_FAILED || ptr == NULL) {
529                 SYSERR("control mmap failed");
530                 return -errno;
531         }
532         hw->mmap_control = ptr;
533         pcm->appl_ptr = &hw->mmap_control->appl_ptr;
534         return 0;
535 }
536
537 static int snd_pcm_hw_munmap_status(snd_pcm_t *pcm)
538 {
539         snd_pcm_hw_t *hw = pcm->private_data;
540         if (munmap((void*)hw->mmap_status, page_align(sizeof(*hw->mmap_status))) < 0) {
541                 SYSERR("status munmap failed");
542                 return -errno;
543         }
544         return 0;
545 }
546
547 static int snd_pcm_hw_munmap_control(snd_pcm_t *pcm)
548 {
549         snd_pcm_hw_t *hw = pcm->private_data;
550         if (munmap(hw->mmap_control, page_align(sizeof(*hw->mmap_control))) < 0) {
551                 SYSERR("control munmap failed");
552                 return -errno;
553         }
554         return 0;
555 }
556
557 static int snd_pcm_hw_mmap(snd_pcm_t *pcm)
558 {
559         snd_pcm_hw_t *hw = pcm->private_data;
560         if (hw->mmap_shm) {
561                 snd_pcm_uframes_t size = snd_pcm_frames_to_bytes(pcm, (snd_pcm_sframes_t) pcm->buffer_size);
562                 int id = shmget(IPC_PRIVATE, size, 0666);
563                 hw->mmap_shm = 1;
564                 if (id < 0) {
565                         SYSERR("shmget failed");
566                         return -errno;
567                 }
568                 hw->shmid = id;
569         }
570         return 0;
571 }
572
573 static int snd_pcm_hw_munmap(snd_pcm_t *pcm)
574 {
575         snd_pcm_hw_t *hw = pcm->private_data;
576         if (hw->mmap_shm) {
577                 if (shmctl(hw->shmid, IPC_RMID, 0) < 0) {
578                         SYSERR("shmctl IPC_RMID failed");
579                         return -errno;
580                 }
581         }
582         return 0;
583 }
584
585 static int snd_pcm_hw_close(snd_pcm_t *pcm)
586 {
587         snd_pcm_hw_t *hw = pcm->private_data;
588         if (close(hw->fd)) {
589                 SYSERR("close failed\n");
590                 return -errno;
591         }
592         snd_pcm_hw_munmap_status(pcm);
593         snd_pcm_hw_munmap_control(pcm);
594         free(hw);
595         return 0;
596 }
597
598 static snd_pcm_sframes_t snd_pcm_hw_mmap_commit(snd_pcm_t *pcm,
599                                                 snd_pcm_uframes_t offset ATTRIBUTE_UNUSED,
600                                                 snd_pcm_uframes_t size)
601 {
602         snd_pcm_hw_t *hw = pcm->private_data;
603
604         if (hw->mmap_shm) {
605                 if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
606                         snd_pcm_sframes_t result = 0, res;
607
608                         do {
609                                 res = snd_pcm_write_mmap(pcm, size);
610                                 if (res < 0)
611                                         return result > 0 ? result : res;
612                                 size -= res;
613                                 result += res;
614                         } while (size > 0);
615                         return result;
616                 } else {
617                         snd_pcm_hw_t *hw = pcm->private_data;
618                         assert(hw->shadow_appl_ptr);
619                 }
620         }
621         snd_pcm_mmap_appl_forward(pcm, size);
622         return size;
623 }
624
625 static snd_pcm_sframes_t snd_pcm_hw_avail_update(snd_pcm_t *pcm)
626 {
627         snd_pcm_hw_t *hw = pcm->private_data;
628         snd_pcm_uframes_t avail;
629         if (pcm->stream == SND_PCM_STREAM_PLAYBACK) {
630                 avail = snd_pcm_mmap_playback_avail(pcm);
631         } else {
632                 avail = snd_pcm_mmap_capture_avail(pcm);
633                 if (avail > 0 && hw->mmap_shm) {
634                         snd_pcm_sframes_t err;
635                         snd_pcm_hw_t *hw = pcm->private_data;
636                         hw->avail_update_flag = 1;
637                         err = snd_pcm_read_mmap(pcm, avail);
638                         hw->avail_update_flag = 0;
639                         if (err < 0)
640                                 return err;
641                         assert((snd_pcm_uframes_t)err == avail);
642                         return err;
643                 }
644         }
645         switch (FAST_PCM_STATE(hw)) {
646         case SNDRV_PCM_STATE_RUNNING:
647                 if (avail >= pcm->stop_threshold) {
648                         /* SNDRV_PCM_IOCTL_XRUN ioctl has been implemented since PCM kernel API 2.0.1 */
649                         if (SNDRV_PROTOCOL_VERSION(2, 0, 1) <= hw->version) {
650                                 if (ioctl(hw->fd, SND_PCM_IOCTL_XRUN) < 0)
651                                         return -errno;
652                         }
653                         /* everything is ok, state == SND_PCM_STATE_XRUN at the moment */
654                         return -EPIPE;
655                 }
656                 break;
657         case SNDRV_PCM_STATE_XRUN:
658                 return -EPIPE;
659         default:
660                 break;
661         }
662         return avail;
663 }
664
665 static void snd_pcm_hw_dump(snd_pcm_t *pcm, snd_output_t *out)
666 {
667         snd_pcm_hw_t *hw = pcm->private_data;
668         char *name;
669         int err = snd_card_get_name(hw->card, &name);
670         assert(err >= 0);
671         snd_output_printf(out, "Hardware PCM card %d '%s' device %d subdevice %d\n",
672                           hw->card, name, hw->device, hw->subdevice);
673         free(name);
674         if (pcm->setup) {
675                 snd_output_printf(out, "\nIts setup is:\n");
676                 snd_pcm_dump_setup(pcm, out);
677         }
678 }
679
680 static snd_pcm_ops_t snd_pcm_hw_ops = {
681         close: snd_pcm_hw_close,
682         info: snd_pcm_hw_info,
683         hw_refine: snd_pcm_hw_hw_refine,
684         hw_params: snd_pcm_hw_hw_params,
685         hw_free: snd_pcm_hw_hw_free,
686         sw_params: snd_pcm_hw_sw_params,
687         channel_info: snd_pcm_hw_channel_info,
688         dump: snd_pcm_hw_dump,
689         nonblock: snd_pcm_hw_nonblock,
690         async: snd_pcm_hw_async,
691         mmap: snd_pcm_hw_mmap,
692         munmap: snd_pcm_hw_munmap,
693 };
694
695 static snd_pcm_fast_ops_t snd_pcm_hw_fast_ops = {
696         status: snd_pcm_hw_status,
697         state: snd_pcm_hw_state,
698         delay: snd_pcm_hw_delay,
699         prepare: snd_pcm_hw_prepare,
700         reset: snd_pcm_hw_reset,
701         start: snd_pcm_hw_start,
702         drop: snd_pcm_hw_drop,
703         drain: snd_pcm_hw_drain,
704         pause: snd_pcm_hw_pause,
705         rewind: snd_pcm_hw_rewind,
706         resume: snd_pcm_hw_resume,
707         writei: snd_pcm_hw_writei,
708         writen: snd_pcm_hw_writen,
709         readi: snd_pcm_hw_readi,
710         readn: snd_pcm_hw_readn,
711         avail_update: snd_pcm_hw_avail_update,
712         mmap_commit: snd_pcm_hw_mmap_commit,
713 };
714
715 /**
716  * \brief Creates a new hw PCM
717  * \param pcmp Returns created PCM handle
718  * \param name Name of PCM
719  * \param card Number of card
720  * \param device Number of device
721  * \param subdevice Number of subdevice
722  * \param stream PCM Stream
723  * \param mode PCM Mode
724  * \retval zero on success otherwise a negative error code
725  * \warning Using of this function might be dangerous in the sense
726  *          of compatibility reasons. The prototype might be freely
727  *          changed in future.
728  */
729 int snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
730                     int card, int device, int subdevice,
731                     snd_pcm_stream_t stream, int mode,
732                     int mmap_emulation)
733 {
734         char filename[32];
735         const char *filefmt;
736         int ver;
737         int err, ret = 0, fd = -1;
738         int attempt = 0;
739         snd_pcm_info_t info;
740         int fmode;
741         snd_ctl_t *ctl;
742         snd_pcm_t *pcm = NULL;
743         snd_pcm_hw_t *hw = NULL;
744
745         assert(pcmp);
746
747         if ((ret = snd_ctl_hw_open(&ctl, NULL, card, 0)) < 0)
748                 return ret;
749
750         switch (stream) {
751         case SND_PCM_STREAM_PLAYBACK:
752                 filefmt = SNDRV_FILE_PCM_STREAM_PLAYBACK;
753                 break;
754         case SND_PCM_STREAM_CAPTURE:
755                 filefmt = SNDRV_FILE_PCM_STREAM_CAPTURE;
756                 break;
757         default:
758                 assert(0);
759         }
760         sprintf(filename, filefmt, card, device);
761
762       __again:
763         if (attempt++ > 3) {
764                 ret = -EBUSY;
765                 goto _err;
766         }
767         ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice);
768         if (ret < 0)
769                 goto _err;
770         fmode = O_RDWR;
771         if (mode & SND_PCM_NONBLOCK)
772                 fmode |= O_NONBLOCK;
773         if (mode & SND_PCM_ASYNC)
774                 fmode |= O_ASYNC;
775         if ((fd = open(filename, fmode)) < 0) {
776                 SYSERR("open %s failed", filename);
777                 ret = -errno;
778                 goto _err;
779         }
780         if (ioctl(fd, SNDRV_PCM_IOCTL_PVERSION, &ver) < 0) {
781                 SYSERR("SNDRV_PCM_IOCTL_PVERSION failed");
782                 ret = -errno;
783                 goto _err;
784         }
785         if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_PCM_VERSION_MAX)) {
786                 ret = -SND_ERROR_INCOMPATIBLE_VERSION;
787                 goto _err;
788         }
789         if (subdevice >= 0) {
790                 memset(&info, 0, sizeof(info));
791                 if (ioctl(fd, SNDRV_PCM_IOCTL_INFO, &info) < 0) {
792                         SYSERR("SNDRV_PCM_IOCTL_INFO failed");
793                         ret = -errno;
794                         goto _err;
795                 }
796                 if (info.subdevice != (unsigned int) subdevice) {
797                         close(fd);
798                         goto __again;
799                 }
800         }
801         hw = calloc(1, sizeof(snd_pcm_hw_t));
802         if (!hw) {
803                 ret = -ENOMEM;
804                 goto _err;
805         }
806         hw->version = ver;
807         hw->card = card;
808         hw->device = device;
809         hw->subdevice = subdevice;
810         hw->fd = fd;
811         hw->mmap_emulation = mmap_emulation;
812
813         err = snd_pcm_new(&pcm, SND_PCM_TYPE_HW, name, stream, mode);
814         if (err < 0) {
815                 ret = err;
816                 goto _err;
817         }
818         snd_ctl_close(ctl);
819         pcm->ops = &snd_pcm_hw_ops;
820         pcm->fast_ops = &snd_pcm_hw_fast_ops;
821         pcm->private_data = hw;
822         pcm->poll_fd = fd;
823         *pcmp = pcm;
824         ret = snd_pcm_hw_mmap_status(pcm);
825         if (ret < 0) {
826                 snd_pcm_close(pcm);
827                 return ret;
828         }
829         ret = snd_pcm_hw_mmap_control(pcm);
830         if (ret < 0) {
831                 snd_pcm_close(pcm);
832                 return ret;
833         }
834         return 0;
835         
836  _err:
837         if (hw)
838                 free(hw);
839         if (pcm)
840                 free(pcm);
841         if (fd >= 0)
842                 close(fd);
843         snd_ctl_close(ctl);
844         return ret;
845 }
846
847 /*! \page pcm_plugins
848
849 \section pcm_plugins_hw Plugin: hw
850
851 This plugin communicates directly with the ALSA kernel driver. It is a raw
852 communication without any conversions. The emulation of mmap access can be
853 optionally enabled, but expect worse latency in the case.
854
855 \code
856 pcm.name {
857         type hw                 # Kernel PCM
858         card INT/STR            # Card name (string) or number (integer)
859         [device INT]            # Device number (default 0)
860         [subdevice INT]         # Subdevice number (default -1: first available)
861         [mmap_emulation BOOL]   # Enable mmap emulation for ro/wo devices
862 }
863 \endcode
864
865 \subsection pcm_plugins_hw_funcref Function reference
866
867 <UL>
868   <LI>snd_pcm_hw_open()
869   <LI>_snd_pcm_hw_open()
870 </UL>
871
872 */
873
874 /**
875  * \brief Creates a new hw PCM
876  * \param pcmp Returns created PCM handle
877  * \param name Name of PCM
878  * \param root Root configuration node
879  * \param conf Configuration node with hw PCM description
880  * \param stream PCM Stream
881  * \param mode PCM Mode
882  * \warning Using of this function might be dangerous in the sense
883  *          of compatibility reasons. The prototype might be freely
884  *          changed in future.
885  */
886 int _snd_pcm_hw_open(snd_pcm_t **pcmp, const char *name,
887                      snd_config_t *root ATTRIBUTE_UNUSED, snd_config_t *conf,
888                      snd_pcm_stream_t stream, int mode)
889 {
890         snd_config_iterator_t i, next;
891         long card = -1, device = 0, subdevice = -1;
892         const char *str;
893         int err, mmap_emulation = 0;
894         snd_config_for_each(i, next, conf) {
895                 snd_config_t *n = snd_config_iterator_entry(i);
896                 const char *id;
897                 if (snd_config_get_id(n, &id) < 0)
898                         continue;
899                 if (snd_pcm_conf_generic_id(id))
900                         continue;
901                 if (strcmp(id, "card") == 0) {
902                         err = snd_config_get_integer(n, &card);
903                         if (err < 0) {
904                                 err = snd_config_get_string(n, &str);
905                                 if (err < 0) {
906                                         SNDERR("Invalid type for %s", id);
907                                         return -EINVAL;
908                                 }
909                                 card = snd_card_get_index(str);
910                                 if (card < 0) {
911                                         SNDERR("Invalid value for %s", id);
912                                         return card;
913                                 }
914                         }
915                         continue;
916                 }
917                 if (strcmp(id, "device") == 0) {
918                         err = snd_config_get_integer(n, &device);
919                         if (err < 0) {
920                                 SNDERR("Invalid type for %s", id);
921                                 return err;
922                         }
923                         continue;
924                 }
925                 if (strcmp(id, "subdevice") == 0) {
926                         err = snd_config_get_integer(n, &subdevice);
927                         if (err < 0) {
928                                 SNDERR("Invalid type for %s", id);
929                                 return err;
930                         }
931                         continue;
932                 }
933                 if (strcmp(id, "mmap_emulation") == 0) {
934                         err = snd_config_get_bool(n);
935                         if (err < 0)
936                                 continue;
937                         mmap_emulation = err;
938                         continue;
939                 }
940                 SNDERR("Unknown field %s", id);
941                 return -EINVAL;
942         }
943         if (card < 0) {
944                 SNDERR("card is not defined");
945                 return -EINVAL;
946         }
947         return snd_pcm_hw_open(pcmp, name, card, device, subdevice, stream, mode, mmap_emulation);
948 }
949 #ifndef DOC_HIDDEN
950 SND_DLSYM_BUILD_VERSION(_snd_pcm_hw_open, SND_PCM_DLSYM_VERSION);
951 #endif