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 int 142 sndstat_process(di_minor_t minor, di_node_t node) 143 { 144 char *mn; 145 146 mn = di_minor_name(minor); 147 148 /* 149 * "Special" handling for /dev/sndstat and /dev/mixer. 150 */ 151 if (strcmp(mn, "sound,sndstat0") == 0) { 152 (void) devfsadm_mklink("sndstat", node, minor, 0); 153 (void) devfsadm_secondary_link("mixer", "sndstat", 0); 154 } 155 156 return (DEVFSADM_CONTINUE); 157 } 158 159 /* 160 * This function is called for every audio node. 161 * Calls enumerate to assign a logical unit id, and then 162 * devfsadm_mklink to make the link. 163 */ 164 static int 165 audio_process(di_minor_t minor, di_node_t node) 166 { 167 int flags = 0; 168 char devpath[PATH_MAX + 1]; 169 char newpath[PATH_MAX + 1]; 170 char *buf; 171 char *mn; 172 char *tmp; 173 char *ep; 174 char re_string[RE_SIZE+1]; 175 devfsadm_enumerate_t rules[1] = {NULL}; 176 char base[PATH_MAX + 1]; 177 char linksrc[PATH_MAX + 1]; 178 char linkdst[PATH_MAX + 1]; 179 long num; 180 long inst; 181 int i; 182 char *driver; 183 184 if (system_labeled) 185 flags = DA_ADD|DA_AUDIO; 186 187 mn = di_minor_name(minor); 188 189 if ((tmp = di_devfs_path(node)) == NULL) { 190 return (DEVFSADM_CONTINUE); 191 } 192 (void) snprintf(devpath, sizeof (devpath), "%s:%s", tmp, mn); 193 di_devfs_path_free(tmp); 194 195 if (strncmp(mn, "sound,", sizeof ("sound,") - 1) != 0) { 196 devfsadm_errprint("SUNW_audio_link: " 197 "can't find match for'%s'\n", mn); 198 return (DEVFSADM_CONTINUE); 199 } 200 201 /* strlen("sound,") */ 202 (void) strlcpy(base, mn + 6, sizeof (base)); 203 mn = base; 204 205 driver = di_driver_name(node); 206 207 /* if driver name override in minor name */ 208 if ((tmp = strchr(mn, ',')) != NULL) { 209 driver = mn; 210 *tmp = '\0'; 211 mn = tmp + 1; 212 } 213 214 /* skip past "audio" portion of the minor name */ 215 if (strncmp(mn, "audio", sizeof ("audio") - 1) == 0) { 216 mn += sizeof ("audio") - 1; 217 } 218 219 /* parse the instance number */ 220 for (i = strlen(mn); i; i--) { 221 if (!isdigit(mn[i - 1])) 222 break; 223 } 224 inst = strtol(mn + i, &ep, 10); 225 mn[i] = 0; /* lop off the instance number */ 226 227 /* 228 * First we create a node with the driver under /dev/sound. 229 * Note that "instance numbers" used by the audio framework 230 * are guaranteed to be unique for each driver. 231 */ 232 (void) snprintf(newpath, sizeof (newpath), "sound/%s:%d%s", 233 driver, inst, mn); 234 (void) devfsadm_mklink(newpath, node, minor, flags); 235 236 /* 237 * The rest of this logic is a gross simplification that is 238 * made possible by the fact that each audio node will have 239 * several different minors associated with it. Rather than 240 * processing each node separately, we just create the links 241 * all at once. 242 * 243 * This reduces the chances of the various links being out of 244 * sync with each other. 245 */ 246 if (strcmp(mn, "mixer") != 0) { 247 return (DEVFSADM_CONTINUE); 248 } 249 250 /* 251 * Its the control node, so create the various 252 * secondary links. 253 */ 254 255 /* 256 * We want a match against the physical path 257 * without the minor name component. 258 */ 259 (void) snprintf(re_string, RE_SIZE, "%s", "^mixer([0-9]+)"); 260 rules[0].re = re_string; 261 rules[0].subexp = 1; 262 rules[0].flags = MATCH_ALL; 263 264 /* 265 * enumerate finds the logical audio id, and stuffs 266 * it in buf 267 */ 268 (void) strlcpy(devpath, newpath, sizeof (devpath)); 269 if (devfsadm_enumerate_int(devpath, 0, &buf, rules, 1)) { 270 return (DEVFSADM_CONTINUE); 271 } 272 num = strtol(buf, &ep, 10); 273 free(buf); 274 275 /* /dev/sound/0 */ 276 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ld", 277 driver, inst); 278 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ld", num); 279 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 280 281 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldctl", 282 driver, inst); 283 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ldctl", num); 284 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 285 286 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%lddsp", 287 driver, inst); 288 (void) snprintf(linkdst, sizeof (linkdst), "dsp%ld", num); 289 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 290 291 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldmixer", 292 driver, inst); 293 (void) snprintf(linkdst, sizeof (linkdst), "mixer%ld", num); 294 (void) devfsadm_secondary_link(linkdst, linksrc, flags); 295 296 /* Send control number */ 297 send_number(num); 298 299 return (DEVFSADM_CONTINUE); 300 } 301 302 static void 303 check_audio_link(char *secondary, const char *primary_format) 304 { 305 char primary[PATH_MAX + 1]; 306 int i; 307 int flags = 0; 308 309 /* if link is present, return */ 310 if (devfsadm_link_valid(secondary) == DEVFSADM_TRUE) { 311 return; 312 } 313 314 if (system_labeled) 315 flags = DA_ADD|DA_AUDIO; 316 317 for (i = 0; i < MAX_AUDIO_LINK; i++) { 318 (void) sprintf(primary, primary_format, i); 319 if (devfsadm_link_valid(primary) == DEVFSADM_TRUE) { 320 /* we read link to get it to the master "real" link */ 321 (void) devfsadm_secondary_link(secondary, 322 primary, flags); 323 break; 324 } 325 } 326 } 327