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