/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ /* * This is a dacf module based upon the Extensions to Device Autoconfiguration * project. See PSARC/1998/212 for more details. * * This module provides the dacf functions * to be called after a driver has attached and before it detaches. * The post attach functionality is used to autoconfigure a serial console * multiplexer if the OBP console is a multiplexer. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "ttymux_dacf.h" #pragma weak find_platform_consoles extern char *find_platform_consoles(sm_mux_state_t *_m, dev_info_t *_di, dev_t _d, uint_t _f); #define platform_consoles(_m, _di, _d, _f) \ (find_platform_consoles != NULL \ ? find_platform_consoles(_m, _di, _d, _f) \ : (nulldev(_m, _di, _d, _f), (char *)0)) /* * External functions */ extern uintptr_t space_fetch(char *key); extern int space_store(char *key, uintptr_t ptr); extern void ttymux_dprintf(int l, const char *fmt, ...); extern int prom_ihandle_to_path(ihandle_t, char *, uint_t); extern void prom_interpret(char *, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t); extern ihandle_t prom_stdin_ihandle(); extern ihandle_t prom_stdout_ihandle(); extern vnode_t *rconsvp; /* redirection device */ /* * Dacf entry points */ static int ttymux_config(dacf_infohdl_t, dacf_arghdl_t, int); /* * Internal functions */ static dacf_op_t ttymuxconfig_op[] = { { DACF_OPID_POSTATTACH, ttymux_config }, { DACF_OPID_END, NULL }, }; static dacf_opset_t opsets[] = { { "ttymux_config", ttymuxconfig_op }, { NULL, NULL } }; struct dacfsw dacfsw = { DACF_MODREV_1, opsets, }; struct modldacf modldacf = { &mod_dacfops, /* Type of module */ "ttymux DACF", &dacfsw }; struct modlinkage modlinkage = { MODREV_1, (void *)&modldacf, NULL }; /*LINTLIBRARY*/ /* * The following minor nodes can be linked underneath the serial * console multiplexer. * These are the only ones currently tested. * (NOTE: Devices of device_type serial are also allowed to be used as * additional consoles). * Disallow plumbing of untested node types. */ static const char * const supported_types[] = { DDI_NT_SERIAL, (char *const)NULL }; #define OFLAGS FREAD|FWRITE|FNOCTTY|FNONBLOCK #define INPUT_ALIAS "multiplexer-input-devices" #define OUTPUT_ALIAS "multiplexer-output-devices" #define OBPDEV 0x100 #define FORTH_STRINGLEN 1024 #define MUXDEVTYPE "SUNW,serial-multiplexer" static char fth_fmt[] = "\" get-device-list\" " /* ( method-str method-len ) */ "h# %p " /* ( method-str method-len ihandle ) */ "$call-method " /* ( ihandle_n-1 ... ihandle n ) */ "dup " /* ( ihandle_n-1 ... ihandle n n ) */ "h# %p " /* ( ihandle_n-1 ... ihandle n n numfound ) */ "l! " /* ( ihandle_n-1 ... ihandle n ) */ "0 " /* ( ihandle_n-1 ... ihandle n 0 ) */ "do " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */ " i " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index ) */ " h# %x " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i index max) */ " < if " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i ) */ " h# %p " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf ) */ " i " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf index) */ " 4 * + " /* ( ihandle_n-1 ... ihandle_i+1 ihandle_i buf' ) */ " l! " /* ( ihandle_n-1 ... ihandle_i+1 ) */ " else " /* */ " drop " /* ( ihandle_n-1 ... ihandle_i+1 ) */ " then " /* */ "loop "; /* ( ihandle_n-1 ... ihandle_i+1 ) */ int _init(void) { return (mod_install(&modlinkage)); } int _fini() { return (mod_remove(&modlinkage)); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int ioctl_cmd(vnode_t *avp, int cmd, void *data, int datasize, int *bytecnt) { struct strioctl ios; int rval; ios.ic_timout = 0; ios.ic_cmd = cmd; ios.ic_dp = (char *)data; ios.ic_len = datasize; rval = kstr_ioctl(avp, I_STR, (intptr_t)&ios); if (bytecnt) *bytecnt = ios.ic_len; return (rval); } /* * How many consoles are actually linked underneath the Solaris console * multiplexer. */ static int usable_consoles(sm_mux_state_t *sp, uint_t *iconsoles, uint_t *oconsoles) { uint_t j, cnt, icnt = 0u, ocnt = 0u; mutex_enter(&sp->sm_cons_mutex); for (j = 0, cnt = 0; j < sp->sm_cons_cnt; j++) if (sp->sm_cons_links[j].sm_muxid != 0) { sm_console_t *cn = &sp->sm_cons_links[j]; if (cn->sm_mode & FORINPUT) icnt += 1; if (cn->sm_mode & FOROUTPUT) ocnt += 1; if (cn->sm_mode == FORIO) cnt += 1; } mutex_exit(&sp->sm_cons_mutex); *iconsoles = icnt; *oconsoles = ocnt; return (cnt); } /* * Before linking a device underneath a serial multiplexer check that * its minor node type is supported. */ static boolean_t compatible_console(dev_t dev) { int circ; boolean_t compatible; char *const *nodetype; struct ddi_minor_data *dmdp; dev_info_t *dip; char devtype[32]; int len; /* * Find the node nodetype to verify that the current version of * the code supports its use as a console * Supported types are listed in the array supported_types */ if ((dip = e_ddi_hold_devi_by_dev(dev, 0)) == NULL) { ttymux_dprintf(DPRINT_L2, "No dip for %d:%d\n", getmajor(dev), getminor(dev)); return (B_FALSE); } compatible = B_FALSE; len = sizeof (devtype); ndi_devi_enter(dip, &circ); for (dmdp = DEVI(dip)->devi_minor; dmdp != NULL; dmdp = dmdp->next) { struct ddi_minor_data *mdp = dmdp; if (mdp->ddm_dev == dev) { ttymux_dprintf(DPRINT_L0, "compat: matched dev\n"); /* * check the OBP device_type property first * its a good bet that it will be compatible * if it has the value serial. */ if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0, "device_type", (caddr_t)devtype, &len) == DDI_PROP_SUCCESS && strcmp(devtype, "serial") == 0) { compatible = B_TRUE; } else { for (nodetype = (char *const *)&supported_types[0]; *nodetype != (char *const)NULL; nodetype++) { if (strcmp(*nodetype, mdp->ddm_node_type) == 0) { compatible = B_TRUE; break; } } } break; } } ndi_devi_exit(dip, circ); ddi_release_devi(dip); /* * The current version of the implementation has only been tested * with a serial multiplexer. */ ttymux_dprintf(DPRINT_L0, "%d:%d is %s\n", getmajor(dev), getminor(dev), (compatible) ? "compatible" : "incompatible"); return (compatible); } /* * get-device-list ( -- [ihandle n-1, ... ihandle], n ) * Call the "get-device-list" method of an OBP device. * ihdl - ihandle of the OBP device whose method is to be called * ihdls - array of ihandles returned to the caller * maxi - length of the ihdls array */ static int get_device_list(ihandle_t ihdl, ihandle_t *ihdls, size_t maxi) { int numfound = -1; char fstr[FORTH_STRINGLEN]; if (snprintf(fstr, FORTH_STRINGLEN, fth_fmt, (caddr32_t)ihdl, &numfound, maxi, ihdls) > FORTH_STRINGLEN) { ttymux_dprintf(DPRINT_L3, "WARNING: forth buffer size is too small.\n"); return (0); } prom_interpret(fstr, 0, 0, 0, 0, 0); ttymux_dprintf(DPRINT_L0, "ihdl 0x%p cnt %d\n", (caddr32_t)ihdl, numfound); return (numfound); } /* * Read an OBP property and return the result in propval. * The caller is responsible for freeing the memory. */ static int read_prop(pnode_t node, char *propname, char **propval) { int proplen = -1; if (node == OBP_BADNODE || (proplen = prom_getproplen(node, propname)) <= 0) return (proplen); *propval = kmem_zalloc(proplen + 1, KM_SLEEP); (void) prom_getprop(node, propname, *propval); return (proplen); } /* * Parse a white space separated list of tokens and call * the input action with each parsed token. */ static void parse(sm_mux_state_t *ms, char *p, void (*action)(sm_mux_state_t *, char *, void *), void *arg) { char *e, *tok = NULL; if (p == 0 || *p == 0) return; e = p + strlen(p); do { switch (*p) { case ' ': case '\t': if (tok != NULL) { *p = 0; action(ms, tok, arg); tok = NULL; *p = ' '; } break; default: if (tok == NULL) { tok = p; } break; } } while (++p < e); if (tok != NULL) action(ms, tok, arg); } /* * Search for a console structure matching a device path. * Return a new initialized structure if one does not exist. */ sm_console_t * get_aconsole(sm_mux_state_t *ms, char *path) { sm_console_t *cn; int j; for (cn = ms->sm_cons_links, j = 0; j < ms->sm_cons_cnt; cn++, j++) { if (cn->sm_path && strcmp(cn->sm_path, path) == 0) break; } if (j == ms->sm_cons_cnt) { if (j + 1 == TTYMUX_MAX_LINKS) { cn = NULL; } else { bzero((caddr_t)cn, sizeof (*cn)); ms->sm_cons_cnt += 1; } } return (cn); } /* * Create a new console structure representing the device * identified by path. The void * argument indicates which I/O * mode the device will support. */ static void add_aconsole(sm_mux_state_t *ms, char *path, void *arg) { sm_console_t *cn; char *cpath; if (*path == '/') { cpath = kmem_alloc(strlen(path) + 1, KM_SLEEP); (void) strcpy(cpath, path); } else if (read_prop(prom_alias_node(), path, &cpath) <= 0) { return; } /* * Device paths should have a minor name - if its missing assume * it should be :a! */ if (strrchr(cpath, ':') == NULL) { char *p; size_t len = strlen(cpath) + 1; p = kmem_zalloc(len + 2, KM_SLEEP); (void) strcpy(p, cpath); (void) strcat(p, ":a"); /* assume :a ! */ kmem_free(cpath, len); cpath = p; } if ((cn = get_aconsole(ms, cpath)) != NULL) { cn->sm_obp_con = ((uint_t)(uintptr_t)arg & OBPDEV) ? B_TRUE : B_FALSE; cn->sm_mode |= (io_mode_t)((uint_t)(uintptr_t)arg & FORIO); if (cn->sm_path != NULL) kmem_free(cn->sm_path, strlen(cn->sm_path) + 1); cn->sm_path = cpath; } else { ttymux_dprintf(DPRINT_L3, "Too many " " consoles - ignoring %s\n", cpath); kmem_free(cpath, strlen(cpath) + 1); } } /* * Discover which consoles OBP is using. */ static int find_obp_consoles(sm_mux_state_t *ms, io_mode_t mode) { sm_console_t *cn; int i, cnt; char *devpath; ihandle_t ihdls[TTYMUX_MAX_LINKS]; ihandle_t stdihdl; if (mode == FORINPUT) stdihdl = ms->sm_cons_stdin.sm_i_ihdl; else if (mode == FOROUTPUT) stdihdl = ms->sm_cons_stdout.sm_o_ihdl; else return (EINVAL); devpath = kmem_alloc(MAXPATHLEN+2, KM_SLEEP); cnt = get_device_list(stdihdl, ihdls, TTYMUX_MAX_LINKS); for (i = 0; i < cnt; i++) { if (prom_ihandle_to_path(ihdls[i], devpath, MAXPATHLEN) == 0) continue; /* * If the minor name is not part of the path and there is * more than one minor node then ddi_pathname_to_dev_t * can fail to resolve the path correctly (it's an OBP * problem)!!! If there's no minor name then assume the default * minor name (:a). */ if (strrchr(devpath, ':') == NULL) (void) strcat(devpath, ":a"); /* assume :a ! */ if ((cn = get_aconsole(ms, devpath)) == 0) { ttymux_dprintf(DPRINT_L3, "Too many " " consoles - ignoring %s\n", devpath); continue; } cn->sm_mode |= mode; cn->sm_obp_con = B_TRUE; if (mode == FORINPUT) cn->sm_i_ihdl = ihdls[i]; else cn->sm_o_ihdl = ihdls[i]; if (cn->sm_path == NULL) { cn->sm_path = kmem_alloc(strlen(devpath) + 1, KM_SLEEP); (void) strcpy(cn->sm_path, devpath); } } kmem_free(devpath, MAXPATHLEN + 2); return (0); } /* * Convert a file system path into a dev_t */ static dev_t fs_devtype(char *fspath) { vnode_t *vp = NULL; dev_t dev; if (fspath == 0 || vn_open(fspath, UIO_SYSSPACE, FREAD, 0, &vp, 0, 0) != 0) { return (NODEV); } else { dev = vp->v_rdev; VOP_CLOSE(vp, FREAD, 1, (offset_t)0, CRED(), NULL); VN_RELE(vp); return (dev); } } /* * Convert a device tree path into a dev_t */ static dev_t di_devtype(char *path) { dev_t dev; if (path == 0 || *path == 0) return (NODEV); ttymux_dprintf(DPRINT_L0, "loading device %s\n", path); dev = ddi_pathname_to_dev_t(path); return (dev); } static int open_stream(vnode_t **vp, int *fd, dev_t dev) { file_t *fp; int rv; /* create a vnode for the device and open it */ *vp = makespecvp(dev, VCHR); if ((rv = VOP_OPEN(vp, FREAD+FWRITE+FNOCTTY, CRED(), NULL)) != 0) { goto out2; } /* Associate a file pointer with the vnode */ if ((rv = falloc(*vp, FREAD+FWRITE+FNOCTTY, &fp, NULL)) != 0) { goto out1; } mutex_exit(&fp->f_tlock); /* must be called single threaded */ /* Allocate a file descriptor (any non-negative integer will suffice) */ if ((*fd = ufalloc(0)) == -1) { rv = EMFILE; goto out1; } /* associate the file pointer with the fd */ setf(*fd, fp); return (0); out1: VOP_CLOSE(*vp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); out2: VN_RELE(*vp); return (rv); } /* * Plumb a device specified by the sm_console_t argument underneath the * serial multiplexer indicated by the vnode_t argument. */ static int link_aconsole(vnode_t *mux_avp, sm_console_t *cn) { vnode_t *lvp; int lfd; int rv, rval; ttymux_assoc_t assoc; struct termios tc; ASSERT(cn->sm_path); /* get an open vnode for the device */ if ((rv = open_stream(&lvp, &lfd, cn->sm_dev)) != 0) return (rv); /* * Enable the receiver on the lower device since it will * be used by OBP. */ if ((rv = ioctl_cmd(lvp, TCGETS, &tc, sizeof (tc), 0)) == 0) { tc.c_cflag |= CREAD; rv = ioctl_cmd(lvp, TCSETS, &tc, sizeof (tc), 0); } if (rv != 0) ttymux_dprintf(DPRINT_L3, "DACF: Failed to enable console receiver [error %d]\n", rv); /* * Pop all the modules off the stream prior to linking it. */ do { rv = strioctl(lvp, I_POP, 0, 0, K_TO_K, CRED(), &rval); } while (rv == 0); if (rv != EINVAL) { ttymux_dprintf(DPRINT_L3, "Failed to pop all modules: error %d", rv); goto out; } if ((rv = strioctl(mux_avp, I_PLINK, (intptr_t)lfd, FREAD+FWRITE+FNOCTTY, K_TO_K, CRED(), &(cn->sm_muxid))) != 0) { ttymux_dprintf(DPRINT_L3, "Failed to link device: error %d", rv); goto out; } /* close the linked device */ (void) closeandsetf(lfd, NULL); /* * Now tell the mux to associate the new stream */ assoc.ttymux_udev = mux_avp->v_rdev; assoc.ttymux_ldev = cn->sm_dev; assoc.ttymux_linkid = cn->sm_muxid; assoc.ttymux_tag = 0; assoc.ttymux_ioflag = cn->sm_mode; if ((rv = ioctl_cmd(mux_avp, TTYMUX_ASSOC, (void *)&assoc, sizeof (assoc), 0)) != 0) { ttymux_dprintf(DPRINT_L3, "Failed to associate %d:%d with the console\n", getmajor(cn->sm_dev), getminor(cn->sm_dev)); if (strioctl(mux_avp, I_PUNLINK, (intptr_t)cn->sm_muxid, 0, K_TO_K, CRED(), &rval) != 0) ttymux_dprintf(DPRINT_L3, "Can't unlink %d:%d - Closing vnode\n", getmajor(cn->sm_dev), getminor(cn->sm_dev)); } return (rv); out: VOP_CLOSE(lvp, FREAD+FWRITE+FNOCTTY, 1, (offset_t)0, CRED(), NULL); VN_RELE(lvp); return (rv); } static int enable_aconsole(sm_mux_state_t *ms, sm_console_t *cn, vnode_t *muxvp) { ttymux_assoc_t assoc; ASSERT(cn && cn->sm_dev != NODEV); assoc.ttymux_ldev = cn->sm_dev; cn->sm_muxid = (ioctl_cmd(muxvp, TTYMUX_GETLINK, (void *)&assoc, sizeof (assoc), 0) == 0) ? assoc.ttymux_linkid : 0; if (cn->sm_muxid != 0) return (0); /* already linked */ else return (link_aconsole(muxvp, cn)); } /* * Enable all discovered consoles such that they can provide real I/O. * The discovered list is stored in the sm_mux_state_t pointer. */ static int enable_all_consoles(sm_mux_state_t *ms, vnode_t *muxvp) { sm_console_t *cn; uint_t j; ttymux_dprintf(DPRINT_L0, "Enable %d devices\n", ms->sm_cons_cnt); for (cn = ms->sm_cons_links, j = 0; j < ms->sm_cons_cnt; cn++, j++) { if (cn->sm_path == NULL) continue; if ((strstr(cn->sm_path, "/dev") == cn->sm_path && (cn->sm_dev = fs_devtype(cn->sm_path)) == NODEV) || (cn->sm_dev = di_devtype(cn->sm_path)) == NODEV) { ttymux_dprintf(DPRINT_L0, "Cannot find a driver for device: %s\n", cn->sm_path ? cn->sm_path : ""); continue; } ttymux_dprintf(DPRINT_L0, "Enabling %d:%d\n", getmajor(cn->sm_dev), getminor(cn->sm_dev)); /* * Refuse requests to use devices as consoles which have an * unsupported minor node type. */ if (compatible_console(cn->sm_dev) == B_FALSE) continue; /* * Enable a console device by linking the target console * underneath the ttymux minor node that has been specified * in a DACF reservation (see /etc/dacf.conf). */ (void) enable_aconsole(ms, cn, muxvp); } return (0); } static int find_consoles(sm_mux_state_t *ms, dev_info_t *dip, dev_t dev) { int len; char *propval; char devtype[32]; pnode_t node; uint_t flags; /* * Look for target consoles based on options node properties */ node = prom_optionsnode(); if ((len = read_prop(node, INPUT_ALIAS, &propval)) > 0) { parse(ms, propval, add_aconsole, (void *)FORINPUT); kmem_free(propval, len + 1); } if ((len = read_prop(node, OUTPUT_ALIAS, &propval)) > 0) { parse(ms, propval, add_aconsole, (void *)FOROUTPUT); kmem_free(propval, len + 1); } /* * Look for platform specific target consoles. * Assume that they are OBP consoles and used for both input and output. */ flags = (uint_t)FORIO | OBPDEV; if ((propval = platform_consoles(ms, dip, dev, flags)) != NULL) { parse(ms, propval, add_aconsole, (void *)(uintptr_t)flags); kmem_free(propval, strlen(propval) + 1); } /* * Discover which consoles OBP is actually using according to * interfaces proposed by case number FWARC/262. */ len = sizeof (devtype); if (ddi_prop_op(DDI_DEV_T_ANY, dip, PROP_LEN_AND_VAL_BUF, 0, "device_type", (caddr_t)devtype, &len) == DDI_PROP_SUCCESS && strcmp(devtype, "serial") == 0 && ddi_prop_exists(DDI_DEV_T_ANY, dip, 0, MUXDEVTYPE) == 1) { (void) find_obp_consoles(ms, FORINPUT); (void) find_obp_consoles(ms, FOROUTPUT); } ttymux_dprintf(DPRINT_L0, "%d consoles configured\n", ms->sm_cons_cnt); return (ms->sm_cons_cnt); } static int validate_reservation(dacf_infohdl_t di, dev_info_t **dip, dev_t *dev, io_mode_t *mode) { char *dname, *nname, *ipath, *opath; if ((dname = (char *)dacf_driver_name(di)) == NULL) return (EINVAL); if ((*dip = dacf_devinfo_node(di)) == NULL) return (EINVAL); *dev = makedevice(ddi_driver_major(*dip), dacf_minor_number(di)); if (*dev == NODEV || *dip == NULL || strcmp(dname, TTYMUX_DRVNAME) != 0) return (EINVAL); else if (getminor(*dev) != (minor_t)0) return (EINVAL); /* minor 0 is special */ else if (rconsvp != NULL || space_fetch(TTYMUXPTR) != NULL) return (EAGAIN); /* already configured */ opath = prom_stdoutpath(); ipath = prom_stdinpath(); nname = ddi_node_name(*dip); *mode = 0; if (ipath != NULL && strstr(ipath, nname) != 0) *mode = FORINPUT; if (opath != NULL && strstr(opath, nname) != 0) *mode |= FOROUTPUT; if ((*mode & FORIO) == 0) return (EINVAL); if ((*mode & FOROUTPUT) == 0) { ttymux_dprintf(DPRINT_L3, "Warning: multiplexer is not the output device\n"); } if ((*mode & FORINPUT) == 0) { ttymux_dprintf(DPRINT_L3, "Warning: multiplexer is not the input device\n"); } return (0); } /* * This operation set is for configuring the ttymux driver for use as * the system console. * It must run before consconfig configures the Solaris console. */ /*ARGSUSED*/ static int ttymux_config(dacf_infohdl_t info_hdl, dacf_arghdl_t arg_hdl, int flags) { sm_mux_state_t *ms; io_mode_t mode; dev_t dev; dev_info_t *dip; uint_t i, icnt = 0, ocnt = 0; int rv; vnode_t *muxvp; ttymux_dprintf(DPRINT_L0, "\n"); if ((rv = validate_reservation(info_hdl, &dip, &dev, &mode)) != 0) { ttymux_dprintf(DPRINT_L0, "reservation ignored (%d)\n", rv); return (DACF_SUCCESS); } ms = kmem_zalloc(sizeof (*ms), KM_SLEEP); mutex_init(&ms->sm_cons_mutex, NULL, MUTEX_DRIVER, NULL); for (i = 0; i < TTYMUX_MAX_LINKS; i++) ms->sm_cons_links[i].sm_dev = NODEV; ms->sm_cons_stdin.sm_dev = ms->sm_cons_stdout.sm_dev = NODEV; if (mode & FORINPUT) ms->sm_cons_stdin.sm_dev = dev; if (mode & FOROUTPUT) ms->sm_cons_stdout.sm_dev = dev; ms->sm_cons_stdin.sm_i_ihdl = prom_stdin_ihandle(); ms->sm_cons_stdout.sm_o_ihdl = prom_stdout_ihandle(); if (prom_is_openprom()) { pnode_t node = prom_optionsnode(); if (prom_getproplen(node, INPUT_ALIAS) > 0) { ms->sm_ialias = kmem_alloc( strlen(INPUT_ALIAS) + 1, KM_SLEEP); (void) strcpy(ms->sm_ialias, INPUT_ALIAS); } if (prom_getproplen(node, OUTPUT_ALIAS) > 0) { ms->sm_oalias = kmem_alloc( strlen(OUTPUT_ALIAS) + 1, KM_SLEEP); (void) strcpy(ms->sm_oalias, OUTPUT_ALIAS); } } (void) find_consoles(ms, dip, dev); /* Store the console list for use by the ttymux driver */ if (space_store(TTYMUXPTR, (uintptr_t)ms) != 0) { ttymux_dprintf(DPRINT_L3, "Named pointer error\n"); if (ms->sm_ialias) kmem_free(ms->sm_ialias, strlen(ms->sm_ialias) + 1); if (ms->sm_oalias) kmem_free(ms->sm_oalias, strlen(ms->sm_oalias) + 1); for (i = 0; i < ms->sm_cons_cnt; i++) { sm_console_t *cn = &ms->sm_cons_links[i]; if (cn->sm_path) kmem_free(cn->sm_path, strlen(cn->sm_path) + 1); } kmem_free(ms, sizeof (*ms)); return (DACF_FAILURE); } muxvp = dacf_makevp(info_hdl); if ((rv = VOP_OPEN(&muxvp, OFLAGS, CRED(), NULL)) == 0) { (void) enable_all_consoles(ms, muxvp); (void) usable_consoles(ms, &icnt, &ocnt); VOP_CLOSE(muxvp, OFLAGS, 1, (offset_t)0, CRED(), NULL); VN_RELE(muxvp); } else { ttymux_dprintf(DPRINT_L3, "Error %d opening the console device\n", rv); VN_RELE(muxvp); return (DACF_FAILURE); } if (icnt == 0 && (mode & FORINPUT)) ttymux_dprintf(DPRINT_L3, "No input consoles configured.\n"); if (ocnt == 0 && (mode & FOROUTPUT)) ttymux_dprintf(DPRINT_L3, "No output consoles configured.\n"); ttymux_dprintf(DPRINT_L0, "mux config complete\n"); return (DACF_SUCCESS); }