1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <regex.h> 26 #include <devfsadm.h> 27 #include <strings.h> 28 #include <ctype.h> 29 #include <stdlib.h> 30 #include <limits.h> 31 #include <stdio.h> 32 #include <syslog.h> 33 #include <bsm/devalloc.h> 34 #include <sys/audio.h> 35 #include <sys/soundcard.h> 36 #include <unistd.h> 37 38 #define MAX_AUDIO_LINK 100 39 #define RE_SIZE 64 40 41 extern int system_labeled; 42 43 static void check_audio_link(di_node_t anynode, char *secondary_link, 44 const char *primary_link_format); 45 46 static int audio_process(di_minor_t minor, di_node_t node); 47 static int sndstat_process(di_minor_t minor, di_node_t node); 48 49 static devfsadm_create_t audio_cbt[] = { 50 { "audio", "ddi_audio", NULL, 51 TYPE_EXACT, ILEVEL_0, audio_process 52 }, 53 { "pseudo", "ddi_pseudo", "audio", 54 TYPE_EXACT|DRV_EXACT, ILEVEL_0, sndstat_process 55 }, 56 }; 57 58 DEVFSADM_CREATE_INIT_V0(audio_cbt); 59 60 /* 61 * the following can't be one big RE with a bunch of alterations "|" 62 * because recurse_dev_re() would not work. 63 */ 64 static devfsadm_remove_t audio_remove_cbt[] = { 65 /* 66 * Secondary links. 67 */ 68 69 /* /dev/audio, /dev/audioctl, /dev/dsp */ 70 { "audio", "^audio$", 71 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 72 }, 73 { "audio", "^audioctl$", 74 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 75 }, 76 { "audio", "^dsp$", 77 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 78 }, 79 { "audio", "^mixer", 80 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 81 }, 82 { "audio", "^sndstat$", 83 RM_PRE|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 84 }, 85 { "audio", "^mixer[0-9]+$", 86 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 87 }, 88 { "audio", "^dsp[0-9]+$", 89 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 90 }, 91 { "audio", "^sound/[0-9]+$", 92 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 93 }, 94 { "audio", "^sound/[0-9]+ctl$", 95 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 96 }, 97 }; 98 99 DEVFSADM_REMOVE_INIT_V0(audio_remove_cbt); 100 101 static di_node_t anynode; 102 103 int 104 minor_init(void) 105 { 106 anynode = DI_NODE_NIL; 107 return (DEVFSADM_SUCCESS); 108 } 109 110 int 111 minor_fini(void) 112 { 113 check_audio_link(anynode, "audio", "sound/%d"); 114 check_audio_link(anynode, "audioctl", "sound/%dctl"); 115 check_audio_link(anynode, "dsp", "dsp%d"); 116 anynode = DI_NODE_NIL; 117 return (DEVFSADM_SUCCESS); 118 } 119 120 121 #define COPYSUB(to, from, pm, pos) (void) strncpy(to, &from[pm[pos].rm_so], \ 122 pm[pos].rm_eo - pm[pos].rm_so); \ 123 to[pm[pos].rm_eo - pm[pos].rm_so] = 0; 124 125 static void 126 send_number(long num) 127 { 128 char buf[PATH_MAX+1]; 129 130 /* 131 * This is not safe with -r. 132 */ 133 if (strcmp(devfsadm_root_path(), "/") != 0) 134 return; 135 136 (void) snprintf(buf, sizeof (buf), "/dev/mixer%ld", num); 137 if (device_exists(buf)) { 138 int fd; 139 140 if ((fd = open(buf, O_RDWR)) < 0) 141 return; 142 143 (void) ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num); 144 (void) close(fd); 145 devfsadm_print(CHATTY_MID, 146 "sent devnum audio %ld to %s\n", num, buf); 147 } 148 } 149 150 static int 151 sndstat_process(di_minor_t minor, di_node_t node) 152 { 153 char *mn; 154 155 mn = di_minor_name(minor); 156 anynode = node; 157 158 /* 159 * "Special" handling for /dev/sndstat and /dev/mixer. 160 */ 161 if (strcmp(mn, "sound,sndstat0") == 0) { 162 (void) devfsadm_mklink("sndstat", node, minor, 0); 163 (void) devfsadm_secondary_link("mixer", "sndstat", 0); 164 } 165 166 return (DEVFSADM_CONTINUE); 167 } 168 169 /* 170 * This function is called for every audio node. 171 * Calls enumerate to assign a logical unit id, and then 172 * devfsadm_mklink to make the link. 173 */ 174 static int 175 audio_process(di_minor_t minor, di_node_t node) 176 { 177 int flags = 0; 178 char devpath[PATH_MAX + 1]; 179 char newpath[PATH_MAX + 1]; 180 char *buf; 181 char *mn; 182 char *tmp; 183 char *ep; 184 char re_string[RE_SIZE+1]; 185 devfsadm_enumerate_t rules[1] = {NULL}; 186 char base[PATH_MAX + 1]; 187 char linksrc[PATH_MAX + 1]; 188 char linkdst[PATH_MAX + 1]; 189 long num; 190 long inst; 191 int i; 192 char *driver; 193 194 if (system_labeled) 195 flags = DA_ADD|DA_AUDIO; 196 197 anynode = node; 198 mn = di_minor_name(minor); 199 200 if ((tmp = di_devfs_path(node)) == NULL) { 201 return (DEVFSADM_CONTINUE); 202 } 203 (void) snprintf(devpath, sizeof (devpath), "%s:%s", tmp, mn); 204 di_devfs_path_free(tmp); 205 206 if (strncmp(mn, "sound,", sizeof ("sound,") - 1) != 0) { 207 devfsadm_errprint("SUNW_audio_link: " 208 "can't find match for'%s'\n", mn); 209 return (DEVFSADM_CONTINUE); 210 } 211 212 /* strlen("sound,") */ 213 (void) strlcpy(base, mn + 6, sizeof (base)); 214 mn = base; 215 216 driver = di_driver_name(node); 217 218 /* if driver name override in minor name */ 219 if ((tmp = strchr(mn, ',')) != NULL) { 220 driver = mn; 221 *tmp = '\0'; 222 mn = tmp + 1; 223 } 224 225 /* skip past "audio" portion of the minor name */ 226 if (strncmp(mn, "audio", sizeof ("audio") - 1) == 0) { 227 mn += sizeof ("audio") - 1; 228 } 229 230 /* parse the instance number */ 231 for (i = strlen(mn); i; i--) { 232 if (!isdigit(mn[i - 1])) 233 break; 234 } 235 inst = strtol(mn + i, &ep, 10); 236 mn[i] = 0; /* lop off the instance number */ 237 238 /* 239 * First we create a node with the driver under /dev/sound. 240 * Note that "instance numbers" used by the audio framework 241 * are guaranteed to be unique for each driver. 242 */ 243 (void) snprintf(newpath, sizeof (newpath), "sound/%s:%d%s", 244 driver, inst, mn); 245 (void) devfsadm_mklink(newpath, node, minor, flags); 246 247 /* 248 * The rest of this logic is a gross simplification that is 249 * made possible by the fact that each audio node will have 250 * several different minors associated with it. Rather than 251 * processing each node separately, we just create the links 252 * all at once. 253 * 254 * This reduces the chances of the various links being out of 255 * sync with each other. 256 */ 257 if (strcmp(mn, "mixer") != 0) { 258 return (DEVFSADM_CONTINUE); 259 } 260 261 /* 262 * Its the control node, so create the various 263 * secondary links. 264 */ 265 266 /* 267 * We want a match against the physical path 268 * without the minor name component. 269 */ 270 (void) snprintf(re_string, RE_SIZE, "%s", "^mixer([0-9]+)"); 271 rules[0].re = re_string; 272 rules[0].subexp = 1; 273 rules[0].flags = MATCH_ALL; 274 275 /* 276 * enumerate finds the logical audio id, and stuffs 277 * it in buf 278 */ 279 (void) strlcpy(devpath, newpath, sizeof (devpath)); 280 if (devfsadm_enumerate_int(devpath, 0, &buf, rules, 1)) { 281 return (DEVFSADM_CONTINUE); 282 } 283 num = strtol(buf, &ep, 10); 284 free(buf); 285 286 /* /dev/sound/0 */ 287 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ld", 288 driver, inst); 289 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ld", num); 290 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 291 292 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldctl", 293 driver, inst); 294 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ldctl", num); 295 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 296 297 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%lddsp", 298 driver, inst); 299 (void) snprintf(linkdst, sizeof (linkdst), "dsp%ld", num); 300 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 301 302 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldmixer", 303 driver, inst); 304 (void) snprintf(linkdst, sizeof (linkdst), "mixer%ld", num); 305 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 306 307 /* Send control number */ 308 send_number(num); 309 310 return (DEVFSADM_CONTINUE); 311 } 312 313 static void 314 check_audio_link(di_node_t anynode, char *secondary, const char *primary_format) 315 { 316 char primary[PATH_MAX + 1]; 317 int i; 318 int flags = 0; 319 320 /* if link is present, return */ 321 if (devfsadm_link_valid(anynode, secondary) == DEVFSADM_TRUE) { 322 return; 323 } 324 325 if (system_labeled) 326 flags = DA_ADD|DA_AUDIO; 327 328 for (i = 0; i < MAX_AUDIO_LINK; i++) { 329 (void) sprintf(primary, primary_format, i); 330 if (devfsadm_link_valid(anynode, primary) == DEVFSADM_TRUE) { 331 /* we read link to get it to the master "real" link */ 332 (void) devfsadm_secondary_link(secondary, 333 primary, flags); 334 break; 335 } 336 } 337 } 338