xref: /third_party/alsa-lib/src/timer/timer_hw.c (revision d5ac70f0)
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 */
26const 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
34enum {
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
41static 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
52static 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
67static 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
101static 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
113static 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
125static 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
142static 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
159static 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
176static 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
193static 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
207static 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
220int 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
284int _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}
335SND_DLSYM_BUILD_VERSION(_snd_timer_hw_open, SND_TIMER_DLSYM_VERSION);
336