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