xref: /illumos-gate/usr/src/uts/common/io/rge/rge_ndd.c (revision 9e1a9180)
1c7fd2ed0Sgs /*
2c7fd2ed0Sgs  * CDDL HEADER START
3c7fd2ed0Sgs  *
4c7fd2ed0Sgs  * The contents of this file are subject to the terms of the
5aa817493Sgs  * Common Development and Distribution License (the "License").
6aa817493Sgs  * You may not use this file except in compliance with the License.
7c7fd2ed0Sgs  *
8c7fd2ed0Sgs  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9c7fd2ed0Sgs  * or http://www.opensolaris.org/os/licensing.
10c7fd2ed0Sgs  * See the License for the specific language governing permissions
11c7fd2ed0Sgs  * and limitations under the License.
12c7fd2ed0Sgs  *
13c7fd2ed0Sgs  * When distributing Covered Code, include this CDDL HEADER in each
14c7fd2ed0Sgs  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15c7fd2ed0Sgs  * If applicable, add the following below this CDDL HEADER, with the
16c7fd2ed0Sgs  * fields enclosed by brackets "[]" replaced with your own identifying
17c7fd2ed0Sgs  * information: Portions Copyright [yyyy] [name of copyright owner]
18c7fd2ed0Sgs  *
19c7fd2ed0Sgs  * CDDL HEADER END
20c7fd2ed0Sgs  */
21c7fd2ed0Sgs /*
22*9e1a9180SLi-Zhen You  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23c7fd2ed0Sgs  * Use is subject to license terms.
24c7fd2ed0Sgs  */
25c7fd2ed0Sgs 
26c7fd2ed0Sgs #include "rge.h"
27c7fd2ed0Sgs 
28c7fd2ed0Sgs #define	RGE_DBG		RGE_DBG_NDD	/* debug flag for this code	*/
29c7fd2ed0Sgs 
30c7fd2ed0Sgs /*
31c7fd2ed0Sgs  * Property names
32c7fd2ed0Sgs  */
33c7fd2ed0Sgs static char transfer_speed_propname[] = "transfer-speed";
34c7fd2ed0Sgs static char speed_propname[] = "speed";
35c7fd2ed0Sgs static char duplex_propname[] = "full-duplex";
36c7fd2ed0Sgs 
37c7fd2ed0Sgs /*
38c7fd2ed0Sgs  * Notes:
39c7fd2ed0Sgs  *	The first character of the <name> field encodes the read/write
40c7fd2ed0Sgs  *	status of the parameter:
41c7fd2ed0Sgs  *		'-' => read-only,
42c7fd2ed0Sgs  *		'+' => read/write,
43c7fd2ed0Sgs  *		'!' => invisible!
44c7fd2ed0Sgs  *
45c7fd2ed0Sgs  *	For writable parameters, we check for a driver property with the
46c7fd2ed0Sgs  *	same name; if found, and its value is in range, we initialise
47c7fd2ed0Sgs  *	the parameter from the property, overriding the default in the
48c7fd2ed0Sgs  *	table below.
49c7fd2ed0Sgs  *
50c7fd2ed0Sgs  *	A NULL in the <name> field terminates the array.
51c7fd2ed0Sgs  *
52c7fd2ed0Sgs  *	The <info> field is used here to provide the index of the
53c7fd2ed0Sgs  *	parameter to be initialised; thus it doesn't matter whether
54c7fd2ed0Sgs  *	this table is kept ordered or not.
55c7fd2ed0Sgs  *
56c7fd2ed0Sgs  *	The <info> field in the per-instance copy, on the other hand,
57c7fd2ed0Sgs  *	is used to count assignments so that we can tell when a magic
58c7fd2ed0Sgs  *	parameter has been set via ndd (see rge_param_set()).
59c7fd2ed0Sgs  */
60dfc2d53eSmx static const nd_param_t nd_template_1000[] = {
61c7fd2ed0Sgs /*	info		min	max	init	r/w+name		*/
62c7fd2ed0Sgs 
63c7fd2ed0Sgs /* Our hardware capabilities */
64c7fd2ed0Sgs { PARAM_AUTONEG_CAP,	    0,	  1,	1,	"-autoneg_cap"		},
65c7fd2ed0Sgs { PARAM_PAUSE_CAP,	    0,	  1,	1,	"-pause_cap"		},
66c7fd2ed0Sgs { PARAM_ASYM_PAUSE_CAP,	    0,	  1,	1,	"-asym_pause_cap"	},
67c7fd2ed0Sgs { PARAM_1000FDX_CAP,	    0,	  1,	1,	"-1000fdx_cap"		},
68c7fd2ed0Sgs { PARAM_1000HDX_CAP,	    0,	  1,	0,	"-1000hdx_cap"		},
69c7fd2ed0Sgs { PARAM_100T4_CAP,	    0,	  1,	0,	"-100T4_cap"		},
70c7fd2ed0Sgs { PARAM_100FDX_CAP,	    0,	  1,	1,	"-100fdx_cap"		},
71c7fd2ed0Sgs { PARAM_100HDX_CAP,	    0,	  1,	1,	"-100hdx_cap"		},
72c7fd2ed0Sgs { PARAM_10FDX_CAP,	    0,	  1,	1,	"-10fdx_cap"		},
73c7fd2ed0Sgs { PARAM_10HDX_CAP,	    0,	  1,	1,	"-10hdx_cap"		},
74c7fd2ed0Sgs 
75c7fd2ed0Sgs /* Our advertised capabilities */
76c7fd2ed0Sgs { PARAM_ADV_AUTONEG_CAP,    0,	  1,	1,	"-adv_autoneg_cap"	},
77c7fd2ed0Sgs { PARAM_ADV_PAUSE_CAP,	    0,	  1,	1,	"+adv_pause_cap"	},
78c7fd2ed0Sgs { PARAM_ADV_ASYM_PAUSE_CAP, 0,	  1,	1,	"+adv_asym_pause_cap"	},
79c7fd2ed0Sgs { PARAM_ADV_1000FDX_CAP,    0,	  1,	1,	"+adv_1000fdx_cap"	},
80c7fd2ed0Sgs { PARAM_ADV_1000HDX_CAP,    0,	  1,	0,	"-adv_1000hdx_cap"	},
81c7fd2ed0Sgs { PARAM_ADV_100T4_CAP,	    0,	  1,	0,	"-adv_100T4_cap"	},
82c7fd2ed0Sgs { PARAM_ADV_100FDX_CAP,	    0,	  1,	1,	"+adv_100fdx_cap"	},
83c7fd2ed0Sgs { PARAM_ADV_100HDX_CAP,	    0,	  1,	1,	"+adv_100hdx_cap"	},
84c7fd2ed0Sgs { PARAM_ADV_10FDX_CAP,	    0,	  1,	1,	"+adv_10fdx_cap"	},
85c7fd2ed0Sgs { PARAM_ADV_10HDX_CAP,	    0,	  1,	1,	"+adv_10hdx_cap"	},
86c7fd2ed0Sgs 
87c7fd2ed0Sgs /* Current operating modes */
88c7fd2ed0Sgs { PARAM_LINK_STATUS,	    0,	  1,	0,	"-link_status"		},
89c7fd2ed0Sgs { PARAM_LINK_SPEED,	    0,    1000,	0,	"-link_speed"		},
90c7fd2ed0Sgs { PARAM_LINK_DUPLEX,	    0,	  2,	0,	"-link_duplex"		},
91c7fd2ed0Sgs 
92c7fd2ed0Sgs /* Loopback status */
93c7fd2ed0Sgs { PARAM_LOOP_MODE,	    0,	  2,	0,	"-loop_mode"		},
94c7fd2ed0Sgs 
95c7fd2ed0Sgs /* Terminator */
96c7fd2ed0Sgs { PARAM_COUNT,		    0,	  0,	0,	NULL			}
97c7fd2ed0Sgs };
98c7fd2ed0Sgs 
99dfc2d53eSmx /* nd_template for RTL8101E */
100dfc2d53eSmx static const nd_param_t nd_template_100[] = {
101dfc2d53eSmx /*	info		min	max	init	r/w+name		*/
102dfc2d53eSmx 
103dfc2d53eSmx /* Our hardware capabilities */
104dfc2d53eSmx { PARAM_AUTONEG_CAP,	    0,	  1,	1,	"-autoneg_cap"		},
105dfc2d53eSmx { PARAM_PAUSE_CAP,	    0,	  1,	1,	"-pause_cap"		},
106dfc2d53eSmx { PARAM_ASYM_PAUSE_CAP,	    0,	  1,	1,	"-asym_pause_cap"	},
107dfc2d53eSmx { PARAM_1000FDX_CAP,	    0,	  1,	0,	"-1000fdx_cap"		},
108dfc2d53eSmx { PARAM_1000HDX_CAP,	    0,	  1,	0,	"-1000hdx_cap"		},
109dfc2d53eSmx { PARAM_100T4_CAP,	    0,	  1,	0,	"-100T4_cap"		},
110dfc2d53eSmx { PARAM_100FDX_CAP,	    0,	  1,	1,	"-100fdx_cap"		},
111dfc2d53eSmx { PARAM_100HDX_CAP,	    0,	  1,	1,	"-100hdx_cap"		},
112dfc2d53eSmx { PARAM_10FDX_CAP,	    0,	  1,	1,	"-10fdx_cap"		},
113dfc2d53eSmx { PARAM_10HDX_CAP,	    0,	  1,	1,	"-10hdx_cap"		},
114dfc2d53eSmx 
115dfc2d53eSmx /* Our advertised capabilities */
116dfc2d53eSmx { PARAM_ADV_AUTONEG_CAP,    0,	  1,	1,	"-adv_autoneg_cap"	},
117dfc2d53eSmx { PARAM_ADV_PAUSE_CAP,	    0,	  1,	1,	"+adv_pause_cap"	},
118dfc2d53eSmx { PARAM_ADV_ASYM_PAUSE_CAP, 0,	  1,	1,	"+adv_asym_pause_cap"	},
119dfc2d53eSmx { PARAM_ADV_1000FDX_CAP,    0,	  1,	0,	"-adv_1000fdx_cap"	},
120dfc2d53eSmx { PARAM_ADV_1000HDX_CAP,    0,	  1,	0,	"-adv_1000hdx_cap"	},
121dfc2d53eSmx { PARAM_ADV_100T4_CAP,	    0,	  1,	0,	"-adv_100T4_cap"	},
122dfc2d53eSmx { PARAM_ADV_100FDX_CAP,	    0,	  1,	1,	"+adv_100fdx_cap"	},
123dfc2d53eSmx { PARAM_ADV_100HDX_CAP,	    0,	  1,	1,	"+adv_100hdx_cap"	},
124dfc2d53eSmx { PARAM_ADV_10FDX_CAP,	    0,	  1,	1,	"+adv_10fdx_cap"	},
125dfc2d53eSmx { PARAM_ADV_10HDX_CAP,	    0,	  1,	1,	"+adv_10hdx_cap"	},
126dfc2d53eSmx 
127dfc2d53eSmx /* Current operating modes */
128dfc2d53eSmx { PARAM_LINK_STATUS,	    0,	  1,	0,	"-link_status"		},
129dfc2d53eSmx { PARAM_LINK_SPEED,	    0,    1000,	0,	"-link_speed"		},
130dfc2d53eSmx { PARAM_LINK_DUPLEX,	    0,	  2,	0,	"-link_duplex"		},
131dfc2d53eSmx 
132dfc2d53eSmx /* Loopback status */
133dfc2d53eSmx { PARAM_LOOP_MODE,	    0,	  2,	0,	"-loop_mode"		},
134dfc2d53eSmx 
135dfc2d53eSmx /* Terminator */
136dfc2d53eSmx { PARAM_COUNT,		    0,	  0,	0,	NULL			}
137dfc2d53eSmx };
138c7fd2ed0Sgs 
139c7fd2ed0Sgs /*  ============== NDD Support Functions ===============  */
140c7fd2ed0Sgs 
141c7fd2ed0Sgs /*
142c7fd2ed0Sgs  * Extracts the value from the rge parameter array and prints
143c7fd2ed0Sgs  * the parameter value. cp points to the required parameter.
144c7fd2ed0Sgs  */
145c7fd2ed0Sgs static int
rge_param_get(queue_t * q,mblk_t * mp,caddr_t cp,cred_t * credp)146c7fd2ed0Sgs rge_param_get(queue_t *q, mblk_t *mp, caddr_t cp, cred_t *credp)
147c7fd2ed0Sgs {
148c7fd2ed0Sgs 	nd_param_t *ndp;
149c7fd2ed0Sgs 
150c7fd2ed0Sgs 	_NOTE(ARGUNUSED(q, credp))
151c7fd2ed0Sgs 
152c7fd2ed0Sgs 	ndp = (nd_param_t *)cp;
153c7fd2ed0Sgs 	(void) mi_mpprintf(mp, "%d", ndp->ndp_val);
154c7fd2ed0Sgs 
155c7fd2ed0Sgs 	return (0);
156c7fd2ed0Sgs }
157c7fd2ed0Sgs 
158c7fd2ed0Sgs /*
159c7fd2ed0Sgs  * Validates the request to set a RGE parameter to a specific value.
160c7fd2ed0Sgs  * If the request is OK, the parameter is set.  Also the <info> field
161c7fd2ed0Sgs  * is incremented to show that the parameter was touched, even though
162c7fd2ed0Sgs  * it may have been set to the same value it already had.
163c7fd2ed0Sgs  */
164c7fd2ed0Sgs static int
rge_param_set(queue_t * q,mblk_t * mp,char * value,caddr_t cp,cred_t * credp)165c7fd2ed0Sgs rge_param_set(queue_t *q, mblk_t *mp, char *value, caddr_t cp, cred_t *credp)
166c7fd2ed0Sgs {
167c7fd2ed0Sgs 	nd_param_t *ndp;
168c7fd2ed0Sgs 	long new_value;
169c7fd2ed0Sgs 
170c7fd2ed0Sgs 	_NOTE(ARGUNUSED(q, mp, credp))
171c7fd2ed0Sgs 
172c7fd2ed0Sgs 	ndp = (nd_param_t *)cp;
173*9e1a9180SLi-Zhen You 	(void) ddi_strtol(value, (char **)NULL, 0, &new_value);
174c7fd2ed0Sgs 	if (new_value < ndp->ndp_min || new_value > ndp->ndp_max)
175c7fd2ed0Sgs 		return (EINVAL);
176c7fd2ed0Sgs 
17727dcfa4cSJing Sun 	ndp->ndp_val = (int)new_value;
178c7fd2ed0Sgs 	ndp->ndp_info += 1;
179c7fd2ed0Sgs 	return (0);
180c7fd2ed0Sgs }
181c7fd2ed0Sgs 
182c7fd2ed0Sgs /*
183c7fd2ed0Sgs  * Initialise the per-instance parameter array from the global prototype,
184c7fd2ed0Sgs  * and register each element with the named dispatch handler using nd_load()
185c7fd2ed0Sgs  */
186c7fd2ed0Sgs static int
rge_param_register(rge_t * rgep)187c7fd2ed0Sgs rge_param_register(rge_t *rgep)
188c7fd2ed0Sgs {
189c7fd2ed0Sgs 	const nd_param_t *tmplp;
190c7fd2ed0Sgs 	dev_info_t *dip;
191c7fd2ed0Sgs 	nd_param_t *ndp;
192c7fd2ed0Sgs 	caddr_t *nddpp;
193c7fd2ed0Sgs 	pfi_t setfn;
194c7fd2ed0Sgs 	char *nm;
195c7fd2ed0Sgs 	int pval;
196c7fd2ed0Sgs 
197c7fd2ed0Sgs 	dip = rgep->devinfo;
198c7fd2ed0Sgs 	nddpp = &rgep->nd_data_p;
199c7fd2ed0Sgs 	ASSERT(*nddpp == NULL);
200c7fd2ed0Sgs 
201dfc2d53eSmx 	if (rgep->chipid.mac_ver == MAC_VER_8101E)
202dfc2d53eSmx 		tmplp = nd_template_100;
203dfc2d53eSmx 	else
204dfc2d53eSmx 		tmplp = nd_template_1000;
205dfc2d53eSmx 
206dfc2d53eSmx 	for (; tmplp->ndp_name != NULL; ++tmplp) {
207c7fd2ed0Sgs 		/*
208c7fd2ed0Sgs 		 * Copy the template from nd_template[] into the
209c7fd2ed0Sgs 		 * proper slot in the per-instance parameters,
210c7fd2ed0Sgs 		 * then register the parameter with nd_load()
211c7fd2ed0Sgs 		 */
212c7fd2ed0Sgs 		ndp = &rgep->nd_params[tmplp->ndp_info];
213c7fd2ed0Sgs 		*ndp = *tmplp;
214c7fd2ed0Sgs 		nm = &ndp->ndp_name[0];
215c7fd2ed0Sgs 		setfn = rge_param_set;
216c7fd2ed0Sgs 
217c7fd2ed0Sgs 		switch (*nm) {
218c7fd2ed0Sgs 		default:
219c7fd2ed0Sgs 		case '!':
220c7fd2ed0Sgs 			continue;
221c7fd2ed0Sgs 
222c7fd2ed0Sgs 		case '+':
223c7fd2ed0Sgs 			break;
224c7fd2ed0Sgs 
225c7fd2ed0Sgs 		case '-':
226c7fd2ed0Sgs 			setfn = NULL;
227c7fd2ed0Sgs 			break;
228c7fd2ed0Sgs 		}
229c7fd2ed0Sgs 
230c7fd2ed0Sgs 		if (!nd_load(nddpp, ++nm, rge_param_get, setfn, (caddr_t)ndp))
231c7fd2ed0Sgs 			goto nd_fail;
232c7fd2ed0Sgs 
233c7fd2ed0Sgs 		/*
234c7fd2ed0Sgs 		 * If the parameter is writable, and there's a property
235c7fd2ed0Sgs 		 * with the same name, and its value is in range, we use
236c7fd2ed0Sgs 		 * it to initialise the parameter.  If it exists but is
237c7fd2ed0Sgs 		 * out of range, it's ignored.
238c7fd2ed0Sgs 		 */
239c7fd2ed0Sgs 		if (setfn && RGE_PROP_EXISTS(dip, nm)) {
240c7fd2ed0Sgs 			pval = RGE_PROP_GET_INT(dip, nm);
241c7fd2ed0Sgs 			if (pval >= ndp->ndp_min && pval <= ndp->ndp_max)
242c7fd2ed0Sgs 				ndp->ndp_val = pval;
243c7fd2ed0Sgs 		}
244c7fd2ed0Sgs 	}
245c7fd2ed0Sgs 
246c7fd2ed0Sgs 	RGE_DEBUG(("rge_param_register: OK"));
247c7fd2ed0Sgs 	return (DDI_SUCCESS);
248c7fd2ed0Sgs 
249c7fd2ed0Sgs nd_fail:
250dfc2d53eSmx 	if (rgep->chipid.mac_ver == MAC_VER_8101E) {
251dfc2d53eSmx 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
252dfc2d53eSmx 		    tmplp-nd_template_100, tmplp->ndp_info));
253dfc2d53eSmx 	} else {
254dfc2d53eSmx 		RGE_DEBUG(("rge_param_register: FAILED at index %d [info %d]",
255dfc2d53eSmx 		    tmplp-nd_template_1000, tmplp->ndp_info));
256dfc2d53eSmx 	}
257c7fd2ed0Sgs 	nd_free(nddpp);
258c7fd2ed0Sgs 	return (DDI_FAILURE);
259c7fd2ed0Sgs }
260c7fd2ed0Sgs 
261c7fd2ed0Sgs int
rge_nd_init(rge_t * rgep)262c7fd2ed0Sgs rge_nd_init(rge_t *rgep)
263c7fd2ed0Sgs {
264c7fd2ed0Sgs 	dev_info_t *dip;
265c7fd2ed0Sgs 	int duplex;
266c7fd2ed0Sgs 	int speed;
267c7fd2ed0Sgs 
268c7fd2ed0Sgs 	/*
269c7fd2ed0Sgs 	 * Register all the per-instance properties, initialising
270c7fd2ed0Sgs 	 * them from the table above or from driver properties set
271c7fd2ed0Sgs 	 * in the .conf file
272c7fd2ed0Sgs 	 */
273c7fd2ed0Sgs 	if (rge_param_register(rgep) != DDI_SUCCESS)
274c7fd2ed0Sgs 		return (-1);
275c7fd2ed0Sgs 
276c7fd2ed0Sgs 	/*
277c7fd2ed0Sgs 	 * The link speed may be forced to 10, 100 or 1000 Mbps using
278c7fd2ed0Sgs 	 * the property "transfer-speed". This may be done in OBP by
279c7fd2ed0Sgs 	 * using the command "apply transfer-speed=<speed> <device>".
280c7fd2ed0Sgs 	 * The speed may be 10, 100 or 1000 - any other value will be
281c7fd2ed0Sgs 	 * ignored.  Note that this does *enables* autonegotiation, but
282c7fd2ed0Sgs 	 * restricts it to the speed specified by the property.
283c7fd2ed0Sgs 	 */
284c7fd2ed0Sgs 	dip = rgep->devinfo;
285c7fd2ed0Sgs 	if (RGE_PROP_EXISTS(dip, transfer_speed_propname)) {
286c7fd2ed0Sgs 
287c7fd2ed0Sgs 		speed = RGE_PROP_GET_INT(dip, transfer_speed_propname);
288c7fd2ed0Sgs 		rge_log(rgep, "%s property is %d",
289dfc2d53eSmx 		    transfer_speed_propname, speed);
290c7fd2ed0Sgs 
291c7fd2ed0Sgs 		switch (speed) {
292c7fd2ed0Sgs 		case 1000:
293c7fd2ed0Sgs 			rgep->param_adv_autoneg = 1;
294c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 1;
295c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 1;
296c7fd2ed0Sgs 			rgep->param_adv_100fdx = 0;
297c7fd2ed0Sgs 			rgep->param_adv_100hdx = 0;
298c7fd2ed0Sgs 			rgep->param_adv_10fdx = 0;
299c7fd2ed0Sgs 			rgep->param_adv_10hdx = 0;
300c7fd2ed0Sgs 			break;
301c7fd2ed0Sgs 
302c7fd2ed0Sgs 		case 100:
303c7fd2ed0Sgs 			rgep->param_adv_autoneg = 1;
304c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 0;
305c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 0;
306c7fd2ed0Sgs 			rgep->param_adv_100fdx = 1;
307c7fd2ed0Sgs 			rgep->param_adv_100hdx = 1;
308c7fd2ed0Sgs 			rgep->param_adv_10fdx = 0;
309c7fd2ed0Sgs 			rgep->param_adv_10hdx = 0;
310c7fd2ed0Sgs 			break;
311c7fd2ed0Sgs 
312c7fd2ed0Sgs 		case 10:
313c7fd2ed0Sgs 			rgep->param_adv_autoneg = 1;
314c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 0;
315c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 0;
316c7fd2ed0Sgs 			rgep->param_adv_100fdx = 0;
317c7fd2ed0Sgs 			rgep->param_adv_100hdx = 0;
318c7fd2ed0Sgs 			rgep->param_adv_10fdx = 1;
319c7fd2ed0Sgs 			rgep->param_adv_10hdx = 1;
320c7fd2ed0Sgs 			break;
321c7fd2ed0Sgs 
322c7fd2ed0Sgs 		default:
323c7fd2ed0Sgs 			break;
324c7fd2ed0Sgs 		}
325c7fd2ed0Sgs 	}
326c7fd2ed0Sgs 
327c7fd2ed0Sgs 	/*
328c7fd2ed0Sgs 	 * Also check the "speed" and "full-duplex" properties.  Setting
329c7fd2ed0Sgs 	 * these properties will override all other settings and *disable*
330c7fd2ed0Sgs 	 * autonegotiation, so both should be specified if either one is.
331c7fd2ed0Sgs 	 * Otherwise, the unspecified parameter will be set to a default
332c7fd2ed0Sgs 	 * value (1000Mb/s, full-duplex).
333c7fd2ed0Sgs 	 */
334c7fd2ed0Sgs 	if (RGE_PROP_EXISTS(dip, speed_propname) ||
335c7fd2ed0Sgs 	    RGE_PROP_EXISTS(dip, duplex_propname)) {
336c7fd2ed0Sgs 
337c7fd2ed0Sgs 		rgep->param_adv_autoneg = 0;
338c7fd2ed0Sgs 		rgep->param_adv_1000fdx = 1;
339c7fd2ed0Sgs 		rgep->param_adv_1000hdx = 1;
340c7fd2ed0Sgs 		rgep->param_adv_100fdx = 1;
341c7fd2ed0Sgs 		rgep->param_adv_100hdx = 1;
342c7fd2ed0Sgs 		rgep->param_adv_10fdx = 1;
343c7fd2ed0Sgs 		rgep->param_adv_10hdx = 1;
344c7fd2ed0Sgs 
345c7fd2ed0Sgs 		speed = RGE_PROP_GET_INT(dip, speed_propname);
346c7fd2ed0Sgs 		duplex = RGE_PROP_GET_INT(dip, duplex_propname);
347c7fd2ed0Sgs 		rge_log(rgep, "%s property is %d",
348dfc2d53eSmx 		    speed_propname, speed);
349c7fd2ed0Sgs 		rge_log(rgep, "%s property is %d",
350dfc2d53eSmx 		    duplex_propname, duplex);
351c7fd2ed0Sgs 
352c7fd2ed0Sgs 		switch (speed) {
353c7fd2ed0Sgs 		case 1000:
354c7fd2ed0Sgs 		default:
355c7fd2ed0Sgs 			rgep->param_adv_100fdx = 0;
356c7fd2ed0Sgs 			rgep->param_adv_100hdx = 0;
357c7fd2ed0Sgs 			rgep->param_adv_10fdx = 0;
358c7fd2ed0Sgs 			rgep->param_adv_10hdx = 0;
359c7fd2ed0Sgs 			break;
360c7fd2ed0Sgs 
361c7fd2ed0Sgs 		case 100:
362c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 0;
363c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 0;
364c7fd2ed0Sgs 			rgep->param_adv_10fdx = 0;
365c7fd2ed0Sgs 			rgep->param_adv_10hdx = 0;
366c7fd2ed0Sgs 			break;
367c7fd2ed0Sgs 
368c7fd2ed0Sgs 		case 10:
369c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 0;
370c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 0;
371c7fd2ed0Sgs 			rgep->param_adv_100fdx = 0;
372c7fd2ed0Sgs 			rgep->param_adv_100hdx = 0;
373c7fd2ed0Sgs 			break;
374c7fd2ed0Sgs 		}
375c7fd2ed0Sgs 
376c7fd2ed0Sgs 		switch (duplex) {
377c7fd2ed0Sgs 		default:
378c7fd2ed0Sgs 		case 1:
379c7fd2ed0Sgs 			rgep->param_adv_1000hdx = 0;
380c7fd2ed0Sgs 			rgep->param_adv_100hdx = 0;
381c7fd2ed0Sgs 			rgep->param_adv_10hdx = 0;
382c7fd2ed0Sgs 			break;
383c7fd2ed0Sgs 
384c7fd2ed0Sgs 		case 0:
385c7fd2ed0Sgs 			rgep->param_adv_1000fdx = 0;
386c7fd2ed0Sgs 			rgep->param_adv_100fdx = 0;
387c7fd2ed0Sgs 			rgep->param_adv_10fdx = 0;
388c7fd2ed0Sgs 			break;
389c7fd2ed0Sgs 		}
390c7fd2ed0Sgs 	}
391c7fd2ed0Sgs 
392c7fd2ed0Sgs 	RGE_DEBUG(("rge_nd_init: autoneg %d"
393dfc2d53eSmx 	    "pause %d asym_pause %d "
394dfc2d53eSmx 	    "1000fdx %d 1000hdx %d "
395dfc2d53eSmx 	    "100fdx %d 100hdx %d "
396dfc2d53eSmx 	    "10fdx %d 10hdx %d ",
397dfc2d53eSmx 	    rgep->param_adv_autoneg,
398dfc2d53eSmx 	    rgep->param_adv_pause, rgep->param_adv_asym_pause,
399dfc2d53eSmx 	    rgep->param_adv_1000fdx, rgep->param_adv_1000hdx,
400dfc2d53eSmx 	    rgep->param_adv_100fdx, rgep->param_adv_100hdx,
401dfc2d53eSmx 	    rgep->param_adv_10fdx, rgep->param_adv_10hdx));
402c7fd2ed0Sgs 
403c7fd2ed0Sgs 	return (0);
404c7fd2ed0Sgs }
405c7fd2ed0Sgs 
406c7fd2ed0Sgs enum ioc_reply
rge_nd_ioctl(rge_t * rgep,queue_t * wq,mblk_t * mp,struct iocblk * iocp)407c7fd2ed0Sgs rge_nd_ioctl(rge_t *rgep, queue_t *wq, mblk_t *mp, struct iocblk *iocp)
408c7fd2ed0Sgs {
409c7fd2ed0Sgs 	nd_param_t *ndp;
410c7fd2ed0Sgs 	boolean_t ok;
411c7fd2ed0Sgs 	int info;
412c7fd2ed0Sgs 	int cmd;
413c7fd2ed0Sgs 
414c7fd2ed0Sgs 	RGE_TRACE(("rge_nd_ioctl($%p, $%p, $%p, $%p)",
415dfc2d53eSmx 	    (void *)rgep, (void *)wq, (void *)mp, (void *)iocp));
416c7fd2ed0Sgs 
417c7fd2ed0Sgs 	ASSERT(mutex_owned(rgep->genlock));
418c7fd2ed0Sgs 
419c7fd2ed0Sgs 	cmd = iocp->ioc_cmd;
420c7fd2ed0Sgs 	switch (cmd) {
421c7fd2ed0Sgs 	default:
422c7fd2ed0Sgs 		/* NOTREACHED */
423c7fd2ed0Sgs 		rge_error(rgep, "rge_nd_ioctl: invalid cmd 0x%x", cmd);
424c7fd2ed0Sgs 		return (IOC_INVAL);
425c7fd2ed0Sgs 
426c7fd2ed0Sgs 	case ND_GET:
427c7fd2ed0Sgs 		/*
428c7fd2ed0Sgs 		 * If nd_getset() returns B_FALSE, the command was
429c7fd2ed0Sgs 		 * not valid (e.g. unknown name), so we just tell the
430c7fd2ed0Sgs 		 * top-level ioctl code to send a NAK (with code EINVAL).
431c7fd2ed0Sgs 		 *
432c7fd2ed0Sgs 		 * Otherwise, nd_getset() will have built the reply to
433c7fd2ed0Sgs 		 * be sent (but not actually sent it), so we tell the
434c7fd2ed0Sgs 		 * caller to send the prepared reply.
435c7fd2ed0Sgs 		 */
436c7fd2ed0Sgs 		ok = nd_getset(wq, rgep->nd_data_p, mp);
437c7fd2ed0Sgs 		RGE_DEBUG(("rge_nd_ioctl: get %s", ok ? "OK" : "FAIL"));
438c7fd2ed0Sgs 		return (ok ? IOC_REPLY : IOC_INVAL);
439c7fd2ed0Sgs 
440c7fd2ed0Sgs 	case ND_SET:
441c7fd2ed0Sgs 		/*
442c7fd2ed0Sgs 		 * All adv_* parameters are locked (read-only) while
443c7fd2ed0Sgs 		 * the device is in any sort of loopback mode ...
444c7fd2ed0Sgs 		 */
445c7fd2ed0Sgs 		if (rgep->param_loop_mode != RGE_LOOP_NONE) {
446c7fd2ed0Sgs 			iocp->ioc_error = EBUSY;
447c7fd2ed0Sgs 			return (IOC_INVAL);
448c7fd2ed0Sgs 		}
449c7fd2ed0Sgs 
450c7fd2ed0Sgs 		/*
451c7fd2ed0Sgs 		 * Before calling nd_getset(), we save the <info> field
452c7fd2ed0Sgs 		 * of the 'autonegotiation' parameter so that we can tell
453c7fd2ed0Sgs 		 * whether it was assigned (even if its value doesn't
454c7fd2ed0Sgs 		 * actually change).
455c7fd2ed0Sgs 		 */
456c7fd2ed0Sgs 		ndp = &rgep->nd_params[PARAM_ADV_AUTONEG_CAP];
457c7fd2ed0Sgs 		info = ndp->ndp_info;
458c7fd2ed0Sgs 		ok = nd_getset(wq, rgep->nd_data_p, mp);
459c7fd2ed0Sgs 
460c7fd2ed0Sgs 		/*
461c7fd2ed0Sgs 		 * If nd_getset() returns B_FALSE, the command was
462c7fd2ed0Sgs 		 * not valid (e.g. unknown name), so we just tell
463c7fd2ed0Sgs 		 * the top-level ioctl code to send a NAK (with code
464c7fd2ed0Sgs 		 * EINVAL by default).
465c7fd2ed0Sgs 		 *
466c7fd2ed0Sgs 		 * Otherwise, nd_getset() will have built the reply to
467c7fd2ed0Sgs 		 * be sent - but that doesn't imply success!  In some
468c7fd2ed0Sgs 		 * cases, the reply it's built will have a non-zero
469c7fd2ed0Sgs 		 * error code in it (e.g. EPERM if not superuser).
470c7fd2ed0Sgs 		 * So, we also drop out in that case ...
471c7fd2ed0Sgs 		 */
472c7fd2ed0Sgs 		RGE_DEBUG(("rge_nd_ioctl: set %s err %d autoneg %d info %d/%d",
473dfc2d53eSmx 		    ok ? "OK" : "FAIL", iocp->ioc_error,
474dfc2d53eSmx 		    ndp->ndp_val, info, ndp->ndp_info));
475c7fd2ed0Sgs 		if (!ok)
476c7fd2ed0Sgs 			return (IOC_INVAL);
477c7fd2ed0Sgs 		if (iocp->ioc_error)
478c7fd2ed0Sgs 			return (IOC_REPLY);
479c7fd2ed0Sgs 
480c7fd2ed0Sgs 		return (IOC_RESTART_REPLY);
481c7fd2ed0Sgs 	}
482c7fd2ed0Sgs }
483c7fd2ed0Sgs 
484c7fd2ed0Sgs /* Free the Named Dispatch Table by calling nd_free */
485c7fd2ed0Sgs void
rge_nd_cleanup(rge_t * rgep)486c7fd2ed0Sgs rge_nd_cleanup(rge_t *rgep)
487c7fd2ed0Sgs {
488c7fd2ed0Sgs 	nd_free(&rgep->nd_data_p);
489c7fd2ed0Sgs }
490