xref: /titanic_51/usr/src/uts/sun4/io/efcode/fc_ops.c (revision 7c478bd95313f5f23a4c958a745db2134aa03244)
1*7c478bd9Sstevel@tonic-gate /*
2*7c478bd9Sstevel@tonic-gate  * CDDL HEADER START
3*7c478bd9Sstevel@tonic-gate  *
4*7c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
5*7c478bd9Sstevel@tonic-gate  * Common Development and Distribution License, Version 1.0 only
6*7c478bd9Sstevel@tonic-gate  * (the "License").  You may not use this file except in compliance
7*7c478bd9Sstevel@tonic-gate  * with the License.
8*7c478bd9Sstevel@tonic-gate  *
9*7c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10*7c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
11*7c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
12*7c478bd9Sstevel@tonic-gate  * and limitations under the License.
13*7c478bd9Sstevel@tonic-gate  *
14*7c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
15*7c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16*7c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
17*7c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
18*7c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
19*7c478bd9Sstevel@tonic-gate  *
20*7c478bd9Sstevel@tonic-gate  * CDDL HEADER END
21*7c478bd9Sstevel@tonic-gate  */
22*7c478bd9Sstevel@tonic-gate /*
23*7c478bd9Sstevel@tonic-gate  * Copyright 2000, 2002 Sun Microsystems, Inc.  All rights reserved.
24*7c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
25*7c478bd9Sstevel@tonic-gate  */
26*7c478bd9Sstevel@tonic-gate 
27*7c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*7c478bd9Sstevel@tonic-gate 
29*7c478bd9Sstevel@tonic-gate /*
30*7c478bd9Sstevel@tonic-gate  * fc_ops.c: Framework generic fcode ops
31*7c478bd9Sstevel@tonic-gate  */
32*7c478bd9Sstevel@tonic-gate #include <sys/types.h>
33*7c478bd9Sstevel@tonic-gate #include <sys/kmem.h>
34*7c478bd9Sstevel@tonic-gate #include <sys/systm.h>
35*7c478bd9Sstevel@tonic-gate #include <sys/ddi.h>
36*7c478bd9Sstevel@tonic-gate #include <sys/sunddi.h>
37*7c478bd9Sstevel@tonic-gate #include <sys/sunndi.h>
38*7c478bd9Sstevel@tonic-gate #include <sys/modctl.h>
39*7c478bd9Sstevel@tonic-gate #include <sys/fcode.h>
40*7c478bd9Sstevel@tonic-gate #include <sys/ddi_implfuncs.h>
41*7c478bd9Sstevel@tonic-gate #include <sys/ndi_impldefs.h>
42*7c478bd9Sstevel@tonic-gate #include <sys/ethernet.h>
43*7c478bd9Sstevel@tonic-gate 
44*7c478bd9Sstevel@tonic-gate static int fco_new_device(dev_info_t *, fco_handle_t, fc_ci_t *);
45*7c478bd9Sstevel@tonic-gate static int fco_finish_device(dev_info_t *, fco_handle_t, fc_ci_t *);
46*7c478bd9Sstevel@tonic-gate static int fco_create_property(dev_info_t *, fco_handle_t, fc_ci_t *);
47*7c478bd9Sstevel@tonic-gate 
48*7c478bd9Sstevel@tonic-gate static int fco_validate(dev_info_t *, fco_handle_t, fc_ci_t *);
49*7c478bd9Sstevel@tonic-gate static int fco_invalidate(dev_info_t *, fco_handle_t, fc_ci_t *);
50*7c478bd9Sstevel@tonic-gate static int fco_exit(dev_info_t *, fco_handle_t, fc_ci_t *);
51*7c478bd9Sstevel@tonic-gate 
52*7c478bd9Sstevel@tonic-gate static int fco_getproplen(dev_info_t *, fco_handle_t, fc_ci_t *);
53*7c478bd9Sstevel@tonic-gate static int fco_getprop(dev_info_t *, fco_handle_t, fc_ci_t *);
54*7c478bd9Sstevel@tonic-gate 
55*7c478bd9Sstevel@tonic-gate static int fco_ap_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
56*7c478bd9Sstevel@tonic-gate static int fco_child(dev_info_t *, fco_handle_t, fc_ci_t *);
57*7c478bd9Sstevel@tonic-gate static int fco_peer(dev_info_t *, fco_handle_t, fc_ci_t *);
58*7c478bd9Sstevel@tonic-gate static int fco_parent(dev_info_t *, fco_handle_t, fc_ci_t *);
59*7c478bd9Sstevel@tonic-gate static int fco_alloc_phandle(dev_info_t *, fco_handle_t, fc_ci_t *);
60*7c478bd9Sstevel@tonic-gate 
61*7c478bd9Sstevel@tonic-gate static int fco_local_ether_addr(dev_info_t *, fco_handle_t, fc_ci_t *);
62*7c478bd9Sstevel@tonic-gate 
63*7c478bd9Sstevel@tonic-gate struct fc_ops_v {
64*7c478bd9Sstevel@tonic-gate 	char *svc_name;
65*7c478bd9Sstevel@tonic-gate 	fc_ops_t *f;
66*7c478bd9Sstevel@tonic-gate };
67*7c478bd9Sstevel@tonic-gate 
68*7c478bd9Sstevel@tonic-gate static struct fc_ops_v fov[] = {
69*7c478bd9Sstevel@tonic-gate 	{	"open",			fc_fail_op},
70*7c478bd9Sstevel@tonic-gate 	{	"close",		fc_fail_op},
71*7c478bd9Sstevel@tonic-gate 	{	"$find",		fc_fail_op},
72*7c478bd9Sstevel@tonic-gate 	{	"encode-unit",		fc_fail_op},
73*7c478bd9Sstevel@tonic-gate 	{	"decode-unit",		fc_fail_op},
74*7c478bd9Sstevel@tonic-gate 	{	FC_GET_MY_PROPLEN,	fco_getproplen},
75*7c478bd9Sstevel@tonic-gate 	{	FC_GET_MY_PROP,		fco_getprop},
76*7c478bd9Sstevel@tonic-gate 	{	FC_GET_PKG_PROPLEN,	fco_getproplen},
77*7c478bd9Sstevel@tonic-gate 	{	FC_GET_PKG_PROP,	fco_getprop},
78*7c478bd9Sstevel@tonic-gate 	{	FC_GET_IN_PROPLEN,	fco_getproplen},
79*7c478bd9Sstevel@tonic-gate 	{	FC_GET_IN_PROP,		fco_getprop},
80*7c478bd9Sstevel@tonic-gate 	{	FC_NEW_DEVICE,		fco_new_device},
81*7c478bd9Sstevel@tonic-gate 	{	FC_FINISH_DEVICE,	fco_finish_device},
82*7c478bd9Sstevel@tonic-gate 	{	FC_CREATE_PROPERTY,	fco_create_property},
83*7c478bd9Sstevel@tonic-gate 	{	FC_AP_PHANDLE,		fco_ap_phandle},
84*7c478bd9Sstevel@tonic-gate 	{	"child",		fco_child},
85*7c478bd9Sstevel@tonic-gate 	{	"peer",			fco_peer},
86*7c478bd9Sstevel@tonic-gate 	{	FC_PARENT,		fco_parent},
87*7c478bd9Sstevel@tonic-gate 	{	FC_ALLOC_PHANDLE,	fco_alloc_phandle},
88*7c478bd9Sstevel@tonic-gate 	{	FC_SVC_VALIDATE,	fco_validate},
89*7c478bd9Sstevel@tonic-gate 	{	FC_SVC_INVALIDATE,	fco_invalidate},
90*7c478bd9Sstevel@tonic-gate 	{	FC_SVC_EXIT,		fco_exit},
91*7c478bd9Sstevel@tonic-gate 	{	"local-ether-addr",	fco_local_ether_addr},
92*7c478bd9Sstevel@tonic-gate 	{	NULL,			NULL}
93*7c478bd9Sstevel@tonic-gate };
94*7c478bd9Sstevel@tonic-gate 
95*7c478bd9Sstevel@tonic-gate /*
96*7c478bd9Sstevel@tonic-gate  * Allocate a handle for the ops function .. our handle is a resource list
97*7c478bd9Sstevel@tonic-gate  * Return the handle to our caller, so he can call us with it when we need it.
98*7c478bd9Sstevel@tonic-gate  */
99*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
100*7c478bd9Sstevel@tonic-gate fco_handle_t
101*7c478bd9Sstevel@tonic-gate fc_ops_alloc_handle(dev_info_t *ap, dev_info_t *child,
102*7c478bd9Sstevel@tonic-gate     void *fcode, size_t fcode_size, char *unit_address, void *bus_args)
103*7c478bd9Sstevel@tonic-gate {
104*7c478bd9Sstevel@tonic-gate 	fco_handle_t rp;
105*7c478bd9Sstevel@tonic-gate 	char *up;
106*7c478bd9Sstevel@tonic-gate 
107*7c478bd9Sstevel@tonic-gate 	rp = kmem_zalloc(sizeof (struct fc_resource_list), KM_SLEEP);
108*7c478bd9Sstevel@tonic-gate 	rp->next_handle = NULL;		/* nobody is downstream */
109*7c478bd9Sstevel@tonic-gate 	rp->ap = ap;
110*7c478bd9Sstevel@tonic-gate 	rp->child = child;
111*7c478bd9Sstevel@tonic-gate 	rp->fcode = fcode;
112*7c478bd9Sstevel@tonic-gate 	rp->fcode_size = fcode_size;
113*7c478bd9Sstevel@tonic-gate 	if (unit_address) {
114*7c478bd9Sstevel@tonic-gate 		up = kmem_zalloc(strlen(unit_address) + 1, KM_SLEEP);
115*7c478bd9Sstevel@tonic-gate 		(void) strcpy(up, unit_address);
116*7c478bd9Sstevel@tonic-gate 		rp->unit_address = up;
117*7c478bd9Sstevel@tonic-gate 	}
118*7c478bd9Sstevel@tonic-gate 	rp->bus_args = NULL;		/* generic module has no bus args */
119*7c478bd9Sstevel@tonic-gate 	fc_phandle_table_alloc(fc_handle_to_phandle_head(rp));
120*7c478bd9Sstevel@tonic-gate 
121*7c478bd9Sstevel@tonic-gate 	(void) fc_dip_to_phandle(fc_handle_to_phandle_head(rp), ap);
122*7c478bd9Sstevel@tonic-gate 
123*7c478bd9Sstevel@tonic-gate 	/*
124*7c478bd9Sstevel@tonic-gate 	 * Create our copy of the device tree.
125*7c478bd9Sstevel@tonic-gate 	 */
126*7c478bd9Sstevel@tonic-gate 	fc_create_device_tree(ap, &rp->dtree);
127*7c478bd9Sstevel@tonic-gate 	return (rp);
128*7c478bd9Sstevel@tonic-gate }
129*7c478bd9Sstevel@tonic-gate 
130*7c478bd9Sstevel@tonic-gate /*
131*7c478bd9Sstevel@tonic-gate  * Free any resources associated with this handle.
132*7c478bd9Sstevel@tonic-gate  */
133*7c478bd9Sstevel@tonic-gate void
134*7c478bd9Sstevel@tonic-gate fc_ops_free_handle(fco_handle_t rp)
135*7c478bd9Sstevel@tonic-gate {
136*7c478bd9Sstevel@tonic-gate 	struct fc_resource *ip, *np;
137*7c478bd9Sstevel@tonic-gate 
138*7c478bd9Sstevel@tonic-gate 	if (rp->unit_address)
139*7c478bd9Sstevel@tonic-gate 		kmem_free(rp->unit_address, strlen(rp->unit_address) + 1);
140*7c478bd9Sstevel@tonic-gate 
141*7c478bd9Sstevel@tonic-gate 	if (rp->dtree)
142*7c478bd9Sstevel@tonic-gate 		fc_remove_device_tree(&rp->dtree);
143*7c478bd9Sstevel@tonic-gate 
144*7c478bd9Sstevel@tonic-gate 	fc_phandle_table_free(fc_handle_to_phandle_head(rp));
145*7c478bd9Sstevel@tonic-gate 
146*7c478bd9Sstevel@tonic-gate 	for (ip = rp->head; ip != NULL; ip = np) {
147*7c478bd9Sstevel@tonic-gate 		np = ip->next;
148*7c478bd9Sstevel@tonic-gate 		switch (ip->type) {
149*7c478bd9Sstevel@tonic-gate 		case RT_NODEID:
150*7c478bd9Sstevel@tonic-gate 			impl_ddi_free_nodeid(ip->fc_nodeid_r);
151*7c478bd9Sstevel@tonic-gate 			break;
152*7c478bd9Sstevel@tonic-gate 		default:
153*7c478bd9Sstevel@tonic-gate 			cmn_err(CE_CONT, "pci_fc_ops_free: "
154*7c478bd9Sstevel@tonic-gate 			    "unknown resource type %d\n", ip->type);
155*7c478bd9Sstevel@tonic-gate 			break;
156*7c478bd9Sstevel@tonic-gate 		}
157*7c478bd9Sstevel@tonic-gate 		fc_rem_resource(rp, ip);
158*7c478bd9Sstevel@tonic-gate 		kmem_free(ip, sizeof (struct fc_resource));
159*7c478bd9Sstevel@tonic-gate 	}
160*7c478bd9Sstevel@tonic-gate 	kmem_free(rp, sizeof (struct fc_resource_list));
161*7c478bd9Sstevel@tonic-gate }
162*7c478bd9Sstevel@tonic-gate 
163*7c478bd9Sstevel@tonic-gate int
164*7c478bd9Sstevel@tonic-gate fc_ops(dev_info_t *ap, fco_handle_t handle, fc_ci_t *cp)
165*7c478bd9Sstevel@tonic-gate {
166*7c478bd9Sstevel@tonic-gate 	struct fc_ops_v *pv;
167*7c478bd9Sstevel@tonic-gate 	char *name = fc_cell2ptr(cp->svc_name);
168*7c478bd9Sstevel@tonic-gate 
169*7c478bd9Sstevel@tonic-gate 	for (pv = fov; pv->svc_name != NULL; ++pv)
170*7c478bd9Sstevel@tonic-gate 		if (strcmp(pv->svc_name, name) == 0)
171*7c478bd9Sstevel@tonic-gate 			return (pv->f(ap, handle, cp));
172*7c478bd9Sstevel@tonic-gate 
173*7c478bd9Sstevel@tonic-gate 	return (-1);
174*7c478bd9Sstevel@tonic-gate }
175*7c478bd9Sstevel@tonic-gate 
176*7c478bd9Sstevel@tonic-gate /*
177*7c478bd9Sstevel@tonic-gate  * The interpreter can't do get-inherited-property directly,
178*7c478bd9Sstevel@tonic-gate  * because we don't want to return a kernel address, so it
179*7c478bd9Sstevel@tonic-gate  * has to break up the request into a get-proplen and get-prop
180*7c478bd9Sstevel@tonic-gate  * call so it can allocate memory for the property and pass that
181*7c478bd9Sstevel@tonic-gate  * buffer in to get-prop.  The buffer should be 'suitably aligned'.
182*7c478bd9Sstevel@tonic-gate  *
183*7c478bd9Sstevel@tonic-gate  * XXX: We don't know the property type, so we can't return
184*7c478bd9Sstevel@tonic-gate  * prop-encoded arrays, which fortunately, isn't a problem
185*7c478bd9Sstevel@tonic-gate  * on big-endian machines.
186*7c478bd9Sstevel@tonic-gate  *
187*7c478bd9Sstevel@tonic-gate  * get-proplen has one result: proplen
188*7c478bd9Sstevel@tonic-gate  * proplen is returned as -1 if the propname doesn't exist and
189*7c478bd9Sstevel@tonic-gate  * as zero if the property is a boolean property.
190*7c478bd9Sstevel@tonic-gate  *
191*7c478bd9Sstevel@tonic-gate  * get-prop has one result: proplen, returned as -1 if propname doesn't exist.
192*7c478bd9Sstevel@tonic-gate  */
193*7c478bd9Sstevel@tonic-gate 
194*7c478bd9Sstevel@tonic-gate /*
195*7c478bd9Sstevel@tonic-gate  * fco_getproplen ( propname phandle -- proplen )
196*7c478bd9Sstevel@tonic-gate  */
197*7c478bd9Sstevel@tonic-gate 
198*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
199*7c478bd9Sstevel@tonic-gate static int
200*7c478bd9Sstevel@tonic-gate fco_getproplen(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
201*7c478bd9Sstevel@tonic-gate {
202*7c478bd9Sstevel@tonic-gate 	int proplen;
203*7c478bd9Sstevel@tonic-gate 	int flags = 0;
204*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
205*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
206*7c478bd9Sstevel@tonic-gate 	char *pnp;
207*7c478bd9Sstevel@tonic-gate 	char propname[OBP_MAXPROPNAME];
208*7c478bd9Sstevel@tonic-gate 
209*7c478bd9Sstevel@tonic-gate 	if (strstr(fc_cell2ptr(cp->svc_name), "inherited") == NULL)
210*7c478bd9Sstevel@tonic-gate 		flags |= DDI_PROP_DONTPASS;
211*7c478bd9Sstevel@tonic-gate 
212*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 2)
213*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 2"));
214*7c478bd9Sstevel@tonic-gate 
215*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
216*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
217*7c478bd9Sstevel@tonic-gate 
218*7c478bd9Sstevel@tonic-gate 	/*
219*7c478bd9Sstevel@tonic-gate 	 * Make sure this is a handle we gave out ...
220*7c478bd9Sstevel@tonic-gate 	 */
221*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
222*7c478bd9Sstevel@tonic-gate 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
223*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown handle"));
224*7c478bd9Sstevel@tonic-gate 
225*7c478bd9Sstevel@tonic-gate 	/*
226*7c478bd9Sstevel@tonic-gate 	 * XXX: We should care if the string is longer than OBP_MAXPROPNAME
227*7c478bd9Sstevel@tonic-gate 	 */
228*7c478bd9Sstevel@tonic-gate 	pnp = fc_cell2ptr(fc_arg(cp, 1));
229*7c478bd9Sstevel@tonic-gate 	bzero(propname, OBP_MAXPROPNAME);
230*7c478bd9Sstevel@tonic-gate 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
231*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "EFAULT copying in propname"));
232*7c478bd9Sstevel@tonic-gate 
233*7c478bd9Sstevel@tonic-gate 	if (ddi_getproplen(DDI_DEV_T_ANY, dip, flags, propname, &proplen))
234*7c478bd9Sstevel@tonic-gate 		proplen = -1;
235*7c478bd9Sstevel@tonic-gate 
236*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_int2cell(proplen);
237*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
238*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
239*7c478bd9Sstevel@tonic-gate }
240*7c478bd9Sstevel@tonic-gate 
241*7c478bd9Sstevel@tonic-gate /*
242*7c478bd9Sstevel@tonic-gate  * fco_getprop ( propname buffer phandle -- proplen )
243*7c478bd9Sstevel@tonic-gate  */
244*7c478bd9Sstevel@tonic-gate 
245*7c478bd9Sstevel@tonic-gate /*ARGSUSED*/
246*7c478bd9Sstevel@tonic-gate static int
247*7c478bd9Sstevel@tonic-gate fco_getprop(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
248*7c478bd9Sstevel@tonic-gate {
249*7c478bd9Sstevel@tonic-gate 	int proplen = -1;
250*7c478bd9Sstevel@tonic-gate 	int flags = DDI_PROP_CANSLEEP;
251*7c478bd9Sstevel@tonic-gate 	char *pnp, *bp;
252*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
253*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
254*7c478bd9Sstevel@tonic-gate 	char propname[OBP_MAXPROPNAME];
255*7c478bd9Sstevel@tonic-gate 
256*7c478bd9Sstevel@tonic-gate 	if (strstr(fc_cell2ptr(cp->svc_name), "inherited") == NULL)
257*7c478bd9Sstevel@tonic-gate 		flags |= DDI_PROP_DONTPASS;
258*7c478bd9Sstevel@tonic-gate 
259*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 3)
260*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 3"));
261*7c478bd9Sstevel@tonic-gate 
262*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
263*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
264*7c478bd9Sstevel@tonic-gate 
265*7c478bd9Sstevel@tonic-gate 	/*
266*7c478bd9Sstevel@tonic-gate 	 * Make sure this is a handle we gave out ...
267*7c478bd9Sstevel@tonic-gate 	 */
268*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
269*7c478bd9Sstevel@tonic-gate 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
270*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown handle"));
271*7c478bd9Sstevel@tonic-gate 
272*7c478bd9Sstevel@tonic-gate 	/*
273*7c478bd9Sstevel@tonic-gate 	 * XXX: We should care if the string is longer than OBP_MAXPROPNAME
274*7c478bd9Sstevel@tonic-gate 	 */
275*7c478bd9Sstevel@tonic-gate 	pnp = fc_cell2ptr(fc_arg(cp, 2));
276*7c478bd9Sstevel@tonic-gate 	bzero(propname, OBP_MAXPROPNAME);
277*7c478bd9Sstevel@tonic-gate 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
278*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "EFAULT copying in propname"));
279*7c478bd9Sstevel@tonic-gate 
280*7c478bd9Sstevel@tonic-gate 	if (ddi_getlongprop(DDI_DEV_T_ANY, dip, flags,
281*7c478bd9Sstevel@tonic-gate 	    propname, (caddr_t)&bp, &proplen))
282*7c478bd9Sstevel@tonic-gate 		proplen = -1;
283*7c478bd9Sstevel@tonic-gate 
284*7c478bd9Sstevel@tonic-gate 	if (proplen > 0) {
285*7c478bd9Sstevel@tonic-gate 		char *up = fc_cell2ptr(fc_arg(cp, 1));
286*7c478bd9Sstevel@tonic-gate 		int error;
287*7c478bd9Sstevel@tonic-gate 
288*7c478bd9Sstevel@tonic-gate 		error = copyout(bp, up, proplen);
289*7c478bd9Sstevel@tonic-gate 		kmem_free(bp, proplen);
290*7c478bd9Sstevel@tonic-gate 		if (error)
291*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "EFAULT copying data out"));
292*7c478bd9Sstevel@tonic-gate 	}
293*7c478bd9Sstevel@tonic-gate 
294*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
295*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_int2cell(proplen);
296*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
297*7c478bd9Sstevel@tonic-gate }
298*7c478bd9Sstevel@tonic-gate 
299*7c478bd9Sstevel@tonic-gate static int
300*7c478bd9Sstevel@tonic-gate fco_ap_phandle(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
301*7c478bd9Sstevel@tonic-gate {
302*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
303*7c478bd9Sstevel@tonic-gate 
304*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 0)
305*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 0"));
306*7c478bd9Sstevel@tonic-gate 
307*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
308*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
309*7c478bd9Sstevel@tonic-gate 
310*7c478bd9Sstevel@tonic-gate 	FC_DEBUG1(9, CE_CONT, "fco_ap_phandle: Looking up ap dip %p\n", ap);
311*7c478bd9Sstevel@tonic-gate 
312*7c478bd9Sstevel@tonic-gate 	h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), ap);
313*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
314*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_phandle2cell(h);
315*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
316*7c478bd9Sstevel@tonic-gate }
317*7c478bd9Sstevel@tonic-gate 
318*7c478bd9Sstevel@tonic-gate static int
319*7c478bd9Sstevel@tonic-gate fco_child(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
320*7c478bd9Sstevel@tonic-gate {
321*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
322*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
323*7c478bd9Sstevel@tonic-gate 
324*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 1)
325*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 1"));
326*7c478bd9Sstevel@tonic-gate 
327*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
328*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
329*7c478bd9Sstevel@tonic-gate 
330*7c478bd9Sstevel@tonic-gate 	/*
331*7c478bd9Sstevel@tonic-gate 	 * Make sure this is a handle we gave out ...
332*7c478bd9Sstevel@tonic-gate 	 */
333*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
334*7c478bd9Sstevel@tonic-gate 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
335*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown handle"));
336*7c478bd9Sstevel@tonic-gate 
337*7c478bd9Sstevel@tonic-gate 	/*
338*7c478bd9Sstevel@tonic-gate 	 * Find the child and if there is one, return it ...
339*7c478bd9Sstevel@tonic-gate 	 */
340*7c478bd9Sstevel@tonic-gate 	dip = ddi_get_child(dip);
341*7c478bd9Sstevel@tonic-gate 	h = 0;
342*7c478bd9Sstevel@tonic-gate 	if (dip != NULL)
343*7c478bd9Sstevel@tonic-gate 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
344*7c478bd9Sstevel@tonic-gate 
345*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
346*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_phandle2cell(h);
347*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
348*7c478bd9Sstevel@tonic-gate }
349*7c478bd9Sstevel@tonic-gate 
350*7c478bd9Sstevel@tonic-gate static int
351*7c478bd9Sstevel@tonic-gate fco_peer(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
352*7c478bd9Sstevel@tonic-gate {
353*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
354*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
355*7c478bd9Sstevel@tonic-gate 
356*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 1)
357*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 1"));
358*7c478bd9Sstevel@tonic-gate 
359*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
360*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
361*7c478bd9Sstevel@tonic-gate 
362*7c478bd9Sstevel@tonic-gate 	/*
363*7c478bd9Sstevel@tonic-gate 	 * Make sure this is a handle we gave out ...
364*7c478bd9Sstevel@tonic-gate 	 */
365*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
366*7c478bd9Sstevel@tonic-gate 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
367*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown handle"));
368*7c478bd9Sstevel@tonic-gate 
369*7c478bd9Sstevel@tonic-gate 	/*
370*7c478bd9Sstevel@tonic-gate 	 * Find the child and if there is one, return it ...
371*7c478bd9Sstevel@tonic-gate 	 */
372*7c478bd9Sstevel@tonic-gate 	dip = ddi_get_next_sibling(dip);
373*7c478bd9Sstevel@tonic-gate 	h = 0;
374*7c478bd9Sstevel@tonic-gate 	if (dip != NULL)
375*7c478bd9Sstevel@tonic-gate 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
376*7c478bd9Sstevel@tonic-gate 
377*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
378*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_phandle2cell(h);
379*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
380*7c478bd9Sstevel@tonic-gate }
381*7c478bd9Sstevel@tonic-gate 
382*7c478bd9Sstevel@tonic-gate static int
383*7c478bd9Sstevel@tonic-gate fco_parent(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
384*7c478bd9Sstevel@tonic-gate {
385*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
386*7c478bd9Sstevel@tonic-gate 	dev_info_t *dip;
387*7c478bd9Sstevel@tonic-gate 
388*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 1)
389*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 1"));
390*7c478bd9Sstevel@tonic-gate 
391*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
392*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
393*7c478bd9Sstevel@tonic-gate 
394*7c478bd9Sstevel@tonic-gate 	/*
395*7c478bd9Sstevel@tonic-gate 	 * Make sure this is a handle we gave out ...
396*7c478bd9Sstevel@tonic-gate 	 */
397*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
398*7c478bd9Sstevel@tonic-gate 	if ((dip = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h)) == NULL)
399*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown handle"));
400*7c478bd9Sstevel@tonic-gate 
401*7c478bd9Sstevel@tonic-gate 	/*
402*7c478bd9Sstevel@tonic-gate 	 * Find the parent and if there is one, return it ...
403*7c478bd9Sstevel@tonic-gate 	 */
404*7c478bd9Sstevel@tonic-gate 	dip = ddi_get_parent(dip);
405*7c478bd9Sstevel@tonic-gate 	h = 0;
406*7c478bd9Sstevel@tonic-gate 	if (dip != NULL)
407*7c478bd9Sstevel@tonic-gate 		h = fc_dip_to_phandle(fc_handle_to_phandle_head(rp), dip);
408*7c478bd9Sstevel@tonic-gate 
409*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
410*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_phandle2cell(h);
411*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
412*7c478bd9Sstevel@tonic-gate }
413*7c478bd9Sstevel@tonic-gate 
414*7c478bd9Sstevel@tonic-gate /*
415*7c478bd9Sstevel@tonic-gate  * Allocate a phandle ... we don't currently track the phandle.
416*7c478bd9Sstevel@tonic-gate  */
417*7c478bd9Sstevel@tonic-gate static int
418*7c478bd9Sstevel@tonic-gate fco_alloc_phandle(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
419*7c478bd9Sstevel@tonic-gate {
420*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
421*7c478bd9Sstevel@tonic-gate 	int n;
422*7c478bd9Sstevel@tonic-gate 	struct fc_resource *ip;
423*7c478bd9Sstevel@tonic-gate 
424*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 0)
425*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 0"));
426*7c478bd9Sstevel@tonic-gate 
427*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) < 1)
428*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be > 0"));
429*7c478bd9Sstevel@tonic-gate 
430*7c478bd9Sstevel@tonic-gate 	if (impl_ddi_alloc_nodeid(&n))
431*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "Can't allocate a nodeid"));
432*7c478bd9Sstevel@tonic-gate 
433*7c478bd9Sstevel@tonic-gate 	/*
434*7c478bd9Sstevel@tonic-gate 	 * Log the nodeid resource so we can release it later if we need to.
435*7c478bd9Sstevel@tonic-gate 	 */
436*7c478bd9Sstevel@tonic-gate 	ip = kmem_zalloc(sizeof (struct fc_resource), KM_SLEEP);
437*7c478bd9Sstevel@tonic-gate 	ip->type = RT_NODEID;
438*7c478bd9Sstevel@tonic-gate 	ip->fc_nodeid_r = n;
439*7c478bd9Sstevel@tonic-gate 	fc_add_resource(rp, ip);
440*7c478bd9Sstevel@tonic-gate 
441*7c478bd9Sstevel@tonic-gate 	h = (fc_phandle_t)n;
442*7c478bd9Sstevel@tonic-gate 
443*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(1);
444*7c478bd9Sstevel@tonic-gate 	fc_result(cp, 0) = fc_phandle2cell(h);
445*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
446*7c478bd9Sstevel@tonic-gate }
447*7c478bd9Sstevel@tonic-gate 
448*7c478bd9Sstevel@tonic-gate static struct fc_resource *
449*7c478bd9Sstevel@tonic-gate find_nodeid_resource(fco_handle_t rp, int n)
450*7c478bd9Sstevel@tonic-gate {
451*7c478bd9Sstevel@tonic-gate 	struct fc_resource *ip;
452*7c478bd9Sstevel@tonic-gate 
453*7c478bd9Sstevel@tonic-gate 	fc_lock_resource_list(rp);
454*7c478bd9Sstevel@tonic-gate 	for (ip = rp->head; ip != NULL; ip = ip->next) {
455*7c478bd9Sstevel@tonic-gate 		if (ip->type != RT_NODEID)
456*7c478bd9Sstevel@tonic-gate 			continue;
457*7c478bd9Sstevel@tonic-gate 		if (ip->fc_nodeid_r == n)
458*7c478bd9Sstevel@tonic-gate 			break;
459*7c478bd9Sstevel@tonic-gate 	}
460*7c478bd9Sstevel@tonic-gate 	fc_unlock_resource_list(rp);
461*7c478bd9Sstevel@tonic-gate 
462*7c478bd9Sstevel@tonic-gate 	return (ip);
463*7c478bd9Sstevel@tonic-gate }
464*7c478bd9Sstevel@tonic-gate 
465*7c478bd9Sstevel@tonic-gate /*
466*7c478bd9Sstevel@tonic-gate  * fco_new_device ( name-cstr unit-addr-cstr parent.phandle phandle -- )
467*7c478bd9Sstevel@tonic-gate  */
468*7c478bd9Sstevel@tonic-gate static int
469*7c478bd9Sstevel@tonic-gate fco_new_device(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
470*7c478bd9Sstevel@tonic-gate {
471*7c478bd9Sstevel@tonic-gate 	fc_phandle_t ph, ch;
472*7c478bd9Sstevel@tonic-gate 	dev_info_t *pdev, *cdev;
473*7c478bd9Sstevel@tonic-gate 	char *s;
474*7c478bd9Sstevel@tonic-gate 	int createmode = 0;
475*7c478bd9Sstevel@tonic-gate 	char *unit_address = NULL;
476*7c478bd9Sstevel@tonic-gate 	char nodename[OBP_MAXPROPNAME];
477*7c478bd9Sstevel@tonic-gate 
478*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 4)
479*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 4"));
480*7c478bd9Sstevel@tonic-gate 
481*7c478bd9Sstevel@tonic-gate 	/*
482*7c478bd9Sstevel@tonic-gate 	 * Make sure these are handles we gave out ... and we have
483*7c478bd9Sstevel@tonic-gate 	 * a corresponding parent devinfo node.
484*7c478bd9Sstevel@tonic-gate 	 */
485*7c478bd9Sstevel@tonic-gate 	ph = fc_cell2phandle(fc_arg(cp, 1));
486*7c478bd9Sstevel@tonic-gate 	pdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), ph);
487*7c478bd9Sstevel@tonic-gate 	if (pdev == NULL)
488*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "unknown parent phandle"));
489*7c478bd9Sstevel@tonic-gate 
490*7c478bd9Sstevel@tonic-gate 	ch = fc_cell2phandle(fc_arg(cp, 0));
491*7c478bd9Sstevel@tonic-gate 	cdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), ch);
492*7c478bd9Sstevel@tonic-gate 
493*7c478bd9Sstevel@tonic-gate 	switch (rp->cdip_state) {
494*7c478bd9Sstevel@tonic-gate 
495*7c478bd9Sstevel@tonic-gate 	case FC_CDIP_NOSTATE:
496*7c478bd9Sstevel@tonic-gate 		/*
497*7c478bd9Sstevel@tonic-gate 		 * The first child must be a child of the attachment point.
498*7c478bd9Sstevel@tonic-gate 		 */
499*7c478bd9Sstevel@tonic-gate 		if (pdev != ap)
500*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "first child must be a "
501*7c478bd9Sstevel@tonic-gate 			    "child of the attachment point"));
502*7c478bd9Sstevel@tonic-gate 
503*7c478bd9Sstevel@tonic-gate 		/*
504*7c478bd9Sstevel@tonic-gate 		 * If this bus has a config child, the first child must
505*7c478bd9Sstevel@tonic-gate 		 * be the configuration child. Otherwise, the child must
506*7c478bd9Sstevel@tonic-gate 		 * be a new (unknown) node.
507*7c478bd9Sstevel@tonic-gate 		 */
508*7c478bd9Sstevel@tonic-gate 		if (cdev != NULL) {
509*7c478bd9Sstevel@tonic-gate 			if (rp->child != NULL) {
510*7c478bd9Sstevel@tonic-gate 				if (cdev != rp->child)
511*7c478bd9Sstevel@tonic-gate 					return (fc_priv_error(cp, "first "
512*7c478bd9Sstevel@tonic-gate 					    "child must be the "
513*7c478bd9Sstevel@tonic-gate 					    "configuration child"));
514*7c478bd9Sstevel@tonic-gate 			} else {
515*7c478bd9Sstevel@tonic-gate 				return (fc_priv_error(cp, "known child -- "
516*7c478bd9Sstevel@tonic-gate 				    "unknown child expected"));
517*7c478bd9Sstevel@tonic-gate 			}
518*7c478bd9Sstevel@tonic-gate 		}
519*7c478bd9Sstevel@tonic-gate 		break;
520*7c478bd9Sstevel@tonic-gate 
521*7c478bd9Sstevel@tonic-gate 	case FC_CDIP_DONE:
522*7c478bd9Sstevel@tonic-gate 		/*
523*7c478bd9Sstevel@tonic-gate 		 * If we've already created the first child, this
524*7c478bd9Sstevel@tonic-gate 		 * child must be unknown and the parent must be a known
525*7c478bd9Sstevel@tonic-gate 		 * child of the attachment point.
526*7c478bd9Sstevel@tonic-gate 		 */
527*7c478bd9Sstevel@tonic-gate 		if (cdev)
528*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "known child -- "
529*7c478bd9Sstevel@tonic-gate 			    "unknown child expected"));
530*7c478bd9Sstevel@tonic-gate 		if (fc_find_node(pdev, fc_handle_to_dtree(rp)) == NULL)
531*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "parent is an unknown "
532*7c478bd9Sstevel@tonic-gate 			    "child of the attachment point"));
533*7c478bd9Sstevel@tonic-gate 		break;
534*7c478bd9Sstevel@tonic-gate 
535*7c478bd9Sstevel@tonic-gate 	default:
536*7c478bd9Sstevel@tonic-gate 		/*
537*7c478bd9Sstevel@tonic-gate 		 * If we're in some other state, we shouldn't be here.
538*7c478bd9Sstevel@tonic-gate 		 */
539*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "bad node-creation state"));
540*7c478bd9Sstevel@tonic-gate 		/* NOTREACHED */
541*7c478bd9Sstevel@tonic-gate 	}
542*7c478bd9Sstevel@tonic-gate 
543*7c478bd9Sstevel@tonic-gate 	/*
544*7c478bd9Sstevel@tonic-gate 	 * Get the nodename and the unit address.
545*7c478bd9Sstevel@tonic-gate 	 */
546*7c478bd9Sstevel@tonic-gate 	s = fc_cell2ptr(fc_arg(cp, 3));
547*7c478bd9Sstevel@tonic-gate 	bzero(nodename, OBP_MAXPROPNAME);
548*7c478bd9Sstevel@tonic-gate 	if (copyinstr(s, nodename, OBP_MAXPROPNAME - 1, NULL))
549*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "EFAULT copying in nodename"));
550*7c478bd9Sstevel@tonic-gate 
551*7c478bd9Sstevel@tonic-gate 	s = fc_cell2ptr(fc_arg(cp, 2));
552*7c478bd9Sstevel@tonic-gate 	unit_address = kmem_zalloc(OBP_MAXPATHLEN, KM_SLEEP);
553*7c478bd9Sstevel@tonic-gate 	if (copyinstr(s, unit_address, OBP_MAXPATHLEN - 1, NULL)) {
554*7c478bd9Sstevel@tonic-gate 		kmem_free(unit_address, OBP_MAXPATHLEN);
555*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "EFAULT copying in unit address"));
556*7c478bd9Sstevel@tonic-gate 	}
557*7c478bd9Sstevel@tonic-gate 
558*7c478bd9Sstevel@tonic-gate 	/*
559*7c478bd9Sstevel@tonic-gate 	 * If cdev is NULL, we have to create the child, otherwise, the
560*7c478bd9Sstevel@tonic-gate 	 * child already exists and we're just merging properties into
561*7c478bd9Sstevel@tonic-gate 	 * the existing node.  The node must be unbound.
562*7c478bd9Sstevel@tonic-gate 	 */
563*7c478bd9Sstevel@tonic-gate 
564*7c478bd9Sstevel@tonic-gate 	if (cdev == NULL)
565*7c478bd9Sstevel@tonic-gate 		createmode = 1;
566*7c478bd9Sstevel@tonic-gate 
567*7c478bd9Sstevel@tonic-gate 	if (createmode) {
568*7c478bd9Sstevel@tonic-gate 		struct fc_resource *ip;
569*7c478bd9Sstevel@tonic-gate 		int nodeid;
570*7c478bd9Sstevel@tonic-gate 		/*
571*7c478bd9Sstevel@tonic-gate 		 * Make sure 'ch' is a nodeid we gave the interpreter.
572*7c478bd9Sstevel@tonic-gate 		 * It must be on our resource list.
573*7c478bd9Sstevel@tonic-gate 		 */
574*7c478bd9Sstevel@tonic-gate 		if ((ip = find_nodeid_resource(rp, (int)ch)) == NULL) {
575*7c478bd9Sstevel@tonic-gate 			kmem_free(unit_address, OBP_MAXPATHLEN);
576*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "Unknown phandle"));
577*7c478bd9Sstevel@tonic-gate 		}
578*7c478bd9Sstevel@tonic-gate 
579*7c478bd9Sstevel@tonic-gate 		/*
580*7c478bd9Sstevel@tonic-gate 		 * Allocate a self-identifying, persistent node with
581*7c478bd9Sstevel@tonic-gate 		 * the auto-free attribute.
582*7c478bd9Sstevel@tonic-gate 		 */
583*7c478bd9Sstevel@tonic-gate 		if (ndi_devi_alloc(pdev, nodename, DEVI_SID_NODEID, &cdev)) {
584*7c478bd9Sstevel@tonic-gate 			kmem_free(unit_address, OBP_MAXPATHLEN);
585*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "Can't create node"));
586*7c478bd9Sstevel@tonic-gate 		}
587*7c478bd9Sstevel@tonic-gate 
588*7c478bd9Sstevel@tonic-gate 		/*
589*7c478bd9Sstevel@tonic-gate 		 * Free the nodeid we just allocated here, and use
590*7c478bd9Sstevel@tonic-gate 		 * the one we handed in. Retain the attributes of
591*7c478bd9Sstevel@tonic-gate 		 * the original SID nodetype.
592*7c478bd9Sstevel@tonic-gate 		 */
593*7c478bd9Sstevel@tonic-gate 		nodeid = ddi_get_nodeid(cdev);
594*7c478bd9Sstevel@tonic-gate 		i_ndi_set_nodeid(cdev, (int)ch);
595*7c478bd9Sstevel@tonic-gate 		impl_ddi_free_nodeid(nodeid);
596*7c478bd9Sstevel@tonic-gate 
597*7c478bd9Sstevel@tonic-gate 		/*
598*7c478bd9Sstevel@tonic-gate 		 * Remove nodeid 'ch' from our resource list, now that it
599*7c478bd9Sstevel@tonic-gate 		 * will be managed by the ddi framework.
600*7c478bd9Sstevel@tonic-gate 		 */
601*7c478bd9Sstevel@tonic-gate 		fc_rem_resource(rp, ip);
602*7c478bd9Sstevel@tonic-gate 		kmem_free(ip, sizeof (struct fc_resource));
603*7c478bd9Sstevel@tonic-gate 
604*7c478bd9Sstevel@tonic-gate 	} else if (strcmp(ddi_node_name(cdev), nodename) != 0) {
605*7c478bd9Sstevel@tonic-gate 		FC_DEBUG2(1, CE_CONT, "Changing <%s> nodename to <%s>\n",
606*7c478bd9Sstevel@tonic-gate 		    ddi_node_name(cdev), nodename);
607*7c478bd9Sstevel@tonic-gate 		if (ndi_devi_set_nodename(cdev, nodename, 0)) {
608*7c478bd9Sstevel@tonic-gate 			kmem_free(unit_address, OBP_MAXPATHLEN);
609*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "Can't set ndi nodename"));
610*7c478bd9Sstevel@tonic-gate 		}
611*7c478bd9Sstevel@tonic-gate 	}
612*7c478bd9Sstevel@tonic-gate 
613*7c478bd9Sstevel@tonic-gate 	if (fc_ndi_prop_update(DDI_DEV_T_NONE, cdev, "name",
614*7c478bd9Sstevel@tonic-gate 	    (uchar_t *)nodename, strlen(nodename) + 1)) {
615*7c478bd9Sstevel@tonic-gate 		kmem_free(unit_address, OBP_MAXPATHLEN);
616*7c478bd9Sstevel@tonic-gate 		if (createmode)
617*7c478bd9Sstevel@tonic-gate 			(void) ndi_devi_free(cdev);
618*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "Can't create name property"));
619*7c478bd9Sstevel@tonic-gate 	}
620*7c478bd9Sstevel@tonic-gate 
621*7c478bd9Sstevel@tonic-gate 	/*
622*7c478bd9Sstevel@tonic-gate 	 * Add the dip->phandle translation to our list of known phandles.
623*7c478bd9Sstevel@tonic-gate 	 */
624*7c478bd9Sstevel@tonic-gate 	fc_add_dip_to_phandle(fc_handle_to_phandle_head(rp), cdev, ch);
625*7c478bd9Sstevel@tonic-gate 
626*7c478bd9Sstevel@tonic-gate 	/*
627*7c478bd9Sstevel@tonic-gate 	 * Add the new node to our copy of the subtree.
628*7c478bd9Sstevel@tonic-gate 	 */
629*7c478bd9Sstevel@tonic-gate 	fc_add_child(cdev, pdev, fc_handle_to_dtree(rp));
630*7c478bd9Sstevel@tonic-gate 
631*7c478bd9Sstevel@tonic-gate 	rp->cdip = cdev;
632*7c478bd9Sstevel@tonic-gate 	rp->cdip_state = FC_CDIP_STARTED;
633*7c478bd9Sstevel@tonic-gate 
634*7c478bd9Sstevel@tonic-gate 	kmem_free(unit_address, OBP_MAXPATHLEN);
635*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
636*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
637*7c478bd9Sstevel@tonic-gate }
638*7c478bd9Sstevel@tonic-gate 
639*7c478bd9Sstevel@tonic-gate /*
640*7c478bd9Sstevel@tonic-gate  * fco_finish_device ( phandle -- )
641*7c478bd9Sstevel@tonic-gate  */
642*7c478bd9Sstevel@tonic-gate static int
643*7c478bd9Sstevel@tonic-gate fco_finish_device(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
644*7c478bd9Sstevel@tonic-gate {
645*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
646*7c478bd9Sstevel@tonic-gate 	dev_info_t *cdev;
647*7c478bd9Sstevel@tonic-gate 
648*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 1)
649*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 1"));
650*7c478bd9Sstevel@tonic-gate 
651*7c478bd9Sstevel@tonic-gate 	if (rp->cdip_state != FC_CDIP_STARTED)
652*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "bad node-creation state"));
653*7c478bd9Sstevel@tonic-gate 
654*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
655*7c478bd9Sstevel@tonic-gate 	cdev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h);
656*7c478bd9Sstevel@tonic-gate 	if (cdev != rp->cdip)
657*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "bad phandle"));
658*7c478bd9Sstevel@tonic-gate 
659*7c478bd9Sstevel@tonic-gate 	/*
660*7c478bd9Sstevel@tonic-gate 	 * We don't want to online children of the attachment point.
661*7c478bd9Sstevel@tonic-gate 	 * We'll 'config' them online later.
662*7c478bd9Sstevel@tonic-gate 	 *
663*7c478bd9Sstevel@tonic-gate 	 * XXX - APA - I've changed this a bit.  The only time we don't
664*7c478bd9Sstevel@tonic-gate 	 * want to bind the device is if the parent is the attachment point
665*7c478bd9Sstevel@tonic-gate 	 * and the device is the same as the device that was passed to
666*7c478bd9Sstevel@tonic-gate 	 * the interpreter.  We assume the configurator will do the binding.
667*7c478bd9Sstevel@tonic-gate 	 */
668*7c478bd9Sstevel@tonic-gate 	if ((ddi_get_parent(cdev) == ap) && (cdev == rp->child)) {
669*7c478bd9Sstevel@tonic-gate 		FC_DEBUG2(5, CE_CONT, "fc_finish_device: "
670*7c478bd9Sstevel@tonic-gate 		    "*not* binding <%s> dip %p\n", ddi_node_name(cdev), cdev);
671*7c478bd9Sstevel@tonic-gate 	} else {
672*7c478bd9Sstevel@tonic-gate 		FC_DEBUG2(5, CE_CONT, "fc_finish_device: binding <%s> dip %p\n",
673*7c478bd9Sstevel@tonic-gate 		    ddi_node_name(cdev), cdev);
674*7c478bd9Sstevel@tonic-gate 
675*7c478bd9Sstevel@tonic-gate 		(void) ndi_devi_bind_driver(cdev, 0);
676*7c478bd9Sstevel@tonic-gate 	}
677*7c478bd9Sstevel@tonic-gate 
678*7c478bd9Sstevel@tonic-gate 	rp->cdip_state = FC_CDIP_DONE;
679*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
680*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
681*7c478bd9Sstevel@tonic-gate }
682*7c478bd9Sstevel@tonic-gate 
683*7c478bd9Sstevel@tonic-gate /*
684*7c478bd9Sstevel@tonic-gate  * fco_create_property ( propname-cstr buf len phandle -- )
685*7c478bd9Sstevel@tonic-gate  */
686*7c478bd9Sstevel@tonic-gate static int
687*7c478bd9Sstevel@tonic-gate fco_create_property(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
688*7c478bd9Sstevel@tonic-gate {
689*7c478bd9Sstevel@tonic-gate 	char *buf, *bp, *pnp;
690*7c478bd9Sstevel@tonic-gate 	size_t len;
691*7c478bd9Sstevel@tonic-gate 	fc_phandle_t h;
692*7c478bd9Sstevel@tonic-gate 	dev_info_t *dev;
693*7c478bd9Sstevel@tonic-gate 	int error;
694*7c478bd9Sstevel@tonic-gate 	char propname[OBP_MAXPROPNAME];
695*7c478bd9Sstevel@tonic-gate 
696*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 4)
697*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 4"));
698*7c478bd9Sstevel@tonic-gate 
699*7c478bd9Sstevel@tonic-gate 	h = fc_cell2phandle(fc_arg(cp, 0));
700*7c478bd9Sstevel@tonic-gate 	len = fc_cell2size(fc_arg(cp, 1));
701*7c478bd9Sstevel@tonic-gate 	bp = fc_cell2ptr(fc_arg(cp, 2));
702*7c478bd9Sstevel@tonic-gate 	pnp = fc_cell2ptr(fc_arg(cp, 3));
703*7c478bd9Sstevel@tonic-gate 
704*7c478bd9Sstevel@tonic-gate 	dev = fc_phandle_to_dip(fc_handle_to_phandle_head(rp), h);
705*7c478bd9Sstevel@tonic-gate 	if (dev == NULL)
706*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "bad phandle"));
707*7c478bd9Sstevel@tonic-gate 
708*7c478bd9Sstevel@tonic-gate 	bzero(propname, OBP_MAXPROPNAME);
709*7c478bd9Sstevel@tonic-gate 	if (copyinstr(pnp, propname, OBP_MAXPROPNAME - 1, NULL))
710*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "EFAULT copying in propname"));
711*7c478bd9Sstevel@tonic-gate 
712*7c478bd9Sstevel@tonic-gate 	buf = NULL;
713*7c478bd9Sstevel@tonic-gate 	if (len != 0) {
714*7c478bd9Sstevel@tonic-gate 		buf = kmem_zalloc(len, KM_SLEEP);
715*7c478bd9Sstevel@tonic-gate 		if (copyin(bp, buf, len)) {
716*7c478bd9Sstevel@tonic-gate 			kmem_free(buf, len);
717*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "EFAULT copying in propval"));
718*7c478bd9Sstevel@tonic-gate 		}
719*7c478bd9Sstevel@tonic-gate 	}
720*7c478bd9Sstevel@tonic-gate 
721*7c478bd9Sstevel@tonic-gate 	/*
722*7c478bd9Sstevel@tonic-gate 	 * check for propname: 'name' ... we don't allow it
723*7c478bd9Sstevel@tonic-gate 	 * by changed here.  It has to be specified when the node
724*7c478bd9Sstevel@tonic-gate 	 * is created.
725*7c478bd9Sstevel@tonic-gate 	 */
726*7c478bd9Sstevel@tonic-gate 	if (strcmp(propname, "name") == 0) {
727*7c478bd9Sstevel@tonic-gate 		char *n = ddi_node_name(dev);
728*7c478bd9Sstevel@tonic-gate 
729*7c478bd9Sstevel@tonic-gate 		if (len == 0)
730*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "setting <name> to NULL"));
731*7c478bd9Sstevel@tonic-gate 		if ((len < (strlen(n) + 1)) || (strcmp(n, buf) != 0)) {
732*7c478bd9Sstevel@tonic-gate 			kmem_free(buf, len);
733*7c478bd9Sstevel@tonic-gate 			return (fc_priv_error(cp, "changing <name> property"));
734*7c478bd9Sstevel@tonic-gate 		}
735*7c478bd9Sstevel@tonic-gate 		/*
736*7c478bd9Sstevel@tonic-gate 		 * Since we're not changing the value, and we already created
737*7c478bd9Sstevel@tonic-gate 		 * the 'name' property when we created the node ...
738*7c478bd9Sstevel@tonic-gate 		 */
739*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, len);
740*7c478bd9Sstevel@tonic-gate 		cp->nresults = fc_int2cell(0);
741*7c478bd9Sstevel@tonic-gate 		return (fc_success_op(ap, rp, cp));
742*7c478bd9Sstevel@tonic-gate 	}
743*7c478bd9Sstevel@tonic-gate 
744*7c478bd9Sstevel@tonic-gate 	error = fc_ndi_prop_update(DDI_DEV_T_NONE, dev, propname,
745*7c478bd9Sstevel@tonic-gate 	    (uchar_t *)buf, len);
746*7c478bd9Sstevel@tonic-gate 
747*7c478bd9Sstevel@tonic-gate 	if (len != 0)
748*7c478bd9Sstevel@tonic-gate 		kmem_free(buf, len);
749*7c478bd9Sstevel@tonic-gate 
750*7c478bd9Sstevel@tonic-gate 	if (error)
751*7c478bd9Sstevel@tonic-gate 		return (fc_priv_error(cp, "Can't create property"));
752*7c478bd9Sstevel@tonic-gate 
753*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
754*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
755*7c478bd9Sstevel@tonic-gate }
756*7c478bd9Sstevel@tonic-gate 
757*7c478bd9Sstevel@tonic-gate /*
758*7c478bd9Sstevel@tonic-gate  * Make sure any in-progress activity is completed,
759*7c478bd9Sstevel@tonic-gate  * and for now, online the subtree.
760*7c478bd9Sstevel@tonic-gate  * XXX: Presumably the configurator will online the subtree
761*7c478bd9Sstevel@tonic-gate  * XXX: by doing an ndi_devi_online with NDI_CONFIG on the child
762*7c478bd9Sstevel@tonic-gate  * XXX: if there is one.  For now, we're doing it here.
763*7c478bd9Sstevel@tonic-gate  * XXX: For buses without a configurator (and thus no config child),
764*7c478bd9Sstevel@tonic-gate  * XXX: we have to do it here.
765*7c478bd9Sstevel@tonic-gate  *
766*7c478bd9Sstevel@tonic-gate  */
767*7c478bd9Sstevel@tonic-gate static int
768*7c478bd9Sstevel@tonic-gate fco_validate(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
769*7c478bd9Sstevel@tonic-gate {
770*7c478bd9Sstevel@tonic-gate 	rp->cdip_state = FC_CDIP_CONFIG;
771*7c478bd9Sstevel@tonic-gate 
772*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
773*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
774*7c478bd9Sstevel@tonic-gate }
775*7c478bd9Sstevel@tonic-gate 
776*7c478bd9Sstevel@tonic-gate static void
777*7c478bd9Sstevel@tonic-gate remove_subtree(dev_info_t *root, struct fc_device_tree *subtree)
778*7c478bd9Sstevel@tonic-gate {
779*7c478bd9Sstevel@tonic-gate 	dev_info_t *child;
780*7c478bd9Sstevel@tonic-gate 
781*7c478bd9Sstevel@tonic-gate 	/*
782*7c478bd9Sstevel@tonic-gate 	 * Remove the subtree, depth first. Each iterative
783*7c478bd9Sstevel@tonic-gate 	 * call gets another child at each level of the tree
784*7c478bd9Sstevel@tonic-gate 	 * until there are no more children.
785*7c478bd9Sstevel@tonic-gate 	 */
786*7c478bd9Sstevel@tonic-gate 	while ((child = fc_child_node(root, subtree)) != NULL)
787*7c478bd9Sstevel@tonic-gate 		remove_subtree(child, subtree);
788*7c478bd9Sstevel@tonic-gate 
789*7c478bd9Sstevel@tonic-gate 	/*
790*7c478bd9Sstevel@tonic-gate 	 * Delete the subtree root and remove its record from our
791*7c478bd9Sstevel@tonic-gate 	 * copy of the subtree.
792*7c478bd9Sstevel@tonic-gate 	 */
793*7c478bd9Sstevel@tonic-gate 	fc_remove_child(root, subtree);
794*7c478bd9Sstevel@tonic-gate 	(void) ndi_devi_offline(root, NDI_UNCONFIG | NDI_DEVI_REMOVE);
795*7c478bd9Sstevel@tonic-gate }
796*7c478bd9Sstevel@tonic-gate 
797*7c478bd9Sstevel@tonic-gate static int
798*7c478bd9Sstevel@tonic-gate fco_invalidate(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
799*7c478bd9Sstevel@tonic-gate {
800*7c478bd9Sstevel@tonic-gate 	dev_info_t *root, *child;
801*7c478bd9Sstevel@tonic-gate 	struct fc_device_tree *subtree = fc_handle_to_dtree(rp);
802*7c478bd9Sstevel@tonic-gate 	int configured = (rp->cdip_state == FC_CDIP_CONFIG);
803*7c478bd9Sstevel@tonic-gate 
804*7c478bd9Sstevel@tonic-gate 	/*
805*7c478bd9Sstevel@tonic-gate 	 * If we created any children, delete them. The root node is the
806*7c478bd9Sstevel@tonic-gate 	 * config child, if one exists for this bus, otherwise it's the
807*7c478bd9Sstevel@tonic-gate 	 * attachment point.
808*7c478bd9Sstevel@tonic-gate 	 *
809*7c478bd9Sstevel@tonic-gate 	 * Our copy of the subtree only contains records of nodes we created
810*7c478bd9Sstevel@tonic-gate 	 * under the subtree root and contains the parent->child linkage
811*7c478bd9Sstevel@tonic-gate 	 * that isn't yet established in the real device tree.
812*7c478bd9Sstevel@tonic-gate 	 *
813*7c478bd9Sstevel@tonic-gate 	 * XXX: What we don't do is restore the config child node to it's
814*7c478bd9Sstevel@tonic-gate 	 * pre-interpretive state. (We may have added properties to
815*7c478bd9Sstevel@tonic-gate 	 * that node. It's not clear if its necessary to clean them up.)
816*7c478bd9Sstevel@tonic-gate 	 */
817*7c478bd9Sstevel@tonic-gate 	root = rp->child ? rp->child : ap;
818*7c478bd9Sstevel@tonic-gate 
819*7c478bd9Sstevel@tonic-gate 	while ((child = fc_child_node(root, subtree)) != NULL) {
820*7c478bd9Sstevel@tonic-gate 		FC_DEBUG2(1, CE_CONT, "fco_invalidate: remove subtree "
821*7c478bd9Sstevel@tonic-gate 		    "<%s> dip %p\n", ddi_node_name(child), child);
822*7c478bd9Sstevel@tonic-gate 		remove_subtree(child, subtree);
823*7c478bd9Sstevel@tonic-gate 	}
824*7c478bd9Sstevel@tonic-gate 
825*7c478bd9Sstevel@tonic-gate 	if (configured)
826*7c478bd9Sstevel@tonic-gate 		(void) ndi_devi_offline(root, NDI_UNCONFIG);
827*7c478bd9Sstevel@tonic-gate 
828*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
829*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
830*7c478bd9Sstevel@tonic-gate }
831*7c478bd9Sstevel@tonic-gate 
832*7c478bd9Sstevel@tonic-gate static int
833*7c478bd9Sstevel@tonic-gate fco_exit(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
834*7c478bd9Sstevel@tonic-gate {
835*7c478bd9Sstevel@tonic-gate 	FC_DEBUG0(1, CE_CONT, "exit op not implemented .. succeeding\n");
836*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(0);
837*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
838*7c478bd9Sstevel@tonic-gate }
839*7c478bd9Sstevel@tonic-gate 
840*7c478bd9Sstevel@tonic-gate /*
841*7c478bd9Sstevel@tonic-gate  * Needed to implement 'mac-address' Fcode, no obvious place to pick this
842*7c478bd9Sstevel@tonic-gate  * info up from user-land.
843*7c478bd9Sstevel@tonic-gate  */
844*7c478bd9Sstevel@tonic-gate static int
845*7c478bd9Sstevel@tonic-gate fco_local_ether_addr(dev_info_t *ap, fco_handle_t rp, fc_ci_t *cp)
846*7c478bd9Sstevel@tonic-gate {
847*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nargs) != 0)
848*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nargs must be 0"));
849*7c478bd9Sstevel@tonic-gate 
850*7c478bd9Sstevel@tonic-gate 	if (fc_cell2int(cp->nresults) != 2)
851*7c478bd9Sstevel@tonic-gate 		return (fc_syntax_error(cp, "nresults must be 2"));
852*7c478bd9Sstevel@tonic-gate 
853*7c478bd9Sstevel@tonic-gate 	cp->nresults = fc_int2cell(2);
854*7c478bd9Sstevel@tonic-gate 
855*7c478bd9Sstevel@tonic-gate 	(void) localetheraddr(NULL, (struct ether_addr *)(&fc_result(cp, 0)));
856*7c478bd9Sstevel@tonic-gate 
857*7c478bd9Sstevel@tonic-gate 	return (fc_success_op(ap, rp, cp));
858*7c478bd9Sstevel@tonic-gate }
859*7c478bd9Sstevel@tonic-gate 
860*7c478bd9Sstevel@tonic-gate #ifdef DEBUG
861*7c478bd9Sstevel@tonic-gate void
862*7c478bd9Sstevel@tonic-gate fc_debug(char *fmt, uintptr_t a1, uintptr_t a2, uintptr_t a3,
863*7c478bd9Sstevel@tonic-gate 	uintptr_t a4, uintptr_t a5)
864*7c478bd9Sstevel@tonic-gate {
865*7c478bd9Sstevel@tonic-gate 	cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5);
866*7c478bd9Sstevel@tonic-gate }
867*7c478bd9Sstevel@tonic-gate #endif
868