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, 1, "-100fdx_cap" }, 108 { PARAM_100HDX_CAP, 0, 1, 1, "-100hdx_cap" }, 109 { PARAM_10FDX_CAP, 0, 1, 1, "-10fdx_cap" }, 110 { PARAM_10HDX_CAP, 0, 1, 1, "-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, 1, "+adv_100fdx_cap" }, 120 { PARAM_ADV_100HDX_CAP, 0, 1, 1, "+adv_100hdx_cap" }, 121 { PARAM_ADV_10FDX_CAP, 0, 1, 1, "+adv_10fdx_cap" }, 122 { PARAM_ADV_10HDX_CAP, 0, 1, 1, "+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