xref: /onnv-gate/usr/src/uts/common/io/rge/rge_ndd.c (revision 11410:c47901fe35a4)
1744Sgs150176 /*
2744Sgs150176  * CDDL HEADER START
3744Sgs150176  *
4744Sgs150176  * The contents of this file are subject to the terms of the
52544Sgs150176  * Common Development and Distribution License (the "License").
62544Sgs150176  * You may not use this file except in compliance with the License.
7744Sgs150176  *
8744Sgs150176  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9744Sgs150176  * or http://www.opensolaris.org/os/licensing.
10744Sgs150176  * See the License for the specific language governing permissions
11744Sgs150176  * and limitations under the License.
12744Sgs150176  *
13744Sgs150176  * When distributing Covered Code, include this CDDL HEADER in each
14744Sgs150176  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15744Sgs150176  * If applicable, add the following below this CDDL HEADER, with the
16744Sgs150176  * fields enclosed by brackets "[]" replaced with your own identifying
17744Sgs150176  * information: Portions Copyright [yyyy] [name of copyright owner]
18744Sgs150176  *
19744Sgs150176  * CDDL HEADER END
20744Sgs150176  */
21744Sgs150176 /*
22*11410SLi-Zhen.You@Sun.COM  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23744Sgs150176  * Use is subject to license terms.
24744Sgs150176  */
25744Sgs150176 
26744Sgs150176 #include "rge.h"
27744Sgs150176 
28744Sgs150176 #define	RGE_DBG		RGE_DBG_NDD	/* debug flag for this code	*/
29744Sgs150176 
30744Sgs150176 /*
31744Sgs150176  * Property names
32744Sgs150176  */
33744Sgs150176 static char transfer_speed_propname[] = "transfer-speed";
34744Sgs150176 static char speed_propname[] = "speed";
35744Sgs150176 static char duplex_propname[] = "full-duplex";
36744Sgs150176 
37744Sgs150176 /*
38744Sgs150176  * Notes:
39744Sgs150176  *	The first character of the <name> field encodes the read/write
40744Sgs150176  *	status of the parameter:
41744Sgs150176  *		'-' => read-only,
42744Sgs150176  *		'+' => read/write,
43744Sgs150176  *		'!' => invisible!
44744Sgs150176  *
45744Sgs150176  *	For writable parameters, we check for a driver property with the
46744Sgs150176  *	same name; if found, and its value is in range, we initialise
47744Sgs150176  *	the parameter from the property, overriding the default in the
48744Sgs150176  *	table below.
49744Sgs150176  *
50744Sgs150176  *	A NULL in the <name> field terminates the array.
51744Sgs150176  *
52744Sgs150176  *	The <info> field is used here to provide the index of the
53744Sgs150176  *	parameter to be initialised; thus it doesn't matter whether
54744Sgs150176  *	this table is kept ordered or not.
55744Sgs150176  *
56744Sgs150176  *	The <info> field in the per-instance copy, on the other hand,
57744Sgs150176  *	is used to count assignments so that we can tell when a magic
58744Sgs150176  *	parameter has been set via ndd (see rge_param_set()).
59744Sgs150176  */
605735Smx205022 static const nd_param_t nd_template_1000[] = {
61744Sgs150176 /*	info		min	max	init	r/w+name		*/
62744Sgs150176 
63744Sgs150176 /* Our hardware capabilities */
64744Sgs150176 { PARAM_AUTONEG_CAP,	    0,	  1,	1,	"-autoneg_cap"		},
65744Sgs150176 { PARAM_PAUSE_CAP,	    0,	  1,	1,	"-pause_cap"		},
66744Sgs150176 { PARAM_ASYM_PAUSE_CAP,	    0,	  1,	1,	"-asym_pause_cap"	},
67744Sgs150176 { PARAM_1000FDX_CAP,	    0,	  1,	1,	"-1000fdx_cap"		},
68744Sgs150176 { PARAM_1000HDX_CAP,	    0,	  1,	0,	"-1000hdx_cap"		},
69744Sgs150176 { PARAM_100T4_CAP,	    0,	  1,	0,	"-100T4_cap"		},
70744Sgs150176 { PARAM_100FDX_CAP,	    0,	  1,	1,	"-100fdx_cap"		},
71744Sgs150176 { PARAM_100HDX_CAP,	    0,	  1,	1,	"-100hdx_cap"		},
72744Sgs150176 { PARAM_10FDX_CAP,	    0,	  1,	1,	"-10fdx_cap"		},
73744Sgs150176 { PARAM_10HDX_CAP,	    0,	  1,	1,	"-10hdx_cap"		},
74744Sgs150176 
75744Sgs150176 /* Our advertised capabilities */
76744Sgs150176 { PARAM_ADV_AUTONEG_CAP,    0,	  1,	1,	"-adv_autoneg_cap"	},
77744Sgs150176 { PARAM_ADV_PAUSE_CAP,	    0,	  1,	1,	"+adv_pause_cap"	},
78744Sgs150176 { PARAM_ADV_ASYM_PAUSE_CAP, 0,	  1,	1,	"+adv_asym_pause_cap"	},
79744Sgs150176 { PARAM_ADV_1000FDX_CAP,    0,	  1,	1,	"+adv_1000fdx_cap"	},
80744Sgs150176 { PARAM_ADV_1000HDX_CAP,    0,	  1,	0,	"-adv_1000hdx_cap"	},
81744Sgs150176 { PARAM_ADV_100T4_CAP,	    0,	  1,	0,	"-adv_100T4_cap"	},
82744Sgs150176 { PARAM_ADV_100FDX_CAP,	    0,	  1,	1,	"+adv_100fdx_cap"	},
83744Sgs150176 { PARAM_ADV_100HDX_CAP,	    0,	  1,	1,	"+adv_100hdx_cap"	},
84744Sgs150176 { PARAM_ADV_10FDX_CAP,	    0,	  1,	1,	"+adv_10fdx_cap"	},
85744Sgs150176 { PARAM_ADV_10HDX_CAP,	    0,	  1,	1,	"+adv_10hdx_cap"	},
86744Sgs150176 
87744Sgs150176 /* Current operating modes */
88744Sgs150176 { PARAM_LINK_STATUS,	    0,	  1,	0,	"-link_status"		},
89744Sgs150176 { PARAM_LINK_SPEED,	    0,    1000,	0,	"-link_speed"		},
90744Sgs150176 { PARAM_LINK_DUPLEX,	    0,	  2,	0,	"-link_duplex"		},
91744Sgs150176 
92744Sgs150176 /* Loopback status */
93744Sgs150176 { PARAM_LOOP_MODE,	    0,	  2,	0,	"-loop_mode"		},
94744Sgs150176 
95744Sgs150176 /* Terminator */
96744Sgs150176 { PARAM_COUNT,		    0,	  0,	0,	NULL			}
97744Sgs150176 };
98744Sgs150176 
995735Smx205022 /* nd_template for RTL8101E */
1005735Smx205022 static const nd_param_t nd_template_100[] = {
1015735Smx205022 /*	info		min	max	init	r/w+name		*/
1025735Smx205022 
1035735Smx205022 /* Our hardware capabilities */
1045735Smx205022 { PARAM_AUTONEG_CAP,	    0,	  1,	1,	"-autoneg_cap"		},
1055735Smx205022 { PARAM_PAUSE_CAP,	    0,	  1,	1,	"-pause_cap"		},
1065735Smx205022 { PARAM_ASYM_PAUSE_CAP,	    0,	  1,	1,	"-asym_pause_cap"	},
1075735Smx205022 { PARAM_1000FDX_CAP,	    0,	  1,	0,	"-1000fdx_cap"		},
1085735Smx205022 { PARAM_1000HDX_CAP,	    0,	  1,	0,	"-1000hdx_cap"		},
1095735Smx205022 { PARAM_100T4_CAP,	    0,	  1,	0,	"-100T4_cap"		},
1105735Smx205022 { PARAM_100FDX_CAP,	    0,	  1,	1,	"-100fdx_cap"		},
1115735Smx205022 { PARAM_100HDX_CAP,	    0,	  1,	1,	"-100hdx_cap"		},
1125735Smx205022 { PARAM_10FDX_CAP,	    0,	  1,	1,	"-10fdx_cap"		},
1135735Smx205022 { PARAM_10HDX_CAP,	    0,	  1,	1,	"-10hdx_cap"		},
1145735Smx205022 
1155735Smx205022 /* Our advertised capabilities */
1165735Smx205022 { PARAM_ADV_AUTONEG_CAP,    0,	  1,	1,	"-adv_autoneg_cap"	},
1175735Smx205022 { PARAM_ADV_PAUSE_CAP,	    0,	  1,	1,	"+adv_pause_cap"	},
1185735Smx205022 { PARAM_ADV_ASYM_PAUSE_CAP, 0,	  1,	1,	"+adv_asym_pause_cap"	},
1195735Smx205022 { PARAM_ADV_1000FDX_CAP,    0,	  1,	0,	"-adv_1000fdx_cap"	},
1205735Smx205022 { PARAM_ADV_1000HDX_CAP,    0,	  1,	0,	"-adv_1000hdx_cap"	},
1215735Smx205022 { PARAM_ADV_100T4_CAP,	    0,	  1,	0,	"-adv_100T4_cap"	},
1225735Smx205022 { PARAM_ADV_100FDX_CAP,	    0,	  1,	1,	"+adv_100fdx_cap"	},
1235735Smx205022 { PARAM_ADV_100HDX_CAP,	    0,	  1,	1,	"+adv_100hdx_cap"	},
1245735Smx205022 { PARAM_ADV_10FDX_CAP,	    0,	  1,	1,	"+adv_10fdx_cap"	},
1255735Smx205022 { PARAM_ADV_10HDX_CAP,	    0,	  1,	1,	"+adv_10hdx_cap"	},
1265735Smx205022 
1275735Smx205022 /* Current operating modes */
1285735Smx205022 { PARAM_LINK_STATUS,	    0,	  1,	0,	"-link_status"		},
1295735Smx205022 { PARAM_LINK_SPEED,	    0,    1000,	0,	"-link_speed"		},
1305735Smx205022 { PARAM_LINK_DUPLEX,	    0,	  2,	0,	"-link_duplex"		},
1315735Smx205022 
1325735Smx205022 /* Loopback status */
1335735Smx205022 { PARAM_LOOP_MODE,	    0,	  2,	0,	"-loop_mode"		},
1345735Smx205022 
1355735Smx205022 /* Terminator */
1365735Smx205022 { PARAM_COUNT,		    0,	  0,	0,	NULL			}
1375735Smx205022 };
138744Sgs150176 
139744Sgs150176 /*  ============== NDD Support Functions ===============  */
140744Sgs150176 
141744Sgs150176 /*
142744Sgs150176  * Extracts the value from the rge parameter array and prints
143744Sgs150176  * the parameter value. cp points to the required parameter.
144744Sgs150176  */
145744Sgs150176 static int
rge_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * credp)146744Sgs150176 rge_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp)
147744Sgs150176 {
148744Sgs150176 	nd_param_t *ndp;
149744Sgs150176 
150744Sgs150176 	_NOTE(ARGUNUSED(q, credp))
151744Sgs150176 
152744Sgs150176 	ndp = (nd_param_t *)cp;
153744Sgs150176 	(void) mi_mpprintf(mp, "%d", ndp->ndp_val);
154744Sgs150176 
155744Sgs150176 	return (0);
156744Sgs150176 }
157744Sgs150176 
158744Sgs150176 /*
159744Sgs150176  * Validates the request to set a RGE parameter to a specific value.
160744Sgs150176  * If the request is OK, the parameter is set.  Also the <info> field
161744Sgs150176  * is incremented to show that the parameter was touched, even though
162744Sgs150176  * it may have been set to the same value it already had.
163744Sgs150176  */
164744Sgs150176 static int
rge_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * credp)165744Sgs150176 rge_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *credp)
166744Sgs150176 {
167744Sgs150176 	nd_param_t *ndp;
168744Sgs150176 	long new_value;
169744Sgs150176 
170744Sgs150176 	_NOTE(ARGUNUSED(q, mp, credp))
171744Sgs150176 
172744Sgs150176 	ndp = (nd_param_t *)cp;
173*11410SLi-Zhen.You@Sun.COM 	(void) ddi_strtol(value, (char **)NULL, 0, &new_value);
174744Sgs150176 	if (new_value < ndp->ndp_min || new_value > ndp->ndp_max)
175744Sgs150176 		return (EINVAL);
176744Sgs150176 
1777504SJing.Sun@Sun.COM 	ndp->ndp_val = (int)new_value;
178744Sgs150176 	ndp->ndp_info += 1;
179744Sgs150176 	return (0);
180744Sgs150176 }
181744Sgs150176 
182744Sgs150176 /*
183744Sgs150176  * Initialise the per-instance parameter array from the global prototype,
184744Sgs150176  * and register each element with the named dispatch handler using nd_load()
185744Sgs150176  */
186744Sgs150176 static int
rge_param_register(rge_t * rgep)187744Sgs150176 rge_param_register(rge_t *rgep)
188744Sgs150176 {
189744Sgs150176 	const nd_param_t *tmplp;
190744Sgs150176 	dev_info_t *dip;
191744Sgs150176 	nd_param_t *ndp;
192744Sgs150176 	caddr_t *nddpp;
193744Sgs150176 	pfi_t setfn;
194744Sgs150176 	char *nm;
195744Sgs150176 	int pval;
196744Sgs150176 
197744Sgs150176 	dip = rgep->devinfo;
198744Sgs150176 	nddpp = &rgep->nd_data_p;
199744Sgs150176 	ASSERT(*nddpp == NULL);
200744Sgs150176 
2015735Smx205022 	if (rgep->chipid.mac_ver == MAC_VER_8101E)
2025735Smx205022 		tmplp = nd_template_100;
2035735Smx205022 	else
2045735Smx205022 		tmplp = nd_template_1000;
2055735Smx205022 
2065735Smx205022 	for (; tmplp->ndp_name != NULL; ++tmplp) {
207744Sgs150176 		/*
208744Sgs150176 		 * Copy the template from nd_template[] into the
209744Sgs150176 		 * proper slot in the per-instance parameters,
210744Sgs150176 		 * then register the parameter with nd_load()
211744Sgs150176 		 */
212744Sgs150176 		ndp = &rgep->nd_params[tmplp->ndp_info];
213744Sgs150176 		*ndp = *tmplp;
214744Sgs150176 		nm = &ndp->ndp_name[0];
215744Sgs150176 		setfn = rge_param_set;
216744Sgs150176 
217744Sgs150176 		switch (*nm) {
218744Sgs150176 		default:
219744Sgs150176 		case '!':
220744Sgs150176 			continue;
221744Sgs150176 
222744Sgs150176 		case '+':
223744Sgs150176 			break;
224744Sgs150176 
225744Sgs150176 		case '-':
226744Sgs150176 			setfn = NULL;
227744Sgs150176 			break;
228744Sgs150176 		}
229744Sgs150176 
230744Sgs150176 		if (!nd_load(nddpp, ++nm, rge_param_get, setfn, (caddr_t)ndp))
231744Sgs150176 			goto nd_fail;
232744Sgs150176 
233744Sgs150176 		/*
234744Sgs150176 		 * If the parameter is writable, and there's a property
235744Sgs150176 		 * with the same name, and its value is in range, we use
236744Sgs150176 		 * it to initialise the parameter.  If it exists but is
237744Sgs150176 		 * out of range, it's ignored.
238744Sgs150176 		 */
239744Sgs150176 		if (setfn && RGE_PROP_EXISTS(dip, nm)) {
240744Sgs150176 			pval = RGE_PROP_GET_INT(dip, nm);
241744Sgs150176 			if (pval >= ndp->ndp_min && pval <= ndp->ndp_max)
242744Sgs150176 				ndp->ndp_val = pval;
243744Sgs150176 		}
244744Sgs150176 	}
245744Sgs150176 
246744Sgs150176 	RGE_DEBUG(("rge_param_register: OK"));
247744Sgs150176 	return (DDI_SUCCESS);
248744Sgs150176 
249744Sgs150176 nd_fail:
2505735Smx205022 	if (rgep->chipid.mac_ver == MAC_VER_8101E) {
2515735Smx205022 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
2525735Smx205022 		    tmplp-nd_template_100, tmplp->ndp_info));
2535735Smx205022 	} else {
2545735Smx205022 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
2555735Smx205022 		    tmplp-nd_template_1000, tmplp->ndp_info));
2565735Smx205022 	}
257744Sgs150176 	nd_free(nddpp);
258744Sgs150176 	return (DDI_FAILURE);
259744Sgs150176 }
260744Sgs150176 
261744Sgs150176 int
rge_nd_init(rge_t * rgep)262744Sgs150176 rge_nd_init(rge_t *rgep)
263744Sgs150176 {
264744Sgs150176 	dev_info_t *dip;
265744Sgs150176 	int duplex;
266744Sgs150176 	int speed;
267744Sgs150176 
268744Sgs150176 	/*
269744Sgs150176 	 * Register all the per-instance properties, initialising
270744Sgs150176 	 * them from the table above or from driver properties set
271744Sgs150176 	 * in the .conf file
272744Sgs150176 	 */
273744Sgs150176 	if (rge_param_register(rgep) != DDI_SUCCESS)
274744Sgs150176 		return (-1);
275744Sgs150176 
276744Sgs150176 	/*
277744Sgs150176 	 * The link speed may be forced to 10, 100 or 1000 Mbps using
278744Sgs150176 	 * the property "transfer-speed". This may be done in OBP by
279744Sgs150176 	 * using the command "apply transfer-speed=<speed> <device>".
280744Sgs150176 	 * The speed may be 10, 100 or 1000 - any other value will be
281744Sgs150176 	 * ignored.  Note that this does *enables* autonegotiation, but
282744Sgs150176 	 * restricts it to the speed specified by the property.
283744Sgs150176 	 */
284744Sgs150176 	dip = rgep->devinfo;
285744Sgs150176 	if (RGE_PROP_EXISTS(dip, transfer_speed_propname)) {
286744Sgs150176 
287744Sgs150176 		speed = RGE_PROP_GET_INT(dip, transfer_speed_propname);
288744Sgs150176 		rge_log(rgep, "%s property is %d",
2895735Smx205022 		    transfer_speed_propname, speed);
290744Sgs150176 
291744Sgs150176 		switch (speed) {
292744Sgs150176 		case 1000:
293744Sgs150176 			rgep->param_adv_autoneg = 1;
294744Sgs150176 			rgep->param_adv_1000fdx = 1;
295744Sgs150176 			rgep->param_adv_1000hdx = 1;
296744Sgs150176 			rgep->param_adv_100fdx = 0;
297744Sgs150176 			rgep->param_adv_100hdx = 0;
298744Sgs150176 			rgep->param_adv_10fdx = 0;
299744Sgs150176 			rgep->param_adv_10hdx = 0;
300744Sgs150176 			break;
301744Sgs150176 
302744Sgs150176 		case 100:
303744Sgs150176 			rgep->param_adv_autoneg = 1;
304744Sgs150176 			rgep->param_adv_1000fdx = 0;
305744Sgs150176 			rgep->param_adv_1000hdx = 0;
306744Sgs150176 			rgep->param_adv_100fdx = 1;
307744Sgs150176 			rgep->param_adv_100hdx = 1;
308744Sgs150176 			rgep->param_adv_10fdx = 0;
309744Sgs150176 			rgep->param_adv_10hdx = 0;
310744Sgs150176 			break;
311744Sgs150176 
312744Sgs150176 		case 10:
313744Sgs150176 			rgep->param_adv_autoneg = 1;
314744Sgs150176 			rgep->param_adv_1000fdx = 0;
315744Sgs150176 			rgep->param_adv_1000hdx = 0;
316744Sgs150176 			rgep->param_adv_100fdx = 0;
317744Sgs150176 			rgep->param_adv_100hdx = 0;
318744Sgs150176 			rgep->param_adv_10fdx = 1;
319744Sgs150176 			rgep->param_adv_10hdx = 1;
320744Sgs150176 			break;
321744Sgs150176 
322744Sgs150176 		default:
323744Sgs150176 			break;
324744Sgs150176 		}
325744Sgs150176 	}
326744Sgs150176 
327744Sgs150176 	/*
328744Sgs150176 	 * Also check the "speed" and "full-duplex" properties.  Setting
329744Sgs150176 	 * these properties will override all other settings and *disable*
330744Sgs150176 	 * autonegotiation, so both should be specified if either one is.
331744Sgs150176 	 * Otherwise, the unspecified parameter will be set to a default
332744Sgs150176 	 * value (1000Mb/s, full-duplex).
333744Sgs150176 	 */
334744Sgs150176 	if (RGE_PROP_EXISTS(dip, speed_propname) ||
335744Sgs150176 	    RGE_PROP_EXISTS(dip, duplex_propname)) {
336744Sgs150176 
337744Sgs150176 		rgep->param_adv_autoneg = 0;
338744Sgs150176 		rgep->param_adv_1000fdx = 1;
339744Sgs150176 		rgep->param_adv_1000hdx = 1;
340744Sgs150176 		rgep->param_adv_100fdx = 1;
341744Sgs150176 		rgep->param_adv_100hdx = 1;
342744Sgs150176 		rgep->param_adv_10fdx = 1;
343744Sgs150176 		rgep->param_adv_10hdx = 1;
344744Sgs150176 
345744Sgs150176 		speed = RGE_PROP_GET_INT(dip, speed_propname);
346744Sgs150176 		duplex = RGE_PROP_GET_INT(dip, duplex_propname);
347744Sgs150176 		rge_log(rgep, "%s property is %d",
3485735Smx205022 		    speed_propname, speed);
349744Sgs150176 		rge_log(rgep, "%s property is %d",
3505735Smx205022 		    duplex_propname, duplex);
351744Sgs150176 
352744Sgs150176 		switch (speed) {
353744Sgs150176 		case 1000:
354744Sgs150176 		default:
355744Sgs150176 			rgep->param_adv_100fdx = 0;
356744Sgs150176 			rgep->param_adv_100hdx = 0;
357744Sgs150176 			rgep->param_adv_10fdx = 0;
358744Sgs150176 			rgep->param_adv_10hdx = 0;
359744Sgs150176 			break;
360744Sgs150176 
361744Sgs150176 		case 100:
362744Sgs150176 			rgep->param_adv_1000fdx = 0;
363744Sgs150176 			rgep->param_adv_1000hdx = 0;
364744Sgs150176 			rgep->param_adv_10fdx = 0;
365744Sgs150176 			rgep->param_adv_10hdx = 0;
366744Sgs150176 			break;
367744Sgs150176 
368744Sgs150176 		case 10:
369744Sgs150176 			rgep->param_adv_1000fdx = 0;
370744Sgs150176 			rgep->param_adv_1000hdx = 0;
371744Sgs150176 			rgep->param_adv_100fdx = 0;
372744Sgs150176 			rgep->param_adv_100hdx = 0;
373744Sgs150176 			break;
374744Sgs150176 		}
375744Sgs150176 
376744Sgs150176 		switch (duplex) {
377744Sgs150176 		default:
378744Sgs150176 		case 1:
379744Sgs150176 			rgep->param_adv_1000hdx = 0;
380744Sgs150176 			rgep->param_adv_100hdx = 0;
381744Sgs150176 			rgep->param_adv_10hdx = 0;
382744Sgs150176 			break;
383744Sgs150176 
384744Sgs150176 		case 0:
385744Sgs150176 			rgep->param_adv_1000fdx = 0;
386744Sgs150176 			rgep->param_adv_100fdx = 0;
387744Sgs150176 			rgep->param_adv_10fdx = 0;
388744Sgs150176 			break;
389744Sgs150176 		}
390744Sgs150176 	}
391744Sgs150176 
392744Sgs150176 	RGE_DEBUG(("rge_nd_init: autoneg %d"
3935735Smx205022 	    "pause %d asym_pause %d "
3945735Smx205022 	    "1000fdx %d 1000hdx %d "
3955735Smx205022 	    "100fdx %d 100hdx %d "
3965735Smx205022 	    "10fdx %d 10hdx %d ",
3975735Smx205022 	    rgep->param_adv_autoneg,
3985735Smx205022 	    rgep->param_adv_pause, rgep->param_adv_asym_pause,
3995735Smx205022 	    rgep->param_adv_1000fdx, rgep->param_adv_1000hdx,
4005735Smx205022 	    rgep->param_adv_100fdx, rgep->param_adv_100hdx,
4015735Smx205022 	    rgep->param_adv_10fdx, rgep->param_adv_10hdx));
402744Sgs150176 
403744Sgs150176 	return (0);
404744Sgs150176 }
405744Sgs150176 
406744Sgs150176 enum ioc_reply
rge_nd_ioctl(rge_t * rgep,queue_t * wq,mblk_t * mp,struct iocblk * iocp)407744Sgs150176 rge_nd_ioctl(rge_t *rgep, queue_t *wq, mblk_t *mp, struct iocblk *iocp)
408744Sgs150176 {
409744Sgs150176 	nd_param_t *ndp;
410744Sgs150176 	boolean_t ok;
411744Sgs150176 	int info;
412744Sgs150176 	int cmd;
413744Sgs150176 
414744Sgs150176 	RGE_TRACE(("rge_nd_ioctl($%p, $%p, $%p, $%p)",
4155735Smx205022 	    (void *)rgep, (void *)wq, (void *)mp, (void *)iocp));
416744Sgs150176 
417744Sgs150176 	ASSERT(mutex_owned(rgep->genlock));
418744Sgs150176 
419744Sgs150176 	cmd = iocp->ioc_cmd;
420744Sgs150176 	switch (cmd) {
421744Sgs150176 	default:
422744Sgs150176 		/* NOTREACHED */
423744Sgs150176 		rge_error(rgep, "rge_nd_ioctl: invalid cmd 0x%x", cmd);
424744Sgs150176 		return (IOC_INVAL);
425744Sgs150176 
426744Sgs150176 	case ND_GET:
427744Sgs150176 		/*
428744Sgs150176 		 * If nd_getset() returns B_FALSE, the command was
429744Sgs150176 		 * not valid (e.g. unknown name), so we just tell the
430744Sgs150176 		 * top-level ioctl code to send a NAK (with code EINVAL).
431744Sgs150176 		 *
432744Sgs150176 		 * Otherwise, nd_getset() will have built the reply to
433744Sgs150176 		 * be sent (but not actually sent it), so we tell the
434744Sgs150176 		 * caller to send the prepared reply.
435744Sgs150176 		 */
436744Sgs150176 		ok = nd_getset(wq, rgep->nd_data_p, mp);
437744Sgs150176 		RGE_DEBUG(("rge_nd_ioctl: get %s", ok ? "OK" : "FAIL"));
438744Sgs150176 		return (ok ? IOC_REPLY : IOC_INVAL);
439744Sgs150176 
440744Sgs150176 	case ND_SET:
441744Sgs150176 		/*
442744Sgs150176 		 * All adv_* parameters are locked (read-only) while
443744Sgs150176 		 * the device is in any sort of loopback mode ...
444744Sgs150176 		 */
445744Sgs150176 		if (rgep->param_loop_mode != RGE_LOOP_NONE) {
446744Sgs150176 			iocp->ioc_error = EBUSY;
447744Sgs150176 			return (IOC_INVAL);
448744Sgs150176 		}
449744Sgs150176 
450744Sgs150176 		/*
451744Sgs150176 		 * Before calling nd_getset(), we save the <info> field
452744Sgs150176 		 * of the 'autonegotiation' parameter so that we can tell
453744Sgs150176 		 * whether it was assigned (even if its value doesn't
454744Sgs150176 		 * actually change).
455744Sgs150176 		 */
456744Sgs150176 		ndp = &rgep->nd_params[PARAM_ADV_AUTONEG_CAP];
457744Sgs150176 		info = ndp->ndp_info;
458744Sgs150176 		ok = nd_getset(wq, rgep->nd_data_p, mp);
459744Sgs150176 
460744Sgs150176 		/*
461744Sgs150176 		 * If nd_getset() returns B_FALSE, the command was
462744Sgs150176 		 * not valid (e.g. unknown name), so we just tell
463744Sgs150176 		 * the top-level ioctl code to send a NAK (with code
464744Sgs150176 		 * EINVAL by default).
465744Sgs150176 		 *
466744Sgs150176 		 * Otherwise, nd_getset() will have built the reply to
467744Sgs150176 		 * be sent - but that doesn't imply success!  In some
468744Sgs150176 		 * cases, the reply it's built will have a non-zero
469744Sgs150176 		 * error code in it (e.g. EPERM if not superuser).
470744Sgs150176 		 * So, we also drop out in that case ...
471744Sgs150176 		 */
472744Sgs150176 		RGE_DEBUG(("rge_nd_ioctl: set %s err %d autoneg %d info %d/%d",
4735735Smx205022 		    ok ? "OK" : "FAIL", iocp->ioc_error,
4745735Smx205022 		    ndp->ndp_val, info, ndp->ndp_info));
475744Sgs150176 		if (!ok)
476744Sgs150176 			return (IOC_INVAL);
477744Sgs150176 		if (iocp->ioc_error)
478744Sgs150176 			return (IOC_REPLY);
479744Sgs150176 
480744Sgs150176 		return (IOC_RESTART_REPLY);
481744Sgs150176 	}
482744Sgs150176 }
483744Sgs150176 
484744Sgs150176 /* Free the Named Dispatch Table by calling nd_free */
485744Sgs150176 void
rge_nd_cleanup(rge_t * rgep)486744Sgs150176 rge_nd_cleanup(rge_t *rgep)
487744Sgs150176 {
488744Sgs150176 	nd_free(&rgep->nd_data_p);
489744Sgs150176 }
490