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