17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 545916cd2Sjpk * Common Development and Distribution License (the "License"). 645916cd2Sjpk * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 2288447a05SGarrett D'Amore * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 257c478bd9Sstevel@tonic-gate 267c478bd9Sstevel@tonic-gate #include <regex.h> 277c478bd9Sstevel@tonic-gate #include <devfsadm.h> 287c478bd9Sstevel@tonic-gate #include <strings.h> 2988447a05SGarrett D'Amore #include <ctype.h> 307c478bd9Sstevel@tonic-gate #include <stdlib.h> 317c478bd9Sstevel@tonic-gate #include <limits.h> 327c478bd9Sstevel@tonic-gate #include <stdio.h> 3388447a05SGarrett D'Amore #include <syslog.h> 3445916cd2Sjpk #include <bsm/devalloc.h> 3588447a05SGarrett D'Amore #include <sys/audio.h> 3688447a05SGarrett D'Amore #include <sys/soundcard.h> 3788447a05SGarrett D'Amore #include <unistd.h> 387c478bd9Sstevel@tonic-gate 397c478bd9Sstevel@tonic-gate #define MAX_AUDIO_LINK 100 407c478bd9Sstevel@tonic-gate #define RE_SIZE 64 417c478bd9Sstevel@tonic-gate 4245916cd2Sjpk extern int system_labeled; 4345916cd2Sjpk 447c478bd9Sstevel@tonic-gate static void check_audio_link(char *secondary_link, 457c478bd9Sstevel@tonic-gate const char *primary_link_format); 4688447a05SGarrett D'Amore 477c478bd9Sstevel@tonic-gate static int audio_process(di_minor_t minor, di_node_t node); 4888447a05SGarrett D'Amore static int sndstat_process(di_minor_t minor, di_node_t node); 497c478bd9Sstevel@tonic-gate 507c478bd9Sstevel@tonic-gate static devfsadm_create_t audio_cbt[] = { 517c478bd9Sstevel@tonic-gate { "audio", "ddi_audio", NULL, 527c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, audio_process 5388447a05SGarrett D'Amore }, 5488447a05SGarrett D'Amore { "pseudo", "ddi_pseudo", "audio", 5588447a05SGarrett D'Amore TYPE_EXACT|DRV_EXACT, ILEVEL_0, sndstat_process 5688447a05SGarrett D'Amore }, 577c478bd9Sstevel@tonic-gate }; 587c478bd9Sstevel@tonic-gate 597c478bd9Sstevel@tonic-gate DEVFSADM_CREATE_INIT_V0(audio_cbt); 607c478bd9Sstevel@tonic-gate 617c478bd9Sstevel@tonic-gate /* 627c478bd9Sstevel@tonic-gate * the following can't be one big RE with a bunch of alterations "|" 637c478bd9Sstevel@tonic-gate * because recurse_dev_re() would not work. 647c478bd9Sstevel@tonic-gate */ 657c478bd9Sstevel@tonic-gate static devfsadm_remove_t audio_remove_cbt[] = { 6688447a05SGarrett D'Amore /* 6788447a05SGarrett D'Amore * Secondary links. 6888447a05SGarrett D'Amore */ 6988447a05SGarrett D'Amore 7088447a05SGarrett D'Amore /* /dev/audio, /dev/audioctl, /dev/dsp */ 7192e8aa2fSGarrett D'Amore { "audio", "^audio$", 7292e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 737c478bd9Sstevel@tonic-gate }, 7492e8aa2fSGarrett D'Amore { "audio", "^audioctl$", 7592e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 7688447a05SGarrett D'Amore }, 7792e8aa2fSGarrett D'Amore { "audio", "^dsp$", 7892e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 7988447a05SGarrett D'Amore }, 8092e8aa2fSGarrett D'Amore { "audio", "^mixer", 8192e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 8288447a05SGarrett D'Amore }, 8392e8aa2fSGarrett D'Amore { "audio", "^sndstat$", 847c478bd9Sstevel@tonic-gate RM_PRE|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 857c478bd9Sstevel@tonic-gate }, 8692e8aa2fSGarrett D'Amore { "audio", "^mixer[0-9]+$", 8792e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 8892e8aa2fSGarrett D'Amore }, 8992e8aa2fSGarrett D'Amore { "audio", "^dsp[0-9]+$", 9092e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 9192e8aa2fSGarrett D'Amore }, 9292e8aa2fSGarrett D'Amore { "audio", "^sound/[0-9]+$", 9392e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 9492e8aa2fSGarrett D'Amore }, 9592e8aa2fSGarrett D'Amore { "audio", "^sound/[0-9]+ctl$", 9692e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 977c478bd9Sstevel@tonic-gate }, 987c478bd9Sstevel@tonic-gate }; 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate DEVFSADM_REMOVE_INIT_V0(audio_remove_cbt); 1017c478bd9Sstevel@tonic-gate 1027c478bd9Sstevel@tonic-gate int 1037c478bd9Sstevel@tonic-gate minor_fini(void) 1047c478bd9Sstevel@tonic-gate { 1057c478bd9Sstevel@tonic-gate check_audio_link("audio", "sound/%d"); 1067c478bd9Sstevel@tonic-gate check_audio_link("audioctl", "sound/%dctl"); 10788447a05SGarrett D'Amore check_audio_link("dsp", "dsp%d"); 1087c478bd9Sstevel@tonic-gate return (DEVFSADM_SUCCESS); 1097c478bd9Sstevel@tonic-gate } 1107c478bd9Sstevel@tonic-gate 1117c478bd9Sstevel@tonic-gate 1127c478bd9Sstevel@tonic-gate #define COPYSUB(to, from, pm, pos) (void) strncpy(to, &from[pm[pos].rm_so], \ 1137c478bd9Sstevel@tonic-gate pm[pos].rm_eo - pm[pos].rm_so); \ 1147c478bd9Sstevel@tonic-gate to[pm[pos].rm_eo - pm[pos].rm_so] = 0; 1157c478bd9Sstevel@tonic-gate 11688447a05SGarrett D'Amore static void 11788447a05SGarrett D'Amore send_number(long num) 11888447a05SGarrett D'Amore { 11988447a05SGarrett D'Amore char buf[PATH_MAX+1]; 12088447a05SGarrett D'Amore 12188447a05SGarrett D'Amore /* 12288447a05SGarrett D'Amore * This is not safe with -r. 12388447a05SGarrett D'Amore */ 12488447a05SGarrett D'Amore if (strcmp(devfsadm_root_path(), "/") != 0) 12588447a05SGarrett D'Amore return; 12688447a05SGarrett D'Amore 12788447a05SGarrett D'Amore (void) snprintf(buf, sizeof (buf), "/dev/mixer%ld", num); 12888447a05SGarrett D'Amore if (device_exists(buf)) { 12988447a05SGarrett D'Amore int fd; 13088447a05SGarrett D'Amore 13188447a05SGarrett D'Amore if ((fd = open(buf, O_RDWR)) < 0) 13288447a05SGarrett D'Amore return; 13388447a05SGarrett D'Amore 13488447a05SGarrett D'Amore (void) ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num); 13588447a05SGarrett D'Amore (void) close(fd); 13688447a05SGarrett D'Amore devfsadm_print(CHATTY_MID, 13788447a05SGarrett D'Amore "sent devnum audio %ld to %s\n", num, buf); 13888447a05SGarrett D'Amore } 13988447a05SGarrett D'Amore } 14088447a05SGarrett D'Amore 14188447a05SGarrett D'Amore static int 14288447a05SGarrett D'Amore sndstat_process(di_minor_t minor, di_node_t node) 14388447a05SGarrett D'Amore { 14488447a05SGarrett D'Amore char *mn; 14588447a05SGarrett D'Amore 14688447a05SGarrett D'Amore mn = di_minor_name(minor); 14788447a05SGarrett D'Amore 14888447a05SGarrett D'Amore /* 14988447a05SGarrett D'Amore * "Special" handling for /dev/sndstat and /dev/mixer. 15088447a05SGarrett D'Amore */ 15188447a05SGarrett D'Amore if (strcmp(mn, "sound,sndstat0") == 0) { 15288447a05SGarrett D'Amore (void) devfsadm_mklink("sndstat", node, minor, 0); 15388447a05SGarrett D'Amore (void) devfsadm_secondary_link("mixer", "sndstat", 0); 15488447a05SGarrett D'Amore } 15588447a05SGarrett D'Amore 15688447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 15788447a05SGarrett D'Amore } 15888447a05SGarrett D'Amore 1597c478bd9Sstevel@tonic-gate /* 1607c478bd9Sstevel@tonic-gate * This function is called for every audio node. 1617c478bd9Sstevel@tonic-gate * Calls enumerate to assign a logical unit id, and then 1627c478bd9Sstevel@tonic-gate * devfsadm_mklink to make the link. 1637c478bd9Sstevel@tonic-gate */ 1647c478bd9Sstevel@tonic-gate static int 1657c478bd9Sstevel@tonic-gate audio_process(di_minor_t minor, di_node_t node) 1667c478bd9Sstevel@tonic-gate { 16745916cd2Sjpk int flags = 0; 16888447a05SGarrett D'Amore char devpath[PATH_MAX + 1]; 16988447a05SGarrett D'Amore char newpath[PATH_MAX + 1]; 1707c478bd9Sstevel@tonic-gate char *buf; 1717c478bd9Sstevel@tonic-gate char *mn; 17288447a05SGarrett D'Amore char *tmp; 17388447a05SGarrett D'Amore char *ep; 1747c478bd9Sstevel@tonic-gate char re_string[RE_SIZE+1]; 1757c478bd9Sstevel@tonic-gate devfsadm_enumerate_t rules[1] = {NULL}; 176*aef5f291SGarrett D'Amore char base[PATH_MAX + 1]; 177*aef5f291SGarrett D'Amore char linksrc[PATH_MAX + 1]; 178*aef5f291SGarrett D'Amore char linkdst[PATH_MAX + 1]; 179*aef5f291SGarrett D'Amore long num; 180*aef5f291SGarrett D'Amore long inst; 181*aef5f291SGarrett D'Amore int i; 182*aef5f291SGarrett D'Amore char *driver; 1837c478bd9Sstevel@tonic-gate 18488447a05SGarrett D'Amore if (system_labeled) 18588447a05SGarrett D'Amore flags = DA_ADD|DA_AUDIO; 1867c478bd9Sstevel@tonic-gate 1877c478bd9Sstevel@tonic-gate mn = di_minor_name(minor); 1887c478bd9Sstevel@tonic-gate 18988447a05SGarrett D'Amore if ((tmp = di_devfs_path(node)) == NULL) { 1907c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 1917c478bd9Sstevel@tonic-gate } 19288447a05SGarrett D'Amore (void) snprintf(devpath, sizeof (devpath), "%s:%s", tmp, mn); 19388447a05SGarrett D'Amore di_devfs_path_free(tmp); 1947c478bd9Sstevel@tonic-gate 195*aef5f291SGarrett D'Amore if (strncmp(mn, "sound,", sizeof ("sound,") - 1) != 0) { 196*aef5f291SGarrett D'Amore devfsadm_errprint("SUNW_audio_link: " 197*aef5f291SGarrett D'Amore "can't find match for'%s'\n", mn); 19888447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 1997c478bd9Sstevel@tonic-gate } 2007c478bd9Sstevel@tonic-gate 20188447a05SGarrett D'Amore /* strlen("sound,") */ 20288447a05SGarrett D'Amore (void) strlcpy(base, mn + 6, sizeof (base)); 20388447a05SGarrett D'Amore mn = base; 20488447a05SGarrett D'Amore 20588447a05SGarrett D'Amore driver = di_driver_name(node); 20688447a05SGarrett D'Amore 20788447a05SGarrett D'Amore /* if driver name override in minor name */ 20888447a05SGarrett D'Amore if ((tmp = strchr(mn, ',')) != NULL) { 20988447a05SGarrett D'Amore driver = mn; 21088447a05SGarrett D'Amore *tmp = '\0'; 21188447a05SGarrett D'Amore mn = tmp + 1; 21288447a05SGarrett D'Amore } 21388447a05SGarrett D'Amore 21488447a05SGarrett D'Amore /* skip past "audio" portion of the minor name */ 215*aef5f291SGarrett D'Amore if (strncmp(mn, "audio", sizeof ("audio") - 1) == 0) { 216*aef5f291SGarrett D'Amore mn += sizeof ("audio") - 1; 21788447a05SGarrett D'Amore } 21888447a05SGarrett D'Amore 21988447a05SGarrett D'Amore /* parse the instance number */ 22088447a05SGarrett D'Amore for (i = strlen(mn); i; i--) { 22188447a05SGarrett D'Amore if (!isdigit(mn[i - 1])) 22288447a05SGarrett D'Amore break; 22388447a05SGarrett D'Amore } 22488447a05SGarrett D'Amore inst = strtol(mn + i, &ep, 10); 22588447a05SGarrett D'Amore mn[i] = 0; /* lop off the instance number */ 22688447a05SGarrett D'Amore 22788447a05SGarrett D'Amore /* 22888447a05SGarrett D'Amore * First we create a node with the driver under /dev/sound. 22988447a05SGarrett D'Amore * Note that "instance numbers" used by the audio framework 23088447a05SGarrett D'Amore * are guaranteed to be unique for each driver. 23188447a05SGarrett D'Amore */ 23288447a05SGarrett D'Amore (void) snprintf(newpath, sizeof (newpath), "sound/%s:%d%s", 23388447a05SGarrett D'Amore driver, inst, mn); 23488447a05SGarrett D'Amore (void) devfsadm_mklink(newpath, node, minor, flags); 23588447a05SGarrett D'Amore 23688447a05SGarrett D'Amore /* 237*aef5f291SGarrett D'Amore * The rest of this logic is a gross simplification that is 238*aef5f291SGarrett D'Amore * made possible by the fact that each audio node will have 239*aef5f291SGarrett D'Amore * several different minors associated with it. Rather than 24088447a05SGarrett D'Amore * processing each node separately, we just create the links 24188447a05SGarrett D'Amore * all at once. 24288447a05SGarrett D'Amore * 243*aef5f291SGarrett D'Amore * This reduces the chances of the various links being out of 244*aef5f291SGarrett D'Amore * sync with each other. 24588447a05SGarrett D'Amore */ 24688447a05SGarrett D'Amore if (strcmp(mn, "mixer") != 0) { 24788447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 24888447a05SGarrett D'Amore } 24988447a05SGarrett D'Amore 25088447a05SGarrett D'Amore /* 25188447a05SGarrett D'Amore * Its the control node, so create the various 25288447a05SGarrett D'Amore * secondary links. 25388447a05SGarrett D'Amore */ 25488447a05SGarrett D'Amore 2557c478bd9Sstevel@tonic-gate /* 2567c478bd9Sstevel@tonic-gate * We want a match against the physical path 2577c478bd9Sstevel@tonic-gate * without the minor name component. 2587c478bd9Sstevel@tonic-gate */ 25988447a05SGarrett D'Amore (void) snprintf(re_string, RE_SIZE, "%s", "^mixer([0-9]+)"); 2607c478bd9Sstevel@tonic-gate rules[0].re = re_string; 2617c478bd9Sstevel@tonic-gate rules[0].subexp = 1; 26288447a05SGarrett D'Amore rules[0].flags = MATCH_ALL; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate /* 2657c478bd9Sstevel@tonic-gate * enumerate finds the logical audio id, and stuffs 2667c478bd9Sstevel@tonic-gate * it in buf 2677c478bd9Sstevel@tonic-gate */ 26888447a05SGarrett D'Amore (void) strlcpy(devpath, newpath, sizeof (devpath)); 26988447a05SGarrett D'Amore if (devfsadm_enumerate_int(devpath, 0, &buf, rules, 1)) { 2707c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2717c478bd9Sstevel@tonic-gate } 27288447a05SGarrett D'Amore num = strtol(buf, &ep, 10); 2737c478bd9Sstevel@tonic-gate free(buf); 2747c478bd9Sstevel@tonic-gate 27588447a05SGarrett D'Amore /* /dev/sound/0 */ 27688447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ld", 27788447a05SGarrett D'Amore driver, inst); 27888447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "sound/%ld", num); 27988447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 28088447a05SGarrett D'Amore 28188447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldctl", 28288447a05SGarrett D'Amore driver, inst); 28388447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "sound/%ldctl", num); 28488447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 28588447a05SGarrett D'Amore 28688447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%lddsp", 28788447a05SGarrett D'Amore driver, inst); 28888447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "dsp%ld", num); 28988447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 29088447a05SGarrett D'Amore 29188447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldmixer", 29288447a05SGarrett D'Amore driver, inst); 29388447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "mixer%ld", num); 29488447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 29588447a05SGarrett D'Amore 29688447a05SGarrett D'Amore /* Send control number */ 29788447a05SGarrett D'Amore send_number(num); 29888447a05SGarrett D'Amore 2997c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 3007c478bd9Sstevel@tonic-gate } 3017c478bd9Sstevel@tonic-gate 3027c478bd9Sstevel@tonic-gate static void 30388447a05SGarrett D'Amore check_audio_link(char *secondary, const char *primary_format) 3047c478bd9Sstevel@tonic-gate { 30588447a05SGarrett D'Amore char primary[PATH_MAX + 1]; 3067c478bd9Sstevel@tonic-gate int i; 30745916cd2Sjpk int flags = 0; 3087c478bd9Sstevel@tonic-gate 3097c478bd9Sstevel@tonic-gate /* if link is present, return */ 31088447a05SGarrett D'Amore if (devfsadm_link_valid(secondary) == DEVFSADM_TRUE) { 3117c478bd9Sstevel@tonic-gate return; 3127c478bd9Sstevel@tonic-gate } 3137c478bd9Sstevel@tonic-gate 31445916cd2Sjpk if (system_labeled) 31545916cd2Sjpk flags = DA_ADD|DA_AUDIO; 31645916cd2Sjpk 3177c478bd9Sstevel@tonic-gate for (i = 0; i < MAX_AUDIO_LINK; i++) { 31888447a05SGarrett D'Amore (void) sprintf(primary, primary_format, i); 31988447a05SGarrett D'Amore if (devfsadm_link_valid(primary) == DEVFSADM_TRUE) { 32088447a05SGarrett D'Amore /* we read link to get it to the master "real" link */ 32188447a05SGarrett D'Amore (void) devfsadm_secondary_link(secondary, 32288447a05SGarrett D'Amore primary, flags); 3237c478bd9Sstevel@tonic-gate break; 3247c478bd9Sstevel@tonic-gate } 3257c478bd9Sstevel@tonic-gate } 3267c478bd9Sstevel@tonic-gate } 327