1 /* 2 * Hardware dependent Interface - main file for hardware access 3 * Copyright (c) 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 #include <fcntl.h> 27 #include <sys/ioctl.h> 28 #include "hwdep_local.h" 29 30 #ifndef PIC 31 /* entry for static linking */ 32 const char *_snd_module_hwdep_hw = ""; 33 #endif 34 35 #define SNDRV_FILE_HWDEP ALSA_DEVICE_DIRECTORY "hwC%iD%i" 36 #define SNDRV_HWDEP_VERSION_MAX SNDRV_PROTOCOL_VERSION(1, 0, 1) 37 38 static int snd_hwdep_hw_close(snd_hwdep_t *hwdep) 39 { 40 int res; 41 assert(hwdep); 42 res = close(hwdep->poll_fd) < 0 ? -errno : 0; 43 return res; 44 } 45 46 static int snd_hwdep_hw_nonblock(snd_hwdep_t *hwdep, int nonblock) 47 { 48 long flags; 49 assert(hwdep); 50 if ((flags = fcntl(hwdep->poll_fd, F_GETFL)) < 0) 51 return -errno; 52 if (nonblock) 53 flags |= O_NONBLOCK; 54 else 55 flags &= ~O_NONBLOCK; 56 if (fcntl(hwdep->poll_fd, F_SETFL, flags) < 0) 57 return -errno; 58 return 0; 59 } 60 61 static int snd_hwdep_hw_info(snd_hwdep_t *hwdep, snd_hwdep_info_t *info) 62 { 63 assert(hwdep && info); 64 if (ioctl(hwdep->poll_fd, SNDRV_HWDEP_IOCTL_INFO, info) < 0) 65 return -errno; 66 return 0; 67 } 68 69 static int snd_hwdep_hw_ioctl(snd_hwdep_t *hwdep, unsigned int request, void * arg) 70 { 71 assert(hwdep); 72 if (ioctl(hwdep->poll_fd, request, arg) < 0) 73 return -errno; 74 return 0; 75 } 76 77 static ssize_t snd_hwdep_hw_write(snd_hwdep_t *hwdep, const void *buffer, size_t size) 78 { 79 ssize_t result; 80 assert(hwdep && (buffer || size == 0)); 81 result = write(hwdep->poll_fd, buffer, size); 82 if (result < 0) 83 return -errno; 84 return result; 85 } 86 87 static ssize_t snd_hwdep_hw_read(snd_hwdep_t *hwdep, void *buffer, size_t size) 88 { 89 ssize_t result; 90 assert(hwdep && (buffer || size == 0)); 91 result = read(hwdep->poll_fd, buffer, size); 92 if (result < 0) 93 return -errno; 94 return result; 95 } 96 97 static const snd_hwdep_ops_t snd_hwdep_hw_ops = { 98 .close = snd_hwdep_hw_close, 99 .nonblock = snd_hwdep_hw_nonblock, 100 .info = snd_hwdep_hw_info, 101 .ioctl = snd_hwdep_hw_ioctl, 102 .write = snd_hwdep_hw_write, 103 .read = snd_hwdep_hw_read, 104 }; 105 106 int snd_hwdep_hw_open(snd_hwdep_t **handle, const char *name, int card, int device, int mode) 107 { 108 int fd, ver, ret; 109 char filename[sizeof(SNDRV_FILE_HWDEP) + 20]; 110 snd_hwdep_t *hwdep; 111 assert(handle); 112 113 *handle = NULL; 114 115 if (card < 0 || card >= 32) 116 return -EINVAL; 117 sprintf(filename, SNDRV_FILE_HWDEP, card, device); 118 fd = snd_open_device(filename, mode); 119 if (fd < 0) { 120 snd_card_load(card); 121 fd = snd_open_device(filename, mode); 122 if (fd < 0) 123 return -errno; 124 } 125 #if 0 126 /* 127 * this is bogus, an application have to care about open filedescriptors 128 */ 129 if (fcntl(fd, F_SETFD, FD_CLOEXEC) != 0) { 130 SYSERR("fcntl FD_CLOEXEC failed"); 131 ret = -errno; 132 close(fd); 133 return ret; 134 } 135 #endif 136 if (ioctl(fd, SNDRV_HWDEP_IOCTL_PVERSION, &ver) < 0) { 137 ret = -errno; 138 close(fd); 139 return ret; 140 } 141 if (SNDRV_PROTOCOL_INCOMPATIBLE(ver, SNDRV_HWDEP_VERSION_MAX)) { 142 close(fd); 143 return -SND_ERROR_INCOMPATIBLE_VERSION; 144 } 145 hwdep = (snd_hwdep_t *) calloc(1, sizeof(snd_hwdep_t)); 146 if (hwdep == NULL) { 147 close(fd); 148 return -ENOMEM; 149 } 150 hwdep->name = strdup(name); 151 hwdep->poll_fd = fd; 152 hwdep->mode = mode; 153 hwdep->type = SND_HWDEP_TYPE_HW; 154 hwdep->ops = &snd_hwdep_hw_ops; 155 *handle = hwdep; 156 return 0; 157 } 158 159 int _snd_hwdep_hw_open(snd_hwdep_t **hwdep, char *name, 160 snd_config_t *root ATTRIBUTE_UNUSED, 161 snd_config_t *conf, int mode) 162 { 163 snd_config_iterator_t i, next; 164 long card = -1, device = 0; 165 const char *str; 166 int err; 167 snd_config_for_each(i, next, conf) { 168 snd_config_t *n = snd_config_iterator_entry(i); 169 const char *id; 170 if (snd_config_get_id(n, &id) < 0) 171 continue; 172 if (strcmp(id, "comment") == 0) 173 continue; 174 if (strcmp(id, "type") == 0) 175 continue; 176 if (strcmp(id, "card") == 0) { 177 err = snd_config_get_integer(n, &card); 178 if (err < 0) { 179 err = snd_config_get_string(n, &str); 180 if (err < 0) 181 return -EINVAL; 182 card = snd_card_get_index(str); 183 if (card < 0) 184 return card; 185 } 186 continue; 187 } 188 if (strcmp(id, "device") == 0) { 189 err = snd_config_get_integer(n, &device); 190 if (err < 0) 191 return err; 192 continue; 193 } 194 SNDERR("Unexpected field %s", id); 195 return -EINVAL; 196 } 197 if (card < 0) 198 return -EINVAL; 199 return snd_hwdep_hw_open(hwdep, name, card, device, mode); 200 } 201 SND_DLSYM_BUILD_VERSION(_snd_hwdep_hw_open, SND_HWDEP_DLSYM_VERSION); 202