/*
* 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
* 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 <sys/ddi_impldefs.h>
#include <sys/ndi_impldefs.h>
#include "ttymux_dacf.h"
#pragma weak find_platform_consoles
(find_platform_consoles != NULL \
/*
* External functions
*/
extern void ttymux_dprintf(int l, const char *fmt, ...);
extern ihandle_t prom_stdin_ihandle();
extern ihandle_t prom_stdout_ihandle();
/*
* Dacf entry points
*/
/*
* Internal functions
*/
{ DACF_OPID_END, NULL },
};
{ "ttymux_config", ttymuxconfig_op },
};
};
&mod_dacfops, /* Type of module */
"ttymux DACF",
};
};
/*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
};
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
{
}
static int
{
int rval;
if (bytecnt)
return (rval);
}
/*
* How many consoles are actually linked underneath the Solaris console
* multiplexer.
*/
static int
{
icnt += 1;
ocnt += 1;
cnt += 1;
}
return (cnt);
}
/*
* Before linking a device underneath a serial multiplexer check that
* its minor node type is supported.
*/
static boolean_t
{
int circ;
char *const *nodetype;
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
*/
return (B_FALSE);
}
/*
* check the OBP device_type property first
* its a good bet that it will be compatible
* if it has the value serial.
*/
PROP_LEN_AND_VAL_BUF, 0, "device_type",
compatible = B_TRUE;
} else {
for (nodetype =
(char *const *)&supported_types[0];
nodetype++) {
mdp->ddm_node_type) == 0) {
compatible = B_TRUE;
break;
}
}
}
break;
}
}
/*
* The current version of the implementation has only been tested
* with a serial multiplexer.
*/
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
{
"WARNING: forth buffer size is too small.\n");
return (0);
}
prom_interpret(fstr, 0, 0, 0, 0, 0);
return (numfound);
}
/*
* Read an OBP property and return the result in propval.
* The caller is responsible for freeing the memory.
*/
static int
{
if (node == OBP_BADNODE ||
return (proplen);
return (proplen);
}
/*
* Parse a white space separated list of tokens and call
* the input action with each parsed token.
*/
static void
{
if (p == 0 || *p == 0)
return;
e = p + strlen(p);
do {
switch (*p) {
case ' ':
case '\t':
*p = 0;
*p = ' ';
}
break;
default:
tok = p;
}
break;
}
} while (++p < e);
}
/*
* Search for a console structure matching a device path.
* Return a new initialized structure if one does not exist.
*/
{
int j;
break;
}
if (j == ms->sm_cons_cnt) {
if (j + 1 == TTYMUX_MAX_LINKS) {
} else {
}
}
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
{
char *cpath;
if (*path == '/') {
return;
}
/*
* Device paths should have a minor name - if its missing assume
* it should be :a!
*/
char *p;
cpath = p;
}
} else {
" consoles - ignoring %s\n", cpath);
}
}
/*
* Discover which consoles OBP is using.
*/
static int
{
int i, cnt;
char *devpath;
else
return (EINVAL);
for (i = 0; i < cnt; i++) {
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).
*/
" consoles - ignoring %s\n", devpath);
continue;
}
else
}
}
return (0);
}
/*
* Convert a file system path into a dev_t
*/
static dev_t
{
if (fspath == 0 ||
return (NODEV);
} else {
return (dev);
}
}
/*
* Convert a device tree path into a dev_t
*/
static dev_t
{
return (NODEV);
return (dev);
}
static int
{
int rv;
/* create a vnode for the device and open it */
goto out2;
}
/* Associate a file pointer with the vnode */
goto out1;
}
/* Allocate a file descriptor (any non-negative integer will suffice) */
goto out1;
}
/* associate the file pointer with the fd */
return (0);
out1:
out2:
return (rv);
}
/*
* Plumb a device specified by the sm_console_t argument underneath the
* serial multiplexer indicated by the vnode_t argument.
*/
static int
{
int lfd;
/* get an open vnode for the device */
return (rv);
/*
* Enable the receiver on the lower device since it will
* be used by OBP.
*/
}
if (rv != 0)
"DACF: Failed to enable console receiver [error %d]\n", rv);
/*
* Pop all the modules off the stream prior to linking it.
*/
do {
} while (rv == 0);
"Failed to pop all modules: error %d", rv);
goto out;
}
"Failed to link device: error %d", rv);
goto out;
}
/* close the linked device */
/*
* Now tell the mux to associate the new stream
*/
assoc.ttymux_tag = 0;
"Failed to associate %d:%d with the console\n",
"Can't unlink %d:%d - Closing vnode\n",
}
return (rv);
out:
return (rv);
}
static int
{
return (0); /* already linked */
else
}
/*
* 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
{
uint_t j;
continue;
"Cannot find a driver for device: %s\n",
continue;
}
/*
* Refuse requests to use devices as consoles which have an
* unsupported minor node type.
*/
continue;
/*
* Enable a console device by linking the target console
* underneath the ttymux minor node that has been specified
*/
}
return (0);
}
static int
{
int len;
char *propval;
/*
* Look for target consoles based on options node properties
*/
node = prom_optionsnode();
}
}
/*
* Look for platform specific target consoles.
* Assume that they are OBP consoles and used for both input and output.
*/
}
/*
* Discover which consoles OBP is actually using according to
* interfaces proposed by case number FWARC/262.
*/
}
ms->sm_cons_cnt);
return (ms->sm_cons_cnt);
}
static int
{
return (EINVAL);
return (EINVAL);
return (EINVAL);
return (EINVAL); /* minor 0 is special */
return (EAGAIN); /* already configured */
opath = prom_stdoutpath();
ipath = prom_stdinpath();
*mode = 0;
return (EINVAL);
"Warning: multiplexer is not the output device\n");
}
"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
{
int rv;
return (DACF_SUCCESS);
}
for (i = 0; i < TTYMUX_MAX_LINKS; i++)
if (prom_is_openprom()) {
}
}
}
/* Store the console list for use by the ttymux driver */
for (i = 0; i < ms->sm_cons_cnt; i++) {
}
return (DACF_FAILURE);
}
} else {
"Error %d opening the console device\n", rv);
return (DACF_FAILURE);
}
return (DACF_SUCCESS);
}