OSDN Git Service

64-bit fixes.
[android-x86/external-alsa-lib.git] / src / pcm / pcm_hw.c
1 /*
2  *  PCM - Hardware
3  *  Copyright (c) 2000 by Abramo Bagnara <abramo@alsa-project.org>
4  *
5  *
6  *   This library is free software; you can redistribute it and/or modify
7  *   it under the terms of the GNU Library General Public License as
8  *   published by the Free Software Foundation; either version 2 of
9  *   the License, or (at your option) any later version.
10  *
11  *   This program is distributed in the hope that it will be useful,
12  *   but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *   GNU Library General Public License for more details.
15  *
16  *   You should have received a copy of the GNU Library General Public
17  *   License along with this library; if not, write to the Free Software
18  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19  *
20  */
21   
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <string.h>
26 #include <errno.h>
27 #include <fcntl.h>
28 #include <sys/ioctl.h>
29 #include <sys/mman.h>
30 #include "pcm_local.h"
31
32 typedef struct {
33         snd_pcm_t *handle;
34         int fd;
35         int card, device, subdevice;
36 } snd_pcm_hw_t;
37
38 #define SND_FILE_PCM_STREAM_PLAYBACK            "/dev/snd/pcmC%iD%ip"
39 #define SND_FILE_PCM_STREAM_CAPTURE             "/dev/snd/pcmC%iD%ic"
40 #define SND_PCM_VERSION_MAX     SND_PROTOCOL_VERSION(2, 0, 0)
41
42 static int snd_pcm_hw_close(void *private)
43 {
44         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
45         int fd = hw->fd;
46         free(private);
47         if (fd >= 0)
48                 if (close(fd))
49                         return -errno;
50         return 0;
51 }
52
53 static int snd_pcm_hw_nonblock(void *private, int nonblock)
54 {
55         long flags;
56         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
57         int fd = hw->fd;
58
59         if ((flags = fcntl(fd, F_GETFL)) < 0)
60                 return -errno;
61         if (nonblock)
62                 flags |= O_NONBLOCK;
63         else
64                 flags &= ~O_NONBLOCK;
65         if (fcntl(fd, F_SETFL, flags) < 0)
66                 return -errno;
67         return 0;
68 }
69
70 static int snd_pcm_hw_info(void *private, snd_pcm_info_t * info)
71 {
72         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
73         int fd = hw->fd;
74         if (ioctl(fd, SND_PCM_IOCTL_INFO, info) < 0)
75                 return -errno;
76         return 0;
77 }
78
79 static int snd_pcm_hw_params_info(void *private, snd_pcm_params_info_t * info)
80 {
81         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
82         int fd = hw->fd;
83         if (ioctl(fd, SND_PCM_IOCTL_PARAMS_INFO, info) < 0)
84                 return -errno;
85         return 0;
86 }
87
88 static int snd_pcm_hw_params(void *private, snd_pcm_params_t * params)
89 {
90         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
91         int fd = hw->fd;
92         if (ioctl(fd, SND_PCM_IOCTL_PARAMS, params) < 0)
93                 return -errno;
94         return 0;
95 }
96
97 static int snd_pcm_hw_setup(void *private, snd_pcm_setup_t * setup)
98 {
99         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
100         int fd = hw->fd;
101         if (ioctl(fd, SND_PCM_IOCTL_SETUP, setup) < 0)
102                 return -errno;
103         return 0;
104 }
105
106 static int snd_pcm_hw_channel_setup(void *private, snd_pcm_channel_setup_t * setup)
107 {
108         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
109         int fd = hw->fd;
110         if (ioctl(fd, SND_PCM_IOCTL_CHANNEL_SETUP, setup) < 0)
111                 return -errno;
112         return 0;
113 }
114
115 static int snd_pcm_hw_status(void *private, snd_pcm_status_t * status)
116 {
117         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
118         int fd = hw->fd;
119         if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0)
120                 return -errno;
121         return 0;
122 }
123
124 static int snd_pcm_hw_state(void *private)
125 {
126         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
127         int fd = hw->fd;
128         snd_pcm_status_t status;
129         if (ioctl(fd, SND_PCM_IOCTL_STATUS, status) < 0)
130                 return -errno;
131         return status.state;
132 }
133
134 static ssize_t snd_pcm_hw_frame_io(void *private, int update UNUSED)
135 {
136         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
137         int fd = hw->fd;
138         ssize_t pos = ioctl(fd, SND_PCM_IOCTL_FRAME_IO);
139         if (pos < 0)
140                 return -errno;
141         return pos;
142 }
143
144 static int snd_pcm_hw_prepare(void *private)
145 {
146         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
147         int fd = hw->fd;
148         if (ioctl(fd, SND_PCM_IOCTL_PREPARE) < 0)
149                 return -errno;
150         return 0;
151 }
152
153 static int snd_pcm_hw_go(void *private)
154 {
155         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
156         int fd = hw->fd;
157         if (ioctl(fd, SND_PCM_IOCTL_GO) < 0)
158                 return -errno;
159         return 0;
160 }
161
162 static int snd_pcm_hw_drain(void *private)
163 {
164         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
165         int fd = hw->fd;
166         if (ioctl(fd, SND_PCM_IOCTL_DRAIN) < 0)
167                 return -errno;
168         return 0;
169 }
170
171 static int snd_pcm_hw_flush(void *private)
172 {
173         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
174         int fd = hw->fd;
175         if (ioctl(fd, SND_PCM_IOCTL_FLUSH) < 0)
176                 return -errno;
177         return 0;
178 }
179
180 static int snd_pcm_hw_pause(void *private, int enable)
181 {
182         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
183         int fd = hw->fd;
184         if (ioctl(fd, SND_PCM_IOCTL_PAUSE, enable) < 0)
185                 return -errno;
186         return 0;
187 }
188
189 static ssize_t snd_pcm_hw_frame_data(void *private, off_t offset)
190 {
191         ssize_t result;
192         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
193         snd_pcm_t *handle = hw->handle;
194         int fd = hw->fd;
195         if (handle->mmap_status && handle->mmap_control)
196                 return snd_pcm_mmap_frame_data(handle, offset);
197         result = ioctl(fd, SND_PCM_IOCTL_FRAME_DATA, offset);
198         if (result < 0)
199                 return -errno;
200         return result;
201 }
202
203 static ssize_t snd_pcm_hw_write(void *private, snd_timestamp_t *tstamp, const void *buffer, size_t size)
204 {
205         ssize_t result;
206         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
207         int fd = hw->fd;
208         snd_xfer_t xfer;
209         if (tstamp)
210                 xfer.tstamp = *tstamp;
211         else
212                 xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0;
213         xfer.buf = (char*) buffer;
214         xfer.count = size;
215         result = ioctl(fd, SND_PCM_IOCTL_WRITE_FRAMES, &xfer);
216         if (result < 0)
217                 return -errno;
218         return result;
219 }
220
221 static ssize_t snd_pcm_hw_writev(void *private, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
222 {
223         ssize_t result;
224         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
225         int fd = hw->fd;
226         snd_xferv_t xferv;
227         if (tstamp)
228                 xferv.tstamp = *tstamp;
229         else
230                 xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0;
231         xferv.vector = vector;
232         xferv.count = count;
233         result = ioctl(fd, SND_PCM_IOCTL_WRITEV_FRAMES, &xferv);
234         if (result < 0)
235                 return -errno;
236         return result;
237 }
238
239 static ssize_t snd_pcm_hw_read(void *private, snd_timestamp_t *tstamp, void *buffer, size_t size)
240 {
241         ssize_t result;
242         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
243         int fd = hw->fd;
244         snd_xfer_t xfer;
245         if (tstamp)
246                 xfer.tstamp = *tstamp;
247         else
248                 xfer.tstamp.tv_sec = xfer.tstamp.tv_usec = 0;
249         xfer.buf = buffer;
250         xfer.count = size;
251         result = ioctl(fd, SND_PCM_IOCTL_READ_FRAMES, &xfer);
252         if (result < 0)
253                 return -errno;
254         return result;
255 }
256
257 ssize_t snd_pcm_hw_readv(void *private, snd_timestamp_t *tstamp, const struct iovec *vector, unsigned long count)
258 {
259         ssize_t result;
260         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
261         int fd = hw->fd;
262         snd_xferv_t xferv;
263         if (tstamp)
264                 xferv.tstamp = *tstamp;
265         else
266                 xferv.tstamp.tv_sec = xferv.tstamp.tv_usec = 0;
267         xferv.vector = vector;
268         xferv.count = count;
269         result = ioctl(fd, SND_PCM_IOCTL_READV_FRAMES, &xferv);
270         if (result < 0)
271                 return -errno;
272         return result;
273 }
274
275 static int snd_pcm_hw_mmap_status(void *private, snd_pcm_mmap_status_t **status)
276 {
277         void *ptr;
278         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
279         ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ, MAP_FILE|MAP_SHARED, 
280                    hw->fd, SND_PCM_MMAP_OFFSET_STATUS);
281         if (ptr == MAP_FAILED || ptr == NULL)
282                 return -errno;
283         *status = ptr;
284         return 0;
285 }
286
287 static int snd_pcm_hw_mmap_control(void *private, snd_pcm_mmap_control_t **control)
288 {
289         void *ptr;
290         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
291         ptr = mmap(NULL, sizeof(snd_pcm_mmap_control_t), PROT_READ|PROT_WRITE, MAP_FILE|MAP_SHARED, 
292                    hw->fd, SND_PCM_MMAP_OFFSET_CONTROL);
293         if (ptr == MAP_FAILED || ptr == NULL)
294                 return -errno;
295         *control = ptr;
296         return 0;
297 }
298
299 static int snd_pcm_hw_mmap_data(void *private, void **buffer, size_t bsize)
300 {
301         int prot;
302         void *daddr;
303         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
304         prot = hw->handle->stream == SND_PCM_STREAM_PLAYBACK ? PROT_WRITE : PROT_READ;
305         daddr = mmap(NULL, bsize, prot, MAP_FILE|MAP_SHARED, 
306                      hw->fd, SND_PCM_MMAP_OFFSET_DATA);
307         if (daddr == MAP_FAILED || daddr == NULL)
308                 return -errno;
309         *buffer = daddr;
310         return 0;
311 }
312
313 static int snd_pcm_hw_munmap_status(void *private UNUSED, snd_pcm_mmap_status_t *status)
314 {
315         if (munmap(status, sizeof(*status)) < 0)
316                 return -errno;
317         return 0;
318 }
319
320 static int snd_pcm_hw_munmap_control(void *private UNUSED, snd_pcm_mmap_control_t *control)
321 {
322         if (munmap(control, sizeof(*control)) < 0)
323                 return -errno;
324         return 0;
325 }
326
327 static int snd_pcm_hw_munmap_data(void *private UNUSED, void *buffer, size_t bsize)
328 {
329         if (munmap(buffer, bsize) < 0)
330                 return -errno;
331         return 0;
332 }
333
334 static int snd_pcm_hw_file_descriptor(void *private)
335 {
336         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
337         return hw->fd;
338 }
339
340 static int snd_pcm_hw_channels_mask(void *private UNUSED,
341                                     bitset_t *client_vmask UNUSED)
342 {
343         return 0;
344 }
345
346 static void snd_pcm_hw_dump(void *private, FILE *fp)
347 {
348         snd_pcm_hw_t *hw = (snd_pcm_hw_t*) private;
349         snd_pcm_t *handle = hw->handle;
350         char *name = "Unknown";
351         snd_card_get_name(hw->card, &name);
352         fprintf(fp, "Hardware PCM card %d '%s' device %d subdevice %d\n",
353                 hw->card, name, hw->device, hw->subdevice);
354         free(name);
355         if (handle->valid_setup) {
356                 fprintf(fp, "\nIts setup is:\n");
357                 snd_pcm_dump_setup(handle, fp);
358         }
359 }
360
361 struct snd_pcm_ops snd_pcm_hw_ops = {
362         close: snd_pcm_hw_close,
363         info: snd_pcm_hw_info,
364         params_info: snd_pcm_hw_params_info,
365         params: snd_pcm_hw_params,
366         setup: snd_pcm_hw_setup,
367         dump: snd_pcm_hw_dump,
368 };
369
370 struct snd_pcm_fast_ops snd_pcm_hw_fast_ops = {
371         nonblock: snd_pcm_hw_nonblock,
372         channel_setup: snd_pcm_hw_channel_setup,
373         status: snd_pcm_hw_status,
374         frame_io: snd_pcm_hw_frame_io,
375         state: snd_pcm_hw_state,
376         prepare: snd_pcm_hw_prepare,
377         go: snd_pcm_hw_go,
378         drain: snd_pcm_hw_drain,
379         flush: snd_pcm_hw_flush,
380         pause: snd_pcm_hw_pause,
381         frame_data: snd_pcm_hw_frame_data,
382         write: snd_pcm_hw_write,
383         writev: snd_pcm_hw_writev,
384         read: snd_pcm_hw_read,
385         readv: snd_pcm_hw_readv,
386         mmap_status: snd_pcm_hw_mmap_status,
387         mmap_control: snd_pcm_hw_mmap_control,
388         mmap_data: snd_pcm_hw_mmap_data,
389         munmap_status: snd_pcm_hw_munmap_status,
390         munmap_control: snd_pcm_hw_munmap_control,
391         munmap_data: snd_pcm_hw_munmap_data,
392         file_descriptor: snd_pcm_hw_file_descriptor,
393         channels_mask: snd_pcm_hw_channels_mask,
394 };
395
396 int snd_pcm_hw_open_subdevice(snd_pcm_t **handlep, int card, int device, int subdevice, int stream, int mode)
397 {
398         char filename[32];
399         char *filefmt;
400         int ver;
401         int ret = 0, fd = -1;
402         int attempt = 0;
403         snd_pcm_info_t info;
404         int fmode;
405         snd_ctl_t *ctl;
406         snd_pcm_t *handle;
407         snd_pcm_hw_t *hw;
408
409         *handlep = 0;
410
411         if ((ret = snd_ctl_open(&ctl, card)) < 0)
412                 return ret;
413
414         switch (stream) {
415         case SND_PCM_STREAM_PLAYBACK:
416                 filefmt = SND_FILE_PCM_STREAM_PLAYBACK;
417                 break;
418         case SND_PCM_STREAM_CAPTURE:
419                 filefmt = SND_FILE_PCM_STREAM_CAPTURE;
420                 break;
421         default:
422                 assert(0);
423         }
424         if ((ret = snd_ctl_pcm_prefer_subdevice(ctl, subdevice)) < 0)
425                 goto __end;
426         sprintf(filename, filefmt, card, device);
427
428       __again:
429         if (attempt++ > 3) {
430                 ret = -EBUSY;
431                 goto __end;
432         }
433         fmode = O_RDWR;
434         if (mode & SND_PCM_NONBLOCK)
435                 fmode |= O_NONBLOCK;
436         if ((fd = open(filename, fmode)) < 0) {
437                 ret = -errno;
438                 goto __end;
439         }
440         if (ioctl(fd, SND_PCM_IOCTL_PVERSION, &ver) < 0) {
441                 ret = -errno;
442                 goto __end;
443         }
444         if (SND_PROTOCOL_INCOMPATIBLE(ver, SND_PCM_VERSION_MAX)) {
445                 ret = -SND_ERROR_INCOMPATIBLE_VERSION;
446                 goto __end;
447         }
448         if (subdevice >= 0) {
449                 memset(&info, 0, sizeof(info));
450                 if (ioctl(fd, SND_PCM_IOCTL_INFO, &info) < 0) {
451                         ret = -errno;
452                         goto __end;
453                 }
454                 if (info.subdevice != subdevice) {
455                         close(fd);
456                         goto __again;
457                 }
458         }
459         handle = calloc(1, sizeof(snd_pcm_t));
460         if (!handle) {
461                 ret = -ENOMEM;
462                 goto __end;
463         }
464         hw = calloc(1, sizeof(snd_pcm_hw_t));
465         if (!handle) {
466                 free(handle);
467                 ret = -ENOMEM;
468                 goto __end;
469         }
470         hw->handle = handle;
471         hw->card = card;
472         hw->device = device;
473         hw->subdevice = subdevice;
474         hw->fd = fd;
475         handle->type = SND_PCM_TYPE_HW;
476         handle->stream = stream;
477         handle->ops = &snd_pcm_hw_ops;
478         handle->op_arg = hw;
479         handle->fast_ops = &snd_pcm_hw_fast_ops;
480         handle->fast_op_arg = hw;
481         handle->mode = mode;
482         handle->private = hw;
483         *handlep = handle;
484         
485  __end:
486         if (ret < 0 && fd >= 0)
487                 close(fd);
488         snd_ctl_close(ctl);
489         return ret;
490 }
491
492 int snd_pcm_hw_open(snd_pcm_t **handlep, int card, int device, int stream, int mode)
493 {
494         return snd_pcm_hw_open_subdevice(handlep, card, device, -1, stream, mode);
495 }
496