• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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