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