OSDN Git Service

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