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 /* 22*94c894bbSVikram Hegde * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved. 237c478bd9Sstevel@tonic-gate */ 247c478bd9Sstevel@tonic-gate 257c478bd9Sstevel@tonic-gate #include <regex.h> 267c478bd9Sstevel@tonic-gate #include <devfsadm.h> 277c478bd9Sstevel@tonic-gate #include <strings.h> 2888447a05SGarrett D'Amore #include <ctype.h> 297c478bd9Sstevel@tonic-gate #include <stdlib.h> 307c478bd9Sstevel@tonic-gate #include <limits.h> 317c478bd9Sstevel@tonic-gate #include <stdio.h> 3288447a05SGarrett D'Amore #include <syslog.h> 3345916cd2Sjpk #include <bsm/devalloc.h> 3488447a05SGarrett D'Amore #include <sys/audio.h> 3588447a05SGarrett D'Amore #include <sys/soundcard.h> 3688447a05SGarrett D'Amore #include <unistd.h> 377c478bd9Sstevel@tonic-gate 387c478bd9Sstevel@tonic-gate #define MAX_AUDIO_LINK 100 397c478bd9Sstevel@tonic-gate #define RE_SIZE 64 407c478bd9Sstevel@tonic-gate 4145916cd2Sjpk extern int system_labeled; 4245916cd2Sjpk 43*94c894bbSVikram Hegde static void check_audio_link(di_node_t anynode, char *secondary_link, 447c478bd9Sstevel@tonic-gate const char *primary_link_format); 4588447a05SGarrett D'Amore 467c478bd9Sstevel@tonic-gate static int audio_process(di_minor_t minor, di_node_t node); 4788447a05SGarrett D'Amore static int sndstat_process(di_minor_t minor, di_node_t node); 487c478bd9Sstevel@tonic-gate 497c478bd9Sstevel@tonic-gate static devfsadm_create_t audio_cbt[] = { 507c478bd9Sstevel@tonic-gate { "audio", "ddi_audio", NULL, 517c478bd9Sstevel@tonic-gate TYPE_EXACT, ILEVEL_0, audio_process 5288447a05SGarrett D'Amore }, 5388447a05SGarrett D'Amore { "pseudo", "ddi_pseudo", "audio", 5488447a05SGarrett D'Amore TYPE_EXACT|DRV_EXACT, ILEVEL_0, sndstat_process 5588447a05SGarrett D'Amore }, 567c478bd9Sstevel@tonic-gate }; 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate DEVFSADM_CREATE_INIT_V0(audio_cbt); 597c478bd9Sstevel@tonic-gate 607c478bd9Sstevel@tonic-gate /* 617c478bd9Sstevel@tonic-gate * the following can't be one big RE with a bunch of alterations "|" 627c478bd9Sstevel@tonic-gate * because recurse_dev_re() would not work. 637c478bd9Sstevel@tonic-gate */ 647c478bd9Sstevel@tonic-gate static devfsadm_remove_t audio_remove_cbt[] = { 6588447a05SGarrett D'Amore /* 6688447a05SGarrett D'Amore * Secondary links. 6788447a05SGarrett D'Amore */ 6888447a05SGarrett D'Amore 6988447a05SGarrett D'Amore /* /dev/audio, /dev/audioctl, /dev/dsp */ 7092e8aa2fSGarrett D'Amore { "audio", "^audio$", 7192e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 727c478bd9Sstevel@tonic-gate }, 7392e8aa2fSGarrett D'Amore { "audio", "^audioctl$", 7492e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 7588447a05SGarrett D'Amore }, 7692e8aa2fSGarrett D'Amore { "audio", "^dsp$", 7792e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 7888447a05SGarrett D'Amore }, 7992e8aa2fSGarrett D'Amore { "audio", "^mixer", 8092e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 8188447a05SGarrett D'Amore }, 8292e8aa2fSGarrett D'Amore { "audio", "^sndstat$", 837c478bd9Sstevel@tonic-gate RM_PRE|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 847c478bd9Sstevel@tonic-gate }, 8592e8aa2fSGarrett D'Amore { "audio", "^mixer[0-9]+$", 8692e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 8792e8aa2fSGarrett D'Amore }, 8892e8aa2fSGarrett D'Amore { "audio", "^dsp[0-9]+$", 8992e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 9092e8aa2fSGarrett D'Amore }, 9192e8aa2fSGarrett D'Amore { "audio", "^sound/[0-9]+$", 9292e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 9392e8aa2fSGarrett D'Amore }, 9492e8aa2fSGarrett D'Amore { "audio", "^sound/[0-9]+ctl$", 9592e8aa2fSGarrett D'Amore RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all 967c478bd9Sstevel@tonic-gate }, 977c478bd9Sstevel@tonic-gate }; 987c478bd9Sstevel@tonic-gate 997c478bd9Sstevel@tonic-gate DEVFSADM_REMOVE_INIT_V0(audio_remove_cbt); 1007c478bd9Sstevel@tonic-gate 101*94c894bbSVikram Hegde static di_node_t anynode; 102*94c894bbSVikram Hegde 103*94c894bbSVikram Hegde int 104*94c894bbSVikram Hegde minor_init(void) 105*94c894bbSVikram Hegde { 106*94c894bbSVikram Hegde anynode = DI_NODE_NIL; 107*94c894bbSVikram Hegde return (DEVFSADM_SUCCESS); 108*94c894bbSVikram Hegde } 109*94c894bbSVikram Hegde 1107c478bd9Sstevel@tonic-gate int 1117c478bd9Sstevel@tonic-gate minor_fini(void) 1127c478bd9Sstevel@tonic-gate { 113*94c894bbSVikram Hegde check_audio_link(anynode, "audio", "sound/%d"); 114*94c894bbSVikram Hegde check_audio_link(anynode, "audioctl", "sound/%dctl"); 115*94c894bbSVikram Hegde check_audio_link(anynode, "dsp", "dsp%d"); 116*94c894bbSVikram Hegde anynode = DI_NODE_NIL; 1177c478bd9Sstevel@tonic-gate return (DEVFSADM_SUCCESS); 1187c478bd9Sstevel@tonic-gate } 1197c478bd9Sstevel@tonic-gate 1207c478bd9Sstevel@tonic-gate 1217c478bd9Sstevel@tonic-gate #define COPYSUB(to, from, pm, pos) (void) strncpy(to, &from[pm[pos].rm_so], \ 1227c478bd9Sstevel@tonic-gate pm[pos].rm_eo - pm[pos].rm_so); \ 1237c478bd9Sstevel@tonic-gate to[pm[pos].rm_eo - pm[pos].rm_so] = 0; 1247c478bd9Sstevel@tonic-gate 12588447a05SGarrett D'Amore static void 12688447a05SGarrett D'Amore send_number(long num) 12788447a05SGarrett D'Amore { 12888447a05SGarrett D'Amore char buf[PATH_MAX+1]; 12988447a05SGarrett D'Amore 13088447a05SGarrett D'Amore /* 13188447a05SGarrett D'Amore * This is not safe with -r. 13288447a05SGarrett D'Amore */ 13388447a05SGarrett D'Amore if (strcmp(devfsadm_root_path(), "/") != 0) 13488447a05SGarrett D'Amore return; 13588447a05SGarrett D'Amore 13688447a05SGarrett D'Amore (void) snprintf(buf, sizeof (buf), "/dev/mixer%ld", num); 13788447a05SGarrett D'Amore if (device_exists(buf)) { 13888447a05SGarrett D'Amore int fd; 13988447a05SGarrett D'Amore 14088447a05SGarrett D'Amore if ((fd = open(buf, O_RDWR)) < 0) 14188447a05SGarrett D'Amore return; 14288447a05SGarrett D'Amore 14388447a05SGarrett D'Amore (void) ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num); 14488447a05SGarrett D'Amore (void) close(fd); 14588447a05SGarrett D'Amore devfsadm_print(CHATTY_MID, 14688447a05SGarrett D'Amore "sent devnum audio %ld to %s\n", num, buf); 14788447a05SGarrett D'Amore } 14888447a05SGarrett D'Amore } 14988447a05SGarrett D'Amore 15088447a05SGarrett D'Amore static int 15188447a05SGarrett D'Amore sndstat_process(di_minor_t minor, di_node_t node) 15288447a05SGarrett D'Amore { 15388447a05SGarrett D'Amore char *mn; 15488447a05SGarrett D'Amore 15588447a05SGarrett D'Amore mn = di_minor_name(minor); 156*94c894bbSVikram Hegde anynode = node; 15788447a05SGarrett D'Amore 15888447a05SGarrett D'Amore /* 15988447a05SGarrett D'Amore * "Special" handling for /dev/sndstat and /dev/mixer. 16088447a05SGarrett D'Amore */ 16188447a05SGarrett D'Amore if (strcmp(mn, "sound,sndstat0") == 0) { 16288447a05SGarrett D'Amore (void) devfsadm_mklink("sndstat", node, minor, 0); 16388447a05SGarrett D'Amore (void) devfsadm_secondary_link("mixer", "sndstat", 0); 16488447a05SGarrett D'Amore } 16588447a05SGarrett D'Amore 16688447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 16788447a05SGarrett D'Amore } 16888447a05SGarrett D'Amore 1697c478bd9Sstevel@tonic-gate /* 1707c478bd9Sstevel@tonic-gate * This function is called for every audio node. 1717c478bd9Sstevel@tonic-gate * Calls enumerate to assign a logical unit id, and then 1727c478bd9Sstevel@tonic-gate * devfsadm_mklink to make the link. 1737c478bd9Sstevel@tonic-gate */ 1747c478bd9Sstevel@tonic-gate static int 1757c478bd9Sstevel@tonic-gate audio_process(di_minor_t minor, di_node_t node) 1767c478bd9Sstevel@tonic-gate { 17745916cd2Sjpk int flags = 0; 17888447a05SGarrett D'Amore char devpath[PATH_MAX + 1]; 17988447a05SGarrett D'Amore char newpath[PATH_MAX + 1]; 1807c478bd9Sstevel@tonic-gate char *buf; 1817c478bd9Sstevel@tonic-gate char *mn; 18288447a05SGarrett D'Amore char *tmp; 18388447a05SGarrett D'Amore char *ep; 1847c478bd9Sstevel@tonic-gate char re_string[RE_SIZE+1]; 1857c478bd9Sstevel@tonic-gate devfsadm_enumerate_t rules[1] = {NULL}; 186aef5f291SGarrett D'Amore char base[PATH_MAX + 1]; 187aef5f291SGarrett D'Amore char linksrc[PATH_MAX + 1]; 188aef5f291SGarrett D'Amore char linkdst[PATH_MAX + 1]; 189aef5f291SGarrett D'Amore long num; 190aef5f291SGarrett D'Amore long inst; 191aef5f291SGarrett D'Amore int i; 192aef5f291SGarrett D'Amore char *driver; 1937c478bd9Sstevel@tonic-gate 19488447a05SGarrett D'Amore if (system_labeled) 19588447a05SGarrett D'Amore flags = DA_ADD|DA_AUDIO; 1967c478bd9Sstevel@tonic-gate 197*94c894bbSVikram Hegde anynode = node; 1987c478bd9Sstevel@tonic-gate mn = di_minor_name(minor); 1997c478bd9Sstevel@tonic-gate 20088447a05SGarrett D'Amore if ((tmp = di_devfs_path(node)) == NULL) { 2017c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2027c478bd9Sstevel@tonic-gate } 20388447a05SGarrett D'Amore (void) snprintf(devpath, sizeof (devpath), "%s:%s", tmp, mn); 20488447a05SGarrett D'Amore di_devfs_path_free(tmp); 2057c478bd9Sstevel@tonic-gate 206aef5f291SGarrett D'Amore if (strncmp(mn, "sound,", sizeof ("sound,") - 1) != 0) { 207aef5f291SGarrett D'Amore devfsadm_errprint("SUNW_audio_link: " 208aef5f291SGarrett D'Amore "can't find match for'%s'\n", mn); 20988447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 2107c478bd9Sstevel@tonic-gate } 2117c478bd9Sstevel@tonic-gate 21288447a05SGarrett D'Amore /* strlen("sound,") */ 21388447a05SGarrett D'Amore (void) strlcpy(base, mn + 6, sizeof (base)); 21488447a05SGarrett D'Amore mn = base; 21588447a05SGarrett D'Amore 21688447a05SGarrett D'Amore driver = di_driver_name(node); 21788447a05SGarrett D'Amore 21888447a05SGarrett D'Amore /* if driver name override in minor name */ 21988447a05SGarrett D'Amore if ((tmp = strchr(mn, ',')) != NULL) { 22088447a05SGarrett D'Amore driver = mn; 22188447a05SGarrett D'Amore *tmp = '\0'; 22288447a05SGarrett D'Amore mn = tmp + 1; 22388447a05SGarrett D'Amore } 22488447a05SGarrett D'Amore 22588447a05SGarrett D'Amore /* skip past "audio" portion of the minor name */ 226aef5f291SGarrett D'Amore if (strncmp(mn, "audio", sizeof ("audio") - 1) == 0) { 227aef5f291SGarrett D'Amore mn += sizeof ("audio") - 1; 22888447a05SGarrett D'Amore } 22988447a05SGarrett D'Amore 23088447a05SGarrett D'Amore /* parse the instance number */ 23188447a05SGarrett D'Amore for (i = strlen(mn); i; i--) { 23288447a05SGarrett D'Amore if (!isdigit(mn[i - 1])) 23388447a05SGarrett D'Amore break; 23488447a05SGarrett D'Amore } 23588447a05SGarrett D'Amore inst = strtol(mn + i, &ep, 10); 23688447a05SGarrett D'Amore mn[i] = 0; /* lop off the instance number */ 23788447a05SGarrett D'Amore 23888447a05SGarrett D'Amore /* 23988447a05SGarrett D'Amore * First we create a node with the driver under /dev/sound. 24088447a05SGarrett D'Amore * Note that "instance numbers" used by the audio framework 24188447a05SGarrett D'Amore * are guaranteed to be unique for each driver. 24288447a05SGarrett D'Amore */ 24388447a05SGarrett D'Amore (void) snprintf(newpath, sizeof (newpath), "sound/%s:%d%s", 24488447a05SGarrett D'Amore driver, inst, mn); 24588447a05SGarrett D'Amore (void) devfsadm_mklink(newpath, node, minor, flags); 24688447a05SGarrett D'Amore 24788447a05SGarrett D'Amore /* 248aef5f291SGarrett D'Amore * The rest of this logic is a gross simplification that is 249aef5f291SGarrett D'Amore * made possible by the fact that each audio node will have 250aef5f291SGarrett D'Amore * several different minors associated with it. Rather than 25188447a05SGarrett D'Amore * processing each node separately, we just create the links 25288447a05SGarrett D'Amore * all at once. 25388447a05SGarrett D'Amore * 254aef5f291SGarrett D'Amore * This reduces the chances of the various links being out of 255aef5f291SGarrett D'Amore * sync with each other. 25688447a05SGarrett D'Amore */ 25788447a05SGarrett D'Amore if (strcmp(mn, "mixer") != 0) { 25888447a05SGarrett D'Amore return (DEVFSADM_CONTINUE); 25988447a05SGarrett D'Amore } 26088447a05SGarrett D'Amore 26188447a05SGarrett D'Amore /* 26288447a05SGarrett D'Amore * Its the control node, so create the various 26388447a05SGarrett D'Amore * secondary links. 26488447a05SGarrett D'Amore */ 26588447a05SGarrett D'Amore 2667c478bd9Sstevel@tonic-gate /* 2677c478bd9Sstevel@tonic-gate * We want a match against the physical path 2687c478bd9Sstevel@tonic-gate * without the minor name component. 2697c478bd9Sstevel@tonic-gate */ 27088447a05SGarrett D'Amore (void) snprintf(re_string, RE_SIZE, "%s", "^mixer([0-9]+)"); 2717c478bd9Sstevel@tonic-gate rules[0].re = re_string; 2727c478bd9Sstevel@tonic-gate rules[0].subexp = 1; 27388447a05SGarrett D'Amore rules[0].flags = MATCH_ALL; 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate /* 2767c478bd9Sstevel@tonic-gate * enumerate finds the logical audio id, and stuffs 2777c478bd9Sstevel@tonic-gate * it in buf 2787c478bd9Sstevel@tonic-gate */ 27988447a05SGarrett D'Amore (void) strlcpy(devpath, newpath, sizeof (devpath)); 28088447a05SGarrett D'Amore if (devfsadm_enumerate_int(devpath, 0, &buf, rules, 1)) { 2817c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 2827c478bd9Sstevel@tonic-gate } 28388447a05SGarrett D'Amore num = strtol(buf, &ep, 10); 2847c478bd9Sstevel@tonic-gate free(buf); 2857c478bd9Sstevel@tonic-gate 28688447a05SGarrett D'Amore /* /dev/sound/0 */ 28788447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ld", 28888447a05SGarrett D'Amore driver, inst); 28988447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "sound/%ld", num); 29088447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 29188447a05SGarrett D'Amore 29288447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldctl", 29388447a05SGarrett D'Amore driver, inst); 29488447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "sound/%ldctl", num); 29588447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 29688447a05SGarrett D'Amore 29788447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%lddsp", 29888447a05SGarrett D'Amore driver, inst); 29988447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "dsp%ld", num); 30088447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 30188447a05SGarrett D'Amore 30288447a05SGarrett D'Amore (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldmixer", 30388447a05SGarrett D'Amore driver, inst); 30488447a05SGarrett D'Amore (void) snprintf(linkdst, sizeof (linkdst), "mixer%ld", num); 30588447a05SGarrett D'Amore (void) devfsadm_secondary_link(linkdst, linksrc, flags); 30688447a05SGarrett D'Amore 30788447a05SGarrett D'Amore /* Send control number */ 30888447a05SGarrett D'Amore send_number(num); 30988447a05SGarrett D'Amore 3107c478bd9Sstevel@tonic-gate return (DEVFSADM_CONTINUE); 3117c478bd9Sstevel@tonic-gate } 3127c478bd9Sstevel@tonic-gate 3137c478bd9Sstevel@tonic-gate static void 314*94c894bbSVikram Hegde check_audio_link(di_node_t anynode, char *secondary, const char *primary_format) 3157c478bd9Sstevel@tonic-gate { 31688447a05SGarrett D'Amore char primary[PATH_MAX + 1]; 3177c478bd9Sstevel@tonic-gate int i; 31845916cd2Sjpk int flags = 0; 3197c478bd9Sstevel@tonic-gate 3207c478bd9Sstevel@tonic-gate /* if link is present, return */ 321*94c894bbSVikram Hegde if (devfsadm_link_valid(anynode, secondary) == DEVFSADM_TRUE) { 3227c478bd9Sstevel@tonic-gate return; 3237c478bd9Sstevel@tonic-gate } 3247c478bd9Sstevel@tonic-gate 32545916cd2Sjpk if (system_labeled) 32645916cd2Sjpk flags = DA_ADD|DA_AUDIO; 32745916cd2Sjpk 3287c478bd9Sstevel@tonic-gate for (i = 0; i < MAX_AUDIO_LINK; i++) { 32988447a05SGarrett D'Amore (void) sprintf(primary, primary_format, i); 330*94c894bbSVikram Hegde if (devfsadm_link_valid(anynode, primary) == DEVFSADM_TRUE) { 33188447a05SGarrett D'Amore /* we read link to get it to the master "real" link */ 33288447a05SGarrett D'Amore (void) devfsadm_secondary_link(secondary, 33388447a05SGarrett D'Amore primary, flags); 3347c478bd9Sstevel@tonic-gate break; 3357c478bd9Sstevel@tonic-gate } 3367c478bd9Sstevel@tonic-gate } 3377c478bd9Sstevel@tonic-gate } 338