1 /*
2 * Timer Interface - main file
3 * Copyright (c) 1998-2001 by Jaroslav Kysela <perex@perex.cz>
4 *
5 *
6 * This library is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Lesser General Public License as
8 * published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 *
20 */
21
22 #include "timer_local.h"
23
24 #ifndef PIC
25 /* entry for static linking */
26 const char *_snd_module_timer_hw = "";
27 #endif
28
29 #define SNDRV_FILE_TIMER ALSA_DEVICE_DIRECTORY "timer"
30 #define SNDRV_TIMER_VERSION_MAX SNDRV_PROTOCOL_VERSION(2, 0, 5)
31
32 #define SNDRV_TIMER_IOCTL_STATUS_OLD _IOW('T', 0x14, struct snd_timer_status)
33
34 enum {
35 SNDRV_TIMER_IOCTL_START_OLD = _IO('T', 0x20),
36 SNDRV_TIMER_IOCTL_STOP_OLD = _IO('T', 0x21),
37 SNDRV_TIMER_IOCTL_CONTINUE_OLD = _IO('T', 0x22),
38 SNDRV_TIMER_IOCTL_PAUSE_OLD = _IO('T', 0x23),
39 };
40
snd_timer_hw_close(snd_timer_t * handle)41 static int snd_timer_hw_close(snd_timer_t *handle)
42 {
43 snd_timer_t *tmr = handle;
44 int res;
45
46 if (!tmr)
47 return -EINVAL;
48 res = close(tmr->poll_fd) < 0 ? -errno : 0;
49 return res;
50 }
51
snd_timer_hw_nonblock(snd_timer_t * timer,int nonblock)52 static int snd_timer_hw_nonblock(snd_timer_t *timer, int nonblock)
53 {
54 long flags;
55 assert(timer);
56 if ((flags = fcntl(timer->poll_fd, F_GETFL)) < 0)
57 return -errno;
58 if (nonblock)
59 flags |= O_NONBLOCK;
60 else
61 flags &= ~O_NONBLOCK;
62 if (fcntl(timer->poll_fd, F_SETFL, flags) < 0)
63 return -errno;
64 return 0;
65 }
66
snd_timer_hw_async(snd_timer_t * timer,int sig,pid_t pid)67 static int snd_timer_hw_async(snd_timer_t *timer, int sig, pid_t pid)
68 {
69 long flags;
70 int fd;
71
72 assert(timer);
73 fd = timer->poll_fd;
74 if ((flags = fcntl(fd, F_GETFL)) < 0) {
75 SYSERR("F_GETFL failed");
76 return -errno;
77 }
78 if (sig >= 0)
79 flags |= O_ASYNC;
80 else
81 flags &= ~O_ASYNC;
82 if (fcntl(fd, F_SETFL, flags) < 0) {
83 SYSERR("F_SETFL for O_ASYNC failed");
84 return -errno;
85 }
86 if (sig < 0)
87 return 0;
88 #ifdef F_SETSIG
89 if (fcntl(fd, F_SETSIG, (long)sig) < 0) {
90 SYSERR("F_SETSIG failed");
91 return -errno;
92 }
93 #endif
94 if (fcntl(fd, F_SETOWN, (long)pid) < 0) {
95 SYSERR("F_SETOWN failed");
96 return -errno;
97 }
98 return 0;
99 }
100
snd_timer_hw_info(snd_timer_t * handle,snd_timer_info_t * info)101 static int snd_timer_hw_info(snd_timer_t *handle, snd_timer_info_t * info)
102 {
103 snd_timer_t *tmr;
104
105 tmr = handle;
106 if (!tmr || !info)
107 return -EINVAL;
108 if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_INFO, info) < 0)
109 return -errno;
110 return 0;
111 }
112
snd_timer_hw_params(snd_timer_t * handle,snd_timer_params_t * params)113 static int snd_timer_hw_params(snd_timer_t *handle, snd_timer_params_t * params)
114 {
115 snd_timer_t *tmr;
116
117 tmr = handle;
118 if (!tmr || !params)
119 return -EINVAL;
120 if (ioctl(tmr->poll_fd, SNDRV_TIMER_IOCTL_PARAMS, params) < 0)
121 return -errno;
122 return 0;
123 }
124
snd_timer_hw_status(snd_timer_t * handle,snd_timer_status_t * status)125 static int snd_timer_hw_status(snd_timer_t *handle, snd_timer_status_t * status)
126 {
127 snd_timer_t *tmr;
128 int cmd;
129
130 tmr = handle;
131 if (!tmr || !status)
132 return -EINVAL;
133 if (tmr->version < SNDRV_PROTOCOL_VERSION(2, 0, 1))
134 cmd = SNDRV_TIMER_IOCTL_STATUS_OLD;
135 else
136 cmd = SNDRV_TIMER_IOCTL_STATUS;
137 if (ioctl(tmr->poll_fd, cmd, status) < 0)
138 return -errno;
139 return 0;
140 }
141
snd_timer_hw_start(snd_timer_t * handle)142 static int snd_timer_hw_start(snd_timer_t *handle)
143 {
144 snd_timer_t *tmr;
145 unsigned int cmd;
146
147 tmr = handle;
148 if (!tmr)
149 return -EINVAL;
150 if (tmr->version < SNDRV_PROTOCOL_VERSION(2, 0, 4))
151 cmd = SNDRV_TIMER_IOCTL_START_OLD;
152 else
153 cmd = SNDRV_TIMER_IOCTL_START;
154 if (ioctl(tmr->poll_fd, cmd) < 0)
155 return -errno;
156 return 0;
157 }
158
snd_timer_hw_stop(snd_timer_t * handle)159 static int snd_timer_hw_stop(snd_timer_t *handle)
160 {
161 snd_timer_t *tmr;
162 unsigned int cmd;
163
164 tmr = handle;
165 if (!tmr)
166 return -EINVAL;
167 if (tmr->version < SNDRV_PROTOCOL_VERSION(2, 0, 4))
168 cmd = SNDRV_TIMER_IOCTL_STOP_OLD;
169 else
170 cmd = SNDRV_TIMER_IOCTL_STOP;
171 if (ioctl(tmr->poll_fd, cmd) < 0)
172 return -errno;
173 return 0;
174 }
175
snd_timer_hw_continue(snd_timer_t * handle)176 static int snd_timer_hw_continue(snd_timer_t *handle)
177 {
178 snd_timer_t *tmr;
179 unsigned int cmd;
180
181 tmr = handle;
182 if (!tmr)
183 return -EINVAL;
184 if (tmr->version < SNDRV_PROTOCOL_VERSION(2, 0, 4))
185 cmd = SNDRV_TIMER_IOCTL_CONTINUE_OLD;
186 else
187 cmd = SNDRV_TIMER_IOCTL_CONTINUE;
188 if (ioctl(tmr->poll_fd, cmd) < 0)
189 return -errno;
190 return 0;
191 }
192
snd_timer_hw_read(snd_timer_t * handle,void * buffer,size_t size)193 static ssize_t snd_timer_hw_read(snd_timer_t *handle, void *buffer, size_t size)
194 {
195 snd_timer_t *tmr;
196 ssize_t result;
197
198 tmr = handle;
199 if (!tmr || (!buffer && size > 0))
200 return -EINVAL;
201 result = read(tmr->poll_fd, buffer, size);
202 if (result < 0)
203 return -errno;
204 return result;
205 }
206
207 static const snd_timer_ops_t snd_timer_hw_ops = {
208 .close = snd_timer_hw_close,
209 .nonblock = snd_timer_hw_nonblock,
210 .async = snd_timer_hw_async,
211 .info = snd_timer_hw_info,
212 .params = snd_timer_hw_params,
213 .status = snd_timer_hw_status,
214 .rt_start = snd_timer_hw_start,
215 .rt_stop = snd_timer_hw_stop,
216 .rt_continue = snd_timer_hw_continue,
217 .read = snd_timer_hw_read,
218 };
219
snd_timer_hw_open(snd_timer_t ** handle,const char * name,int dev_class,int dev_sclass,int card,int device,int subdevice,int mode)220 int snd_timer_hw_open(snd_timer_t **handle, const char *name, int dev_class, int dev_sclass, int card, int device, int subdevice, int mode)
221 {
222 int fd, ver, tmode, ret;
223 snd_timer_t *tmr;
224 struct snd_timer_select sel;
225
226 *handle = NULL;
227
228 tmode = O_RDONLY;
229 if (mode & SND_TIMER_OPEN_NONBLOCK)
230 tmode |= O_NONBLOCK;
231 fd = snd_open_device(SNDRV_FILE_TIMER, tmode);
232 if (fd < 0)
233 return -errno;
234 if (ioctl(fd, SNDRV_TIMER_IOCTL_PVERSION, &ver) < 0) {
235 ret = -errno;
236 close(fd);
237 return ret;
238 }
239 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_TIMER_VERSION_MAX)) {
240 close(fd);
241 return -SND_ERROR_INCOMPATIBLE_VERSION;
242 }
243 if (mode & SND_TIMER_OPEN_TREAD) {
244 int arg = 1;
245 if (ver < SNDRV_PROTOCOL_VERSION(2, 0, 3)) {
246 ret = -ENOTTY;
247 goto __no_tread;
248 }
249 if (ioctl(fd, SNDRV_TIMER_IOCTL_TREAD, &arg) < 0) {
250 ret = -errno;
251 __no_tread:
252 close(fd);
253 SNDMSG("extended read is not supported (SNDRV_TIMER_IOCTL_TREAD)");
254 return ret;
255 }
256 }
257 memset(&sel, 0, sizeof(sel));
258 sel.id.dev_class = dev_class;
259 sel.id.dev_sclass = dev_sclass;
260 sel.id.card = card;
261 sel.id.device = device;
262 sel.id.subdevice = subdevice;
263 if (ioctl(fd, SNDRV_TIMER_IOCTL_SELECT, &sel) < 0) {
264 ret = -errno;
265 close(fd);
266 return ret;
267 }
268 tmr = (snd_timer_t *) calloc(1, sizeof(snd_timer_t));
269 if (tmr == NULL) {
270 close(fd);
271 return -ENOMEM;
272 }
273 tmr->type = SND_TIMER_TYPE_HW;
274 tmr->version = ver;
275 tmr->mode = tmode;
276 tmr->name = strdup(name);
277 tmr->poll_fd = fd;
278 tmr->ops = &snd_timer_hw_ops;
279 INIT_LIST_HEAD(&tmr->async_handlers);
280 *handle = tmr;
281 return 0;
282 }
283
_snd_timer_hw_open(snd_timer_t ** timer,char * name,snd_config_t * root ATTRIBUTE_UNUSED,snd_config_t * conf,int mode)284 int _snd_timer_hw_open(snd_timer_t **timer, char *name,
285 snd_config_t *root ATTRIBUTE_UNUSED,
286 snd_config_t *conf, int mode)
287 {
288 snd_config_iterator_t i, next;
289 long dev_class = SND_TIMER_CLASS_GLOBAL, dev_sclass = SND_TIMER_SCLASS_NONE;
290 long card = 0, device = 0, subdevice = 0;
291 int err;
292 snd_config_for_each(i, next, conf) {
293 snd_config_t *n = snd_config_iterator_entry(i);
294 const char *id;
295 if (snd_config_get_id(n, &id) < 0)
296 continue;
297 if (_snd_conf_generic_id(id))
298 continue;
299 if (strcmp(id, "class") == 0) {
300 err = snd_config_get_integer(n, &dev_class);
301 if (err < 0)
302 return err;
303 continue;
304 }
305 if (strcmp(id, "sclass") == 0) {
306 err = snd_config_get_integer(n, &dev_sclass);
307 if (err < 0)
308 return err;
309 continue;
310 }
311 if (strcmp(id, "card") == 0) {
312 err = snd_config_get_card(n);
313 if (err < 0)
314 return err;
315 card = err;
316 continue;
317 }
318 if (strcmp(id, "device") == 0) {
319 err = snd_config_get_integer(n, &device);
320 if (err < 0)
321 return err;
322 continue;
323 }
324 if (strcmp(id, "subdevice") == 0) {
325 err = snd_config_get_integer(n, &subdevice);
326 if (err < 0)
327 return err;
328 continue;
329 }
330 SNDERR("Unexpected field %s", id);
331 return -EINVAL;
332 }
333 return snd_timer_hw_open(timer, name, dev_class, dev_sclass, card, device, subdevice, mode);
334 }
335 SND_DLSYM_BUILD_VERSION(_snd_timer_hw_open, SND_TIMER_DLSYM_VERSION);
336