1 /* pcm_hw.c
2 **
3 ** Copyright (c) 2019, The Linux Foundation. All rights reserved.
4 **
5 ** Redistribution and use in source and binary forms, with or without
6 ** modification, are permitted provided that the following conditions are
7 ** met:
8 ** * Redistributions of source code must retain the above copyright
9 ** notice, this list of conditions and the following disclaimer.
10 ** * Redistributions in binary form must reproduce the above
11 ** copyright notice, this list of conditions and the following
12 ** disclaimer in the documentation and/or other materials provided
13 ** with the distribution.
14 ** * Neither the name of The Linux Foundation nor the names of its
15 ** contributors may be used to endorse or promote products derived
16 ** from this software without specific prior written permission.
17 **
18 ** THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
19 ** WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
20 ** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
21 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
22 ** BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
23 ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24 ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
25 ** BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26 ** WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27 ** OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
28 ** IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 **/
30
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <fcntl.h>
34 #include <stdarg.h>
35 #include <string.h>
36 #include <errno.h>
37 #include <unistd.h>
38 #include <poll.h>
39
40 #include <sys/ioctl.h>
41 #include <sys/mman.h>
42 #include <linux/ioctl.h>
43 #include <sound/asound.h>
44 #include <tinyalsa/asoundlib.h>
45
46 #include "pcm_io.h"
47
48 struct pcm_hw_data {
49 unsigned int card;
50 unsigned int device;
51 unsigned int fd;
52 void *snd_node;
53 };
54
pcm_hw_close(void * data)55 static void pcm_hw_close(void *data)
56 {
57 struct pcm_hw_data *hw_data = data;
58
59 if (hw_data->fd >= 0)
60 close(hw_data->fd);
61
62 free(hw_data);
63 }
64
pcm_hw_ioctl(void * data,unsigned int cmd,...)65 static int pcm_hw_ioctl(void *data, unsigned int cmd, ...)
66 {
67 struct pcm_hw_data *hw_data = data;
68 va_list ap;
69 void *arg;
70
71 va_start(ap, cmd);
72 arg = va_arg(ap, void *);
73 va_end(ap);
74
75 return ioctl(hw_data->fd, cmd, arg);
76 }
77
pcm_hw_poll(void * data,struct pollfd * pfd,nfds_t nfds,int timeout)78 static int pcm_hw_poll(void *data __attribute__((unused)),
79 struct pollfd *pfd, nfds_t nfds, int timeout)
80 {
81 return poll(pfd, nfds, timeout);
82 }
83
pcm_hw_mmap(void * data,void * addr,size_t length,int prot,int flags,off_t offset)84 static void* pcm_hw_mmap(void *data, void *addr, size_t length, int prot,
85 int flags, off_t offset)
86 {
87 struct pcm_hw_data *hw_data = data;
88
89 return mmap(addr, length, prot, flags, hw_data->fd, offset);
90 }
91
pcm_hw_munmap(void * data,void * addr,size_t length)92 static int pcm_hw_munmap(void *data __attribute__((unused)), void *addr, size_t length)
93 {
94 return munmap(addr, length);
95 }
96
pcm_hw_open(unsigned int card,unsigned int device,unsigned int flags,void ** data,void * node)97 static int pcm_hw_open(unsigned int card, unsigned int device,
98 unsigned int flags, void **data,
99 __attribute__((unused)) void *node)
100 {
101 struct pcm_hw_data *hw_data;
102 char fn[256];
103 int fd;
104
105 hw_data = calloc(1, sizeof(*hw_data));
106 if (!hw_data) {
107 return -ENOMEM;
108 }
109
110 snprintf(fn, sizeof(fn), "/dev/snd/pcmC%uD%u%c", card, device,
111 flags & PCM_IN ? 'c' : 'p');
112 fd = open(fn, O_RDWR|O_NONBLOCK);
113 if (fd < 0) {
114 printf("%s: cannot open device '%s'", __func__, fn);
115 return fd;
116 }
117
118 if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK) < 0) {
119 printf("%s: failed to reset blocking mode '%s'",
120 __func__, fn);
121 goto err_close;
122 }
123
124 hw_data->snd_node = node;
125 hw_data->card = card;
126 hw_data->device = device;
127 hw_data->fd = fd;
128
129 *data = hw_data;
130
131 return fd;
132
133 err_close:
134 close(fd);
135 free(hw_data);
136 return -ENODEV;
137 }
138
139 struct pcm_ops hw_ops = {
140 .open = pcm_hw_open,
141 .close = pcm_hw_close,
142 .ioctl = pcm_hw_ioctl,
143 .mmap = pcm_hw_mmap,
144 .munmap = pcm_hw_munmap,
145 .poll = pcm_hw_poll,
146 };
147