dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / devfsadm / audio_link.c
blob8bb98c96cb998f58a77e203269f0bdc52ac1f9bc
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright (c) 1998, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <regex.h>
26 #include <devfsadm.h>
27 #include <strings.h>
28 #include <ctype.h>
29 #include <stdlib.h>
30 #include <limits.h>
31 #include <stdio.h>
32 #include <syslog.h>
33 #include <bsm/devalloc.h>
34 #include <sys/audio.h>
35 #include <sys/soundcard.h>
36 #include <unistd.h>
38 #define MAX_AUDIO_LINK 100
39 #define RE_SIZE 64
41 static void check_audio_link(di_node_t anynode, char *secondary_link,
42 const char *primary_link_format);
44 static int audio_process(di_minor_t minor, di_node_t node);
45 static int sndstat_process(di_minor_t minor, di_node_t node);
47 static devfsadm_create_t audio_cbt[] = {
48 { "audio", "ddi_audio", NULL,
49 TYPE_EXACT, ILEVEL_0, audio_process
51 { "pseudo", "ddi_pseudo", "audio",
52 TYPE_EXACT|DRV_EXACT, ILEVEL_0, sndstat_process
56 DEVFSADM_CREATE_INIT_V0(audio_cbt);
59 * the following can't be one big RE with a bunch of alterations "|"
60 * because recurse_dev_re() would not work.
62 static devfsadm_remove_t audio_remove_cbt[] = {
64 * Secondary links.
67 /* /dev/audio, /dev/audioctl, /dev/dsp */
68 { "audio", "^audio$",
69 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
71 { "audio", "^audioctl$",
72 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
74 { "audio", "^dsp$",
75 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
77 { "audio", "^mixer",
78 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
80 { "audio", "^sndstat$",
81 RM_PRE|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
83 { "audio", "^mixer[0-9]+$",
84 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
86 { "audio", "^dsp[0-9]+$",
87 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
89 { "audio", "^sound/[0-9]+$",
90 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
92 { "audio", "^sound/[0-9]+ctl$",
93 RM_POST|RM_HOT|RM_ALWAYS, ILEVEL_0, devfsadm_rm_all
97 DEVFSADM_REMOVE_INIT_V0(audio_remove_cbt);
99 static di_node_t anynode;
102 minor_init(void)
104 anynode = DI_NODE_NIL;
105 return (DEVFSADM_SUCCESS);
109 minor_fini(void)
111 check_audio_link(anynode, "audio", "sound/%d");
112 check_audio_link(anynode, "audioctl", "sound/%dctl");
113 check_audio_link(anynode, "dsp", "dsp%d");
114 anynode = DI_NODE_NIL;
115 return (DEVFSADM_SUCCESS);
119 #define COPYSUB(to, from, pm, pos) (void) strncpy(to, &from[pm[pos].rm_so], \
120 pm[pos].rm_eo - pm[pos].rm_so); \
121 to[pm[pos].rm_eo - pm[pos].rm_so] = 0;
123 static void
124 send_number(long num)
126 char buf[PATH_MAX+1];
129 * This is not safe with -r.
131 if (strcmp(devfsadm_root_path(), "/") != 0)
132 return;
134 (void) snprintf(buf, sizeof (buf), "/dev/mixer%ld", num);
135 if (device_exists(buf)) {
136 int fd;
138 if ((fd = open(buf, O_RDWR)) < 0)
139 return;
141 (void) ioctl(fd, SNDCTL_SUN_SEND_NUMBER, &num);
142 (void) close(fd);
143 devfsadm_print(CHATTY_MID,
144 "sent devnum audio %ld to %s\n", num, buf);
148 static int
149 sndstat_process(di_minor_t minor, di_node_t node)
151 char *mn;
153 mn = di_minor_name(minor);
154 anynode = node;
157 * "Special" handling for /dev/sndstat and /dev/mixer.
159 if (strcmp(mn, "sound,sndstat0") == 0) {
160 (void) devfsadm_mklink("sndstat", node, minor, 0);
161 (void) devfsadm_secondary_link("mixer", "sndstat", 0);
164 return (DEVFSADM_CONTINUE);
168 * This function is called for every audio node.
169 * Calls enumerate to assign a logical unit id, and then
170 * devfsadm_mklink to make the link.
172 static int
173 audio_process(di_minor_t minor, di_node_t node)
175 int flags = 0;
176 char devpath[PATH_MAX + 1];
177 char newpath[PATH_MAX + 1];
178 char *buf;
179 char *mn;
180 char *tmp;
181 char *ep;
182 char re_string[RE_SIZE+1];
183 devfsadm_enumerate_t rules[1] = {NULL};
184 char base[PATH_MAX + 1];
185 char linksrc[PATH_MAX + 1];
186 char linkdst[PATH_MAX + 1];
187 long num;
188 long inst;
189 int i;
190 char *driver;
192 anynode = node;
193 mn = di_minor_name(minor);
195 if ((tmp = di_devfs_path(node)) == NULL) {
196 return (DEVFSADM_CONTINUE);
198 (void) snprintf(devpath, sizeof (devpath), "%s:%s", tmp, mn);
199 di_devfs_path_free(tmp);
201 if (strncmp(mn, "sound,", sizeof ("sound,") - 1) != 0) {
202 devfsadm_errprint("SUNW_audio_link: "
203 "can't find match for'%s'\n", mn);
204 return (DEVFSADM_CONTINUE);
207 /* strlen("sound,") */
208 (void) strlcpy(base, mn + 6, sizeof (base));
209 mn = base;
211 driver = di_driver_name(node);
213 /* if driver name override in minor name */
214 if ((tmp = strchr(mn, ',')) != NULL) {
215 driver = mn;
216 *tmp = '\0';
217 mn = tmp + 1;
220 /* skip past "audio" portion of the minor name */
221 if (strncmp(mn, "audio", sizeof ("audio") - 1) == 0) {
222 mn += sizeof ("audio") - 1;
225 /* parse the instance number */
226 for (i = strlen(mn); i; i--) {
227 if (!isdigit(mn[i - 1]))
228 break;
230 inst = strtol(mn + i, &ep, 10);
231 mn[i] = 0; /* lop off the instance number */
234 * First we create a node with the driver under /dev/sound.
235 * Note that "instance numbers" used by the audio framework
236 * are guaranteed to be unique for each driver.
238 (void) snprintf(newpath, sizeof (newpath), "sound/%s:%d%s",
239 driver, inst, mn);
240 (void) devfsadm_mklink(newpath, node, minor, flags);
243 * The rest of this logic is a gross simplification that is
244 * made possible by the fact that each audio node will have
245 * several different minors associated with it. Rather than
246 * processing each node separately, we just create the links
247 * all at once.
249 * This reduces the chances of the various links being out of
250 * sync with each other.
252 if (strcmp(mn, "mixer") != 0) {
253 return (DEVFSADM_CONTINUE);
257 * Its the control node, so create the various
258 * secondary links.
262 * We want a match against the physical path
263 * without the minor name component.
265 (void) snprintf(re_string, RE_SIZE, "%s", "^mixer([0-9]+)");
266 rules[0].re = re_string;
267 rules[0].subexp = 1;
268 rules[0].flags = MATCH_ALL;
271 * enumerate finds the logical audio id, and stuffs
272 * it in buf
274 (void) strlcpy(devpath, newpath, sizeof (devpath));
275 if (devfsadm_enumerate_int(devpath, 0, &buf, rules, 1)) {
276 return (DEVFSADM_CONTINUE);
278 num = strtol(buf, &ep, 10);
279 free(buf);
281 /* /dev/sound/0 */
282 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ld",
283 driver, inst);
284 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ld", num);
285 (void) devfsadm_secondary_link(linkdst, linksrc, flags);
287 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldctl",
288 driver, inst);
289 (void) snprintf(linkdst, sizeof (linkdst), "sound/%ldctl", num);
290 (void) devfsadm_secondary_link(linkdst, linksrc, flags);
292 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%lddsp",
293 driver, inst);
294 (void) snprintf(linkdst, sizeof (linkdst), "dsp%ld", num);
295 (void) devfsadm_secondary_link(linkdst, linksrc, flags);
297 (void) snprintf(linksrc, sizeof (linksrc), "sound/%s:%ldmixer",
298 driver, inst);
299 (void) snprintf(linkdst, sizeof (linkdst), "mixer%ld", num);
300 (void) devfsadm_secondary_link(linkdst, linksrc, flags);
302 /* Send control number */
303 send_number(num);
305 return (DEVFSADM_CONTINUE);
308 static void
309 check_audio_link(di_node_t anynode, char *secondary, const char *primary_format)
311 char primary[PATH_MAX + 1];
312 int i;
313 int flags = 0;
315 /* if link is present, return */
316 if (devfsadm_link_valid(anynode, secondary) == DEVFSADM_TRUE) {
317 return;
320 for (i = 0; i < MAX_AUDIO_LINK; i++) {
321 (void) sprintf(primary, primary_format, i);
322 if (devfsadm_link_valid(anynode, primary) == DEVFSADM_TRUE) {
323 /* we read link to get it to the master "real" link */
324 (void) devfsadm_secondary_link(secondary,
325 primary, flags);
326 break;