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