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