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