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