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