xref: /onnv-gate/usr/src/uts/common/io/rge/rge_ndd.c (revision 7504:169583c6a79e)
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*7504SJing.Sun@Sun.COM  * Copyright 2008 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
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
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 	char *end;
170744Sgs150176 
171744Sgs150176 	_NOTE(ARGUNUSED(q, mp, credp))
172744Sgs150176 
173744Sgs150176 	ndp = (nd_param_t *)cp;
174744Sgs150176 	new_value = mi_strtol(value, &end, 10);
175744Sgs150176 	if (end == value)
176744Sgs150176 		return (EINVAL);
177744Sgs150176 	if (new_value < ndp->ndp_min || new_value > ndp->ndp_max)
178744Sgs150176 		return (EINVAL);
179744Sgs150176 
180*7504SJing.Sun@Sun.COM 	ndp->ndp_val = (int)new_value;
181744Sgs150176 	ndp->ndp_info += 1;
182744Sgs150176 	return (0);
183744Sgs150176 }
184744Sgs150176 
185744Sgs150176 /*
186744Sgs150176  * Initialise the per-instance parameter array from the global prototype,
187744Sgs150176  * and register each element with the named dispatch handler using nd_load()
188744Sgs150176  */
189744Sgs150176 static int
190744Sgs150176 rge_param_register(rge_t *rgep)
191744Sgs150176 {
192744Sgs150176 	const nd_param_t *tmplp;
193744Sgs150176 	dev_info_t *dip;
194744Sgs150176 	nd_param_t *ndp;
195744Sgs150176 	caddr_t *nddpp;
196744Sgs150176 	pfi_t setfn;
197744Sgs150176 	char *nm;
198744Sgs150176 	int pval;
199744Sgs150176 
200744Sgs150176 	dip = rgep->devinfo;
201744Sgs150176 	nddpp = &rgep->nd_data_p;
202744Sgs150176 	ASSERT(*nddpp == NULL);
203744Sgs150176 
2045735Smx205022 	if (rgep->chipid.mac_ver == MAC_VER_8101E)
2055735Smx205022 		tmplp = nd_template_100;
2065735Smx205022 	else
2075735Smx205022 		tmplp = nd_template_1000;
2085735Smx205022 
2095735Smx205022 	for (; tmplp->ndp_name != NULL; ++tmplp) {
210744Sgs150176 		/*
211744Sgs150176 		 * Copy the template from nd_template[] into the
212744Sgs150176 		 * proper slot in the per-instance parameters,
213744Sgs150176 		 * then register the parameter with nd_load()
214744Sgs150176 		 */
215744Sgs150176 		ndp = &rgep->nd_params[tmplp->ndp_info];
216744Sgs150176 		*ndp = *tmplp;
217744Sgs150176 		nm = &ndp->ndp_name[0];
218744Sgs150176 		setfn = rge_param_set;
219744Sgs150176 
220744Sgs150176 		switch (*nm) {
221744Sgs150176 		default:
222744Sgs150176 		case '!':
223744Sgs150176 			continue;
224744Sgs150176 
225744Sgs150176 		case '+':
226744Sgs150176 			break;
227744Sgs150176 
228744Sgs150176 		case '-':
229744Sgs150176 			setfn = NULL;
230744Sgs150176 			break;
231744Sgs150176 		}
232744Sgs150176 
233744Sgs150176 		if (!nd_load(nddpp, ++nm, rge_param_get, setfn, (caddr_t)ndp))
234744Sgs150176 			goto nd_fail;
235744Sgs150176 
236744Sgs150176 		/*
237744Sgs150176 		 * If the parameter is writable, and there's a property
238744Sgs150176 		 * with the same name, and its value is in range, we use
239744Sgs150176 		 * it to initialise the parameter.  If it exists but is
240744Sgs150176 		 * out of range, it's ignored.
241744Sgs150176 		 */
242744Sgs150176 		if (setfn && RGE_PROP_EXISTS(dip, nm)) {
243744Sgs150176 			pval = RGE_PROP_GET_INT(dip, nm);
244744Sgs150176 			if (pval >= ndp->ndp_min && pval <= ndp->ndp_max)
245744Sgs150176 				ndp->ndp_val = pval;
246744Sgs150176 		}
247744Sgs150176 	}
248744Sgs150176 
249744Sgs150176 	RGE_DEBUG(("rge_param_register: OK"));
250744Sgs150176 	return (DDI_SUCCESS);
251744Sgs150176 
252744Sgs150176 nd_fail:
2535735Smx205022 	if (rgep->chipid.mac_ver == MAC_VER_8101E) {
2545735Smx205022 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
2555735Smx205022 		    tmplp-nd_template_100, tmplp->ndp_info));
2565735Smx205022 	} else {
2575735Smx205022 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
2585735Smx205022 		    tmplp-nd_template_1000, tmplp->ndp_info));
2595735Smx205022 	}
260744Sgs150176 	nd_free(nddpp);
261744Sgs150176 	return (DDI_FAILURE);
262744Sgs150176 }
263744Sgs150176 
264744Sgs150176 int
265744Sgs150176 rge_nd_init(rge_t *rgep)
266744Sgs150176 {
267744Sgs150176 	dev_info_t *dip;
268744Sgs150176 	int duplex;
269744Sgs150176 	int speed;
270744Sgs150176 
271744Sgs150176 	/*
272744Sgs150176 	 * Register all the per-instance properties, initialising
273744Sgs150176 	 * them from the table above or from driver properties set
274744Sgs150176 	 * in the .conf file
275744Sgs150176 	 */
276744Sgs150176 	if (rge_param_register(rgep) != DDI_SUCCESS)
277744Sgs150176 		return (-1);
278744Sgs150176 
279744Sgs150176 	/*
280744Sgs150176 	 * The link speed may be forced to 10, 100 or 1000 Mbps using
281744Sgs150176 	 * the property "transfer-speed". This may be done in OBP by
282744Sgs150176 	 * using the command "apply transfer-speed=<speed> <device>".
283744Sgs150176 	 * The speed may be 10, 100 or 1000 - any other value will be
284744Sgs150176 	 * ignored.  Note that this does *enables* autonegotiation, but
285744Sgs150176 	 * restricts it to the speed specified by the property.
286744Sgs150176 	 */
287744Sgs150176 	dip = rgep->devinfo;
288744Sgs150176 	if (RGE_PROP_EXISTS(dip, transfer_speed_propname)) {
289744Sgs150176 
290744Sgs150176 		speed = RGE_PROP_GET_INT(dip, transfer_speed_propname);
291744Sgs150176 		rge_log(rgep, "%s property is %d",
2925735Smx205022 		    transfer_speed_propname, speed);
293744Sgs150176 
294744Sgs150176 		switch (speed) {
295744Sgs150176 		case 1000:
296744Sgs150176 			rgep->param_adv_autoneg = 1;
297744Sgs150176 			rgep->param_adv_1000fdx = 1;
298744Sgs150176 			rgep->param_adv_1000hdx = 1;
299744Sgs150176 			rgep->param_adv_100fdx = 0;
300744Sgs150176 			rgep->param_adv_100hdx = 0;
301744Sgs150176 			rgep->param_adv_10fdx = 0;
302744Sgs150176 			rgep->param_adv_10hdx = 0;
303744Sgs150176 			break;
304744Sgs150176 
305744Sgs150176 		case 100:
306744Sgs150176 			rgep->param_adv_autoneg = 1;
307744Sgs150176 			rgep->param_adv_1000fdx = 0;
308744Sgs150176 			rgep->param_adv_1000hdx = 0;
309744Sgs150176 			rgep->param_adv_100fdx = 1;
310744Sgs150176 			rgep->param_adv_100hdx = 1;
311744Sgs150176 			rgep->param_adv_10fdx = 0;
312744Sgs150176 			rgep->param_adv_10hdx = 0;
313744Sgs150176 			break;
314744Sgs150176 
315744Sgs150176 		case 10:
316744Sgs150176 			rgep->param_adv_autoneg = 1;
317744Sgs150176 			rgep->param_adv_1000fdx = 0;
318744Sgs150176 			rgep->param_adv_1000hdx = 0;
319744Sgs150176 			rgep->param_adv_100fdx = 0;
320744Sgs150176 			rgep->param_adv_100hdx = 0;
321744Sgs150176 			rgep->param_adv_10fdx = 1;
322744Sgs150176 			rgep->param_adv_10hdx = 1;
323744Sgs150176 			break;
324744Sgs150176 
325744Sgs150176 		default:
326744Sgs150176 			break;
327744Sgs150176 		}
328744Sgs150176 	}
329744Sgs150176 
330744Sgs150176 	/*
331744Sgs150176 	 * Also check the "speed" and "full-duplex" properties.  Setting
332744Sgs150176 	 * these properties will override all other settings and *disable*
333744Sgs150176 	 * autonegotiation, so both should be specified if either one is.
334744Sgs150176 	 * Otherwise, the unspecified parameter will be set to a default
335744Sgs150176 	 * value (1000Mb/s, full-duplex).
336744Sgs150176 	 */
337744Sgs150176 	if (RGE_PROP_EXISTS(dip, speed_propname) ||
338744Sgs150176 	    RGE_PROP_EXISTS(dip, duplex_propname)) {
339744Sgs150176 
340744Sgs150176 		rgep->param_adv_autoneg = 0;
341744Sgs150176 		rgep->param_adv_1000fdx = 1;
342744Sgs150176 		rgep->param_adv_1000hdx = 1;
343744Sgs150176 		rgep->param_adv_100fdx = 1;
344744Sgs150176 		rgep->param_adv_100hdx = 1;
345744Sgs150176 		rgep->param_adv_10fdx = 1;
346744Sgs150176 		rgep->param_adv_10hdx = 1;
347744Sgs150176 
348744Sgs150176 		speed = RGE_PROP_GET_INT(dip, speed_propname);
349744Sgs150176 		duplex = RGE_PROP_GET_INT(dip, duplex_propname);
350744Sgs150176 		rge_log(rgep, "%s property is %d",
3515735Smx205022 		    speed_propname, speed);
352744Sgs150176 		rge_log(rgep, "%s property is %d",
3535735Smx205022 		    duplex_propname, duplex);
354744Sgs150176 
355744Sgs150176 		switch (speed) {
356744Sgs150176 		case 1000:
357744Sgs150176 		default:
358744Sgs150176 			rgep->param_adv_100fdx = 0;
359744Sgs150176 			rgep->param_adv_100hdx = 0;
360744Sgs150176 			rgep->param_adv_10fdx = 0;
361744Sgs150176 			rgep->param_adv_10hdx = 0;
362744Sgs150176 			break;
363744Sgs150176 
364744Sgs150176 		case 100:
365744Sgs150176 			rgep->param_adv_1000fdx = 0;
366744Sgs150176 			rgep->param_adv_1000hdx = 0;
367744Sgs150176 			rgep->param_adv_10fdx = 0;
368744Sgs150176 			rgep->param_adv_10hdx = 0;
369744Sgs150176 			break;
370744Sgs150176 
371744Sgs150176 		case 10:
372744Sgs150176 			rgep->param_adv_1000fdx = 0;
373744Sgs150176 			rgep->param_adv_1000hdx = 0;
374744Sgs150176 			rgep->param_adv_100fdx = 0;
375744Sgs150176 			rgep->param_adv_100hdx = 0;
376744Sgs150176 			break;
377744Sgs150176 		}
378744Sgs150176 
379744Sgs150176 		switch (duplex) {
380744Sgs150176 		default:
381744Sgs150176 		case 1:
382744Sgs150176 			rgep->param_adv_1000hdx = 0;
383744Sgs150176 			rgep->param_adv_100hdx = 0;
384744Sgs150176 			rgep->param_adv_10hdx = 0;
385744Sgs150176 			break;
386744Sgs150176 
387744Sgs150176 		case 0:
388744Sgs150176 			rgep->param_adv_1000fdx = 0;
389744Sgs150176 			rgep->param_adv_100fdx = 0;
390744Sgs150176 			rgep->param_adv_10fdx = 0;
391744Sgs150176 			break;
392744Sgs150176 		}
393744Sgs150176 	}
394744Sgs150176 
395744Sgs150176 	RGE_DEBUG(("rge_nd_init: autoneg %d"
3965735Smx205022 	    "pause %d asym_pause %d "
3975735Smx205022 	    "1000fdx %d 1000hdx %d "
3985735Smx205022 	    "100fdx %d 100hdx %d "
3995735Smx205022 	    "10fdx %d 10hdx %d ",
4005735Smx205022 	    rgep->param_adv_autoneg,
4015735Smx205022 	    rgep->param_adv_pause, rgep->param_adv_asym_pause,
4025735Smx205022 	    rgep->param_adv_1000fdx, rgep->param_adv_1000hdx,
4035735Smx205022 	    rgep->param_adv_100fdx, rgep->param_adv_100hdx,
4045735Smx205022 	    rgep->param_adv_10fdx, rgep->param_adv_10hdx));
405744Sgs150176 
406744Sgs150176 	return (0);
407744Sgs150176 }
408744Sgs150176 
409744Sgs150176 enum ioc_reply
410744Sgs150176 rge_nd_ioctl(rge_t *rgep, queue_t *wq, mblk_t *mp, struct iocblk *iocp)
411744Sgs150176 {
412744Sgs150176 	nd_param_t *ndp;
413744Sgs150176 	boolean_t ok;
414744Sgs150176 	int info;
415744Sgs150176 	int cmd;
416744Sgs150176 
417744Sgs150176 	RGE_TRACE(("rge_nd_ioctl($%p, $%p, $%p, $%p)",
4185735Smx205022 	    (void *)rgep, (void *)wq, (void *)mp, (void *)iocp));
419744Sgs150176 
420744Sgs150176 	ASSERT(mutex_owned(rgep->genlock));
421744Sgs150176 
422744Sgs150176 	cmd = iocp->ioc_cmd;
423744Sgs150176 	switch (cmd) {
424744Sgs150176 	default:
425744Sgs150176 		/* NOTREACHED */
426744Sgs150176 		rge_error(rgep, "rge_nd_ioctl: invalid cmd 0x%x", cmd);
427744Sgs150176 		return (IOC_INVAL);
428744Sgs150176 
429744Sgs150176 	case ND_GET:
430744Sgs150176 		/*
431744Sgs150176 		 * If nd_getset() returns B_FALSE, the command was
432744Sgs150176 		 * not valid (e.g. unknown name), so we just tell the
433744Sgs150176 		 * top-level ioctl code to send a NAK (with code EINVAL).
434744Sgs150176 		 *
435744Sgs150176 		 * Otherwise, nd_getset() will have built the reply to
436744Sgs150176 		 * be sent (but not actually sent it), so we tell the
437744Sgs150176 		 * caller to send the prepared reply.
438744Sgs150176 		 */
439744Sgs150176 		ok = nd_getset(wq, rgep->nd_data_p, mp);
440744Sgs150176 		RGE_DEBUG(("rge_nd_ioctl: get %s", ok ? "OK" : "FAIL"));
441744Sgs150176 		return (ok ? IOC_REPLY : IOC_INVAL);
442744Sgs150176 
443744Sgs150176 	case ND_SET:
444744Sgs150176 		/*
445744Sgs150176 		 * All adv_* parameters are locked (read-only) while
446744Sgs150176 		 * the device is in any sort of loopback mode ...
447744Sgs150176 		 */
448744Sgs150176 		if (rgep->param_loop_mode != RGE_LOOP_NONE) {
449744Sgs150176 			iocp->ioc_error = EBUSY;
450744Sgs150176 			return (IOC_INVAL);
451744Sgs150176 		}
452744Sgs150176 
453744Sgs150176 		/*
454744Sgs150176 		 * Before calling nd_getset(), we save the <info> field
455744Sgs150176 		 * of the 'autonegotiation' parameter so that we can tell
456744Sgs150176 		 * whether it was assigned (even if its value doesn't
457744Sgs150176 		 * actually change).
458744Sgs150176 		 */
459744Sgs150176 		ndp = &rgep->nd_params[PARAM_ADV_AUTONEG_CAP];
460744Sgs150176 		info = ndp->ndp_info;
461744Sgs150176 		ok = nd_getset(wq, rgep->nd_data_p, mp);
462744Sgs150176 
463744Sgs150176 		/*
464744Sgs150176 		 * If nd_getset() returns B_FALSE, the command was
465744Sgs150176 		 * not valid (e.g. unknown name), so we just tell
466744Sgs150176 		 * the top-level ioctl code to send a NAK (with code
467744Sgs150176 		 * EINVAL by default).
468744Sgs150176 		 *
469744Sgs150176 		 * Otherwise, nd_getset() will have built the reply to
470744Sgs150176 		 * be sent - but that doesn't imply success!  In some
471744Sgs150176 		 * cases, the reply it's built will have a non-zero
472744Sgs150176 		 * error code in it (e.g. EPERM if not superuser).
473744Sgs150176 		 * So, we also drop out in that case ...
474744Sgs150176 		 */
475744Sgs150176 		RGE_DEBUG(("rge_nd_ioctl: set %s err %d autoneg %d info %d/%d",
4765735Smx205022 		    ok ? "OK" : "FAIL", iocp->ioc_error,
4775735Smx205022 		    ndp->ndp_val, info, ndp->ndp_info));
478744Sgs150176 		if (!ok)
479744Sgs150176 			return (IOC_INVAL);
480744Sgs150176 		if (iocp->ioc_error)
481744Sgs150176 			return (IOC_REPLY);
482744Sgs150176 
483744Sgs150176 		return (IOC_RESTART_REPLY);
484744Sgs150176 	}
485744Sgs150176 }
486744Sgs150176 
487744Sgs150176 /* Free the Named Dispatch Table by calling nd_free */
488744Sgs150176 void
489744Sgs150176 rge_nd_cleanup(rge_t *rgep)
490744Sgs150176 {
491744Sgs150176 	nd_free(&rgep->nd_data_p);
492744Sgs150176 }
493