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 2010 Emulex. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * Source file containing the implementation of the driver entry points 29 * and related helper functions 30 */ 31 32 #include <oce_impl.h> 33 #include <oce_ioctl.h> 34 35 /* array of properties supported by this driver */ 36 char *oce_priv_props[] = { 37 "_tx_ring_size", 38 "_tx_bcopy_limit", 39 "_rx_ring_size", 40 "_rx_bcopy_limit", 41 NULL 42 }; 43 44 /* ---[ static function declarations ]----------------------------------- */ 45 static int oce_power10(int power); 46 static int oce_set_priv_prop(struct oce_dev *dev, const char *name, 47 uint_t size, const void *val); 48 49 static int oce_get_priv_prop(struct oce_dev *dev, const char *name, 50 uint_t size, void *val); 51 52 /* ---[ GLD entry points ]----------------------------------------------- */ 53 int 54 oce_m_start(void *arg) 55 { 56 struct oce_dev *dev = arg; 57 int ret; 58 59 mutex_enter(&dev->dev_lock); 60 61 if (dev->state & STATE_MAC_STARTED) { 62 mutex_exit(&dev->dev_lock); 63 return (0); 64 } 65 66 if (dev->suspended) { 67 mutex_exit(&dev->dev_lock); 68 return (EIO); 69 } 70 71 if (oce_fm_check_acc_handle(dev, dev->db_handle) != DDI_FM_OK) { 72 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 73 mutex_exit(&dev->dev_lock); 74 return (EIO); 75 } 76 77 if (oce_fm_check_acc_handle(dev, dev->csr_handle) != DDI_FM_OK) { 78 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 79 mutex_exit(&dev->dev_lock); 80 return (EIO); 81 } 82 83 if (oce_fm_check_acc_handle(dev, dev->cfg_handle) != DDI_FM_OK) { 84 ddi_fm_service_impact(dev->dip, DDI_SERVICE_DEGRADED); 85 mutex_exit(&dev->dev_lock); 86 return (EIO); 87 } 88 89 ret = oce_start(dev); 90 if (ret != DDI_SUCCESS) { 91 mutex_exit(&dev->dev_lock); 92 return (EIO); 93 } 94 95 dev->state |= STATE_MAC_STARTED; 96 mutex_exit(&dev->dev_lock); 97 98 99 return (DDI_SUCCESS); 100 } 101 102 int 103 oce_start(struct oce_dev *dev) 104 { 105 int qidx = 0; 106 int ret; 107 108 ret = oce_alloc_intr(dev); 109 if (ret != DDI_SUCCESS) 110 goto start_fail; 111 ret = oce_setup_handlers(dev); 112 if (ret != DDI_SUCCESS) { 113 oce_log(dev, CE_WARN, MOD_CONFIG, 114 "Interrupt handler setup failed with %d", ret); 115 (void) oce_teardown_intr(dev); 116 goto start_fail; 117 } 118 /* get link status */ 119 (void) oce_get_link_status(dev, &dev->link); 120 121 if (dev->link.mac_speed == PHY_LINK_SPEED_ZERO) { 122 oce_log(dev, CE_NOTE, MOD_CONFIG, 123 "LINK_DOWN: 0x%x", dev->link.mac_speed); 124 mac_link_update(dev->mac_handle, LINK_STATE_DOWN); 125 } else { 126 oce_log(dev, CE_NOTE, MOD_CONFIG, 127 "(f,s,d,pp)=(0x%x, 0x%x, 0x%x, 0x%x)", 128 dev->link.mac_fault, dev->link.mac_speed, 129 dev->link.mac_duplex, dev->link.physical_port); 130 mac_link_update(dev->mac_handle, LINK_STATE_UP); 131 } 132 133 (void) oce_start_wq(dev->wq[0]); 134 (void) oce_start_rq(dev->rq[0]); 135 (void) oce_start_mq(dev->mq); 136 /* enable interrupts */ 137 oce_ei(dev); 138 /* arm the eqs */ 139 for (qidx = 0; qidx < dev->neqs; qidx++) { 140 oce_arm_eq(dev, dev->eq[qidx]->eq_id, 0, B_TRUE, B_FALSE); 141 } 142 143 /* update state */ 144 return (DDI_SUCCESS); 145 start_fail: 146 return (DDI_FAILURE); 147 } /* oce_start */ 148 149 150 void 151 oce_m_stop(void *arg) 152 { 153 struct oce_dev *dev = arg; 154 155 /* disable interrupts */ 156 157 mutex_enter(&dev->dev_lock); 158 if (dev->suspended) { 159 mutex_exit(&dev->dev_lock); 160 return; 161 } 162 dev->state |= STATE_MAC_STOPPING; 163 oce_stop(dev); 164 dev->state &= ~(STATE_MAC_STOPPING | STATE_MAC_STARTED); 165 mutex_exit(&dev->dev_lock); 166 } 167 /* called with Tx/Rx comp locks held */ 168 void 169 oce_stop(struct oce_dev *dev) 170 { 171 /* disable interrupts */ 172 oce_di(dev); 173 oce_remove_handler(dev); 174 (void) oce_teardown_intr(dev); 175 mutex_enter(&dev->wq[0]->tx_lock); 176 mutex_enter(&dev->rq[0]->rx_lock); 177 mutex_enter(&dev->mq->lock); 178 /* complete the pending Tx */ 179 oce_clean_wq(dev->wq[0]); 180 /* Release all the locks */ 181 mutex_exit(&dev->mq->lock); 182 mutex_exit(&dev->rq[0]->rx_lock); 183 mutex_exit(&dev->wq[0]->tx_lock); 184 185 } /* oce_stop */ 186 187 int 188 oce_m_multicast(void *arg, boolean_t add, const uint8_t *mca) 189 { 190 191 struct oce_dev *dev = (struct oce_dev *)arg; 192 struct ether_addr *mca_drv_list; 193 struct ether_addr mca_hw_list[OCE_MAX_MCA]; 194 uint16_t new_mcnt = 0; 195 int ret; 196 int i; 197 198 /* check the address */ 199 if ((mca[0] & 0x1) == 0) { 200 return (EINVAL); 201 } 202 /* Allocate the local array for holding the addresses temporarily */ 203 bzero(&mca_hw_list, sizeof (&mca_hw_list)); 204 mca_drv_list = &dev->multi_cast[0]; 205 206 DEV_LOCK(dev); 207 if (add) { 208 /* check if we exceeded hw max supported */ 209 if (dev->num_mca <= OCE_MAX_MCA) { 210 /* copy entire dev mca to the mbx */ 211 bcopy((void*)mca_drv_list, 212 (void*)mca_hw_list, 213 (dev->num_mca * sizeof (struct ether_addr))); 214 /* Append the new one to local list */ 215 bcopy(mca, &mca_hw_list[dev->num_mca], 216 sizeof (struct ether_addr)); 217 } 218 new_mcnt = dev->num_mca + 1; 219 } else { 220 struct ether_addr *hwlistp = &mca_hw_list[0]; 221 for (i = 0; i < dev->num_mca; i++) { 222 /* copy only if it does not match */ 223 if (bcmp((mca_drv_list + i), mca, ETHERADDRL)) { 224 bcopy(mca_drv_list + i, hwlistp, 225 ETHERADDRL); 226 hwlistp++; 227 } 228 } 229 new_mcnt = dev->num_mca - 1; 230 } 231 232 if (dev->suspended) { 233 goto finish; 234 } 235 if (new_mcnt == 0 || new_mcnt > OCE_MAX_MCA) { 236 ret = oce_set_multicast_table(dev, dev->if_id, NULL, 0, B_TRUE); 237 } else { 238 ret = oce_set_multicast_table(dev, dev->if_id, 239 &mca_hw_list[0], new_mcnt, B_FALSE); 240 } 241 if (ret != 0) { 242 DEV_UNLOCK(dev); 243 return (EIO); 244 } 245 /* 246 * Copy the local structure to dev structure 247 */ 248 finish: 249 if (new_mcnt && new_mcnt <= OCE_MAX_MCA) { 250 bcopy(mca_hw_list, mca_drv_list, 251 new_mcnt * sizeof (struct ether_addr)); 252 } 253 dev->num_mca = (uint16_t)new_mcnt; 254 DEV_UNLOCK(dev); 255 return (0); 256 } /* oce_m_multicast */ 257 258 int 259 oce_m_unicast(void *arg, const uint8_t *uca) 260 { 261 struct oce_dev *dev = arg; 262 int ret; 263 264 DEV_LOCK(dev); 265 if (dev->suspended) { 266 bcopy(uca, dev->unicast_addr, ETHERADDRL); 267 DEV_UNLOCK(dev); 268 return (DDI_SUCCESS); 269 } 270 271 /* Delete previous one and add new one */ 272 ret = oce_del_mac(dev, dev->if_id, &dev->pmac_id); 273 if (ret != DDI_SUCCESS) { 274 DEV_UNLOCK(dev); 275 return (EIO); 276 } 277 278 /* Set the New MAC addr earlier is no longer valid */ 279 ret = oce_add_mac(dev, dev->if_id, uca, &dev->pmac_id); 280 if (ret != DDI_SUCCESS) { 281 DEV_UNLOCK(dev); 282 return (EIO); 283 } 284 DEV_UNLOCK(dev); 285 return (ret); 286 } /* oce_m_unicast */ 287 288 mblk_t * 289 oce_m_send(void *arg, mblk_t *mp) 290 { 291 struct oce_dev *dev = arg; 292 mblk_t *nxt_pkt; 293 mblk_t *rmp = NULL; 294 struct oce_wq *wq; 295 296 DEV_LOCK(dev); 297 if (dev->suspended || !(dev->state & STATE_MAC_STARTED)) { 298 DEV_UNLOCK(dev); 299 freemsg(mp); 300 return (NULL); 301 } 302 DEV_UNLOCK(dev); 303 wq = dev->wq[0]; 304 305 while (mp != NULL) { 306 /* Save the Pointer since mp will be freed in case of copy */ 307 nxt_pkt = mp->b_next; 308 mp->b_next = NULL; 309 /* Hardcode wq since we have only one */ 310 rmp = oce_send_packet(wq, mp); 311 if (rmp != NULL) { 312 /* reschedule Tx */ 313 wq->resched = B_TRUE; 314 oce_arm_cq(dev, wq->cq->cq_id, 0, B_TRUE); 315 /* restore the chain */ 316 rmp->b_next = nxt_pkt; 317 break; 318 } 319 mp = nxt_pkt; 320 } 321 return (rmp); 322 } /* oce_send */ 323 324 boolean_t 325 oce_m_getcap(void *arg, mac_capab_t cap, void *data) 326 { 327 struct oce_dev *dev = arg; 328 boolean_t ret = B_TRUE; 329 switch (cap) { 330 331 case MAC_CAPAB_HCKSUM: { 332 uint32_t *csum_flags = u32ptr(data); 333 *csum_flags = HCKSUM_ENABLE | 334 HCKSUM_INET_FULL_V4 | 335 HCKSUM_IPHDRCKSUM; 336 break; 337 } 338 case MAC_CAPAB_LSO: { 339 mac_capab_lso_t *mcap_lso = (mac_capab_lso_t *)data; 340 if (dev->lso_capable) { 341 mcap_lso->lso_flags = LSO_TX_BASIC_TCP_IPV4; 342 mcap_lso->lso_basic_tcp_ipv4.lso_max = OCE_LSO_MAX_SIZE; 343 } else { 344 ret = B_FALSE; 345 } 346 break; 347 } 348 default: 349 ret = B_FALSE; 350 break; 351 } 352 return (ret); 353 } /* oce_m_getcap */ 354 355 int 356 oce_m_setprop(void *arg, const char *name, mac_prop_id_t id, 357 uint_t size, const void *val) 358 { 359 struct oce_dev *dev = arg; 360 int ret = 0; 361 362 DEV_LOCK(dev); 363 switch (id) { 364 case MAC_PROP_MTU: { 365 uint32_t mtu; 366 367 bcopy(val, &mtu, sizeof (uint32_t)); 368 369 if (dev->mtu == mtu) { 370 ret = 0; 371 break; 372 } 373 374 if (mtu != OCE_MIN_MTU && mtu != OCE_MAX_MTU) { 375 ret = EINVAL; 376 break; 377 } 378 379 ret = mac_maxsdu_update(dev->mac_handle, mtu); 380 if (0 == ret) { 381 dev->mtu = mtu; 382 break; 383 } 384 break; 385 } 386 387 case MAC_PROP_FLOWCTRL: { 388 link_flowctrl_t flowctrl; 389 uint32_t fc = 0; 390 391 bcopy(val, &flowctrl, sizeof (link_flowctrl_t)); 392 393 switch (flowctrl) { 394 case LINK_FLOWCTRL_NONE: 395 fc = 0; 396 break; 397 398 case LINK_FLOWCTRL_RX: 399 fc = OCE_FC_RX; 400 break; 401 402 case LINK_FLOWCTRL_TX: 403 fc = OCE_FC_TX; 404 break; 405 406 case LINK_FLOWCTRL_BI: 407 fc = OCE_FC_RX | OCE_FC_TX; 408 break; 409 default: 410 ret = EINVAL; 411 break; 412 } /* switch flowctrl */ 413 414 if (ret) 415 break; 416 417 if (fc == dev->flow_control) 418 break; 419 420 if (dev->suspended) { 421 dev->flow_control = fc; 422 break; 423 } 424 /* call to set flow control */ 425 ret = oce_set_flow_control(dev, fc); 426 /* store the new fc setting on success */ 427 if (ret == 0) { 428 dev->flow_control = fc; 429 } 430 break; 431 } 432 433 case MAC_PROP_PRIVATE: 434 ret = oce_set_priv_prop(dev, name, size, val); 435 break; 436 437 default: 438 ret = ENOTSUP; 439 break; 440 } /* switch id */ 441 442 DEV_UNLOCK(dev); 443 return (ret); 444 } /* oce_m_setprop */ 445 446 int 447 oce_m_getprop(void *arg, const char *name, mac_prop_id_t id, 448 uint_t size, void *val) 449 { 450 struct oce_dev *dev = arg; 451 uint32_t ret = 0; 452 453 switch (id) { 454 case MAC_PROP_ADV_10GFDX_CAP: 455 case MAC_PROP_EN_10GFDX_CAP: 456 *(uint8_t *)val = 0x01; 457 break; 458 459 case MAC_PROP_DUPLEX: { 460 uint32_t *mode = (uint32_t *)val; 461 462 ASSERT(size >= sizeof (link_duplex_t)); 463 if (dev->state & STATE_MAC_STARTED) 464 *mode = LINK_DUPLEX_FULL; 465 else 466 *mode = LINK_DUPLEX_UNKNOWN; 467 break; 468 } 469 470 case MAC_PROP_SPEED: { 471 uint64_t *speed = (uint64_t *)val; 472 473 ASSERT(size >= sizeof (uint64_t)); 474 *speed = 0; 475 if ((dev->state & STATE_MAC_STARTED) && 476 (dev->link.mac_speed != 0)) { 477 *speed = 1000000ull * oce_power10(dev->link.mac_speed); 478 } 479 break; 480 } 481 482 case MAC_PROP_FLOWCTRL: { 483 link_flowctrl_t *fc = (link_flowctrl_t *)val; 484 485 ASSERT(size >= sizeof (link_flowctrl_t)); 486 if (dev->flow_control & OCE_FC_TX && 487 dev->flow_control & OCE_FC_RX) 488 *fc = LINK_FLOWCTRL_BI; 489 else if (dev->flow_control == OCE_FC_TX) 490 *fc = LINK_FLOWCTRL_TX; 491 else if (dev->flow_control == OCE_FC_RX) 492 *fc = LINK_FLOWCTRL_RX; 493 else if (dev->flow_control == 0) 494 *fc = LINK_FLOWCTRL_NONE; 495 else 496 ret = EINVAL; 497 break; 498 } 499 500 case MAC_PROP_PRIVATE: 501 ret = oce_get_priv_prop(dev, name, size, val); 502 break; 503 504 default: 505 ret = ENOTSUP; 506 break; 507 } /* switch id */ 508 return (ret); 509 } /* oce_m_getprop */ 510 511 void 512 oce_m_propinfo(void *arg, const char *name, mac_prop_id_t pr_num, 513 mac_prop_info_handle_t prh) 514 { 515 _NOTE(ARGUNUSED(arg)); 516 517 switch (pr_num) { 518 case MAC_PROP_AUTONEG: 519 case MAC_PROP_EN_AUTONEG: 520 case MAC_PROP_ADV_1000FDX_CAP: 521 case MAC_PROP_EN_1000FDX_CAP: 522 case MAC_PROP_ADV_1000HDX_CAP: 523 case MAC_PROP_EN_1000HDX_CAP: 524 case MAC_PROP_ADV_100FDX_CAP: 525 case MAC_PROP_EN_100FDX_CAP: 526 case MAC_PROP_ADV_100HDX_CAP: 527 case MAC_PROP_EN_100HDX_CAP: 528 case MAC_PROP_ADV_10FDX_CAP: 529 case MAC_PROP_EN_10FDX_CAP: 530 case MAC_PROP_ADV_10HDX_CAP: 531 case MAC_PROP_EN_10HDX_CAP: 532 case MAC_PROP_ADV_100T4_CAP: 533 case MAC_PROP_EN_100T4_CAP: 534 case MAC_PROP_ADV_10GFDX_CAP: 535 case MAC_PROP_EN_10GFDX_CAP: 536 case MAC_PROP_SPEED: 537 case MAC_PROP_DUPLEX: 538 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 539 break; 540 541 case MAC_PROP_MTU: 542 mac_prop_info_set_range_uint32(prh, OCE_MIN_MTU, OCE_MAX_MTU); 543 break; 544 545 case MAC_PROP_PRIVATE: { 546 char valstr[64]; 547 int value; 548 549 if (strcmp(name, "_tx_ring_size") == 0) { 550 value = OCE_DEFAULT_TX_RING_SIZE; 551 } else if (strcmp(name, "_rx_ring_size") == 0) { 552 value = OCE_DEFAULT_RX_RING_SIZE; 553 } else { 554 return; 555 } 556 557 (void) snprintf(valstr, sizeof (valstr), "%d", value); 558 mac_prop_info_set_default_str(prh, valstr); 559 mac_prop_info_set_perm(prh, MAC_PROP_PERM_READ); 560 break; 561 } 562 } 563 } /* oce_m_propinfo */ 564 565 /* 566 * function to handle dlpi streams message from GLDv3 mac layer 567 */ 568 void 569 oce_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 570 { 571 struct oce_dev *dev = arg; 572 struct iocblk *iocp; 573 int cmd; 574 uint32_t payload_length; 575 int ret; 576 577 iocp = (struct iocblk *)voidptr(mp->b_rptr); 578 iocp->ioc_error = 0; 579 cmd = iocp->ioc_cmd; 580 581 DEV_LOCK(dev); 582 if (dev->suspended) { 583 miocnak(wq, mp, 0, EINVAL); 584 DEV_UNLOCK(dev); 585 return; 586 } 587 DEV_UNLOCK(dev); 588 589 switch (cmd) { 590 591 case OCE_ISSUE_MBOX: { 592 ret = oce_issue_mbox(dev, wq, mp, &payload_length); 593 if (ret != 0) { 594 miocnak(wq, mp, payload_length, ret); 595 } else { 596 miocack(wq, mp, payload_length, 0); 597 } 598 break; 599 } 600 601 default: 602 miocnak(wq, mp, 0, ENOTSUP); 603 break; 604 } 605 } /* oce_m_ioctl */ 606 607 int 608 oce_m_promiscuous(void *arg, boolean_t enable) 609 { 610 struct oce_dev *dev = arg; 611 int ret = 0; 612 613 DEV_LOCK(dev); 614 615 if (dev->promisc == enable) { 616 DEV_UNLOCK(dev); 617 return (ret); 618 } 619 620 if (dev->suspended) { 621 /* remember the setting */ 622 dev->promisc = enable; 623 DEV_UNLOCK(dev); 624 return (ret); 625 } 626 627 ret = oce_set_promiscuous(dev, enable); 628 if (ret == DDI_SUCCESS) 629 dev->promisc = enable; 630 DEV_UNLOCK(dev); 631 return (ret); 632 } /* oce_m_promiscuous */ 633 634 static int 635 oce_power10(int power) 636 { 637 int ret = 1; 638 639 while (power) { 640 ret *= 10; 641 power--; 642 } 643 return (ret); 644 } 645 646 /* 647 * function to set a private property. 648 * Called from the set_prop GLD entry point 649 * 650 * dev - sofware handle to the device 651 * name - string containing the property name 652 * size - length of the string in name 653 * val - pointer to a location where the value to set is stored 654 * 655 * return EINVAL => invalid value in val 0 => success 656 */ 657 static int 658 oce_set_priv_prop(struct oce_dev *dev, const char *name, 659 uint_t size, const void *val) 660 { 661 int ret = ENOTSUP; 662 long result; 663 664 _NOTE(ARGUNUSED(size)); 665 666 if (NULL == val) { 667 ret = EINVAL; 668 return (ret); 669 } 670 671 if (strcmp(name, "_tx_bcopy_limit") == 0) { 672 (void) ddi_strtol(val, (char **)NULL, 0, &result); 673 if (result <= OCE_WQ_BUF_SIZE) { 674 if (result != dev->tx_bcopy_limit) 675 dev->tx_bcopy_limit = (uint32_t)result; 676 ret = 0; 677 } else { 678 ret = EINVAL; 679 } 680 } 681 if (strcmp(name, "_rx_bcopy_limit") == 0) { 682 (void) ddi_strtol(val, (char **)NULL, 0, &result); 683 if (result <= OCE_RQ_BUF_SIZE) { 684 if (result != dev->rx_bcopy_limit) 685 dev->rx_bcopy_limit = (uint32_t)result; 686 ret = 0; 687 } else { 688 ret = EINVAL; 689 } 690 } 691 692 return (ret); 693 } /* oce_set_priv_prop */ 694 695 /* 696 * function to get the value of a private property. Called from get_prop 697 * 698 * dev - software handle to the device 699 * name - string containing the property name 700 * size - length of the string contained name 701 * val - [OUT] pointer to the location where the result is returned 702 * 703 * return EINVAL => invalid request 0 => success 704 */ 705 static int 706 oce_get_priv_prop(struct oce_dev *dev, const char *name, 707 uint_t size, void *val) 708 { 709 int value; 710 711 if (strcmp(name, "_tx_ring_size") == 0) { 712 value = dev->tx_ring_size; 713 } else if (strcmp(name, "_tx_bcopy_limit") == 0) { 714 value = dev->tx_bcopy_limit; 715 } else if (strcmp(name, "_rx_ring_size") == 0) { 716 value = dev->rx_ring_size; 717 } else if (strcmp(name, "_rx_bcopy_limit") == 0) { 718 value = dev->rx_bcopy_limit; 719 } else { 720 return (ENOTSUP); 721 } 722 723 (void) snprintf(val, size, "%d", value); 724 return (0); 725 } /* oce_get_priv_prop */ 726