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