/*- * Copyright (c) 2002-2007 Neterion, Inc. * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ /* * if_nxge.c * * FreeBSD specific initialization & routines */ #include #include #include #include #include #include int copyright_print = 0; int hal_driver_init_count = 0; size_t size = sizeof(int); /****************************************** * xge_probe * Parameters: Device structure * Return: BUS_PROBE_DEFAULT/ENXIO/ENOMEM * Description: Probes for Xframe device ******************************************/ int xge_probe(device_t dev) { int devid = pci_get_device(dev); int vendorid = pci_get_vendor(dev); int retValue = ENXIO; ENTER_FUNCTION if(vendorid == XGE_PCI_VENDOR_ID) { if((devid == XGE_PCI_DEVICE_ID_XENA_2) || (devid == XGE_PCI_DEVICE_ID_HERC_2)) { if(!copyright_print) { PRINT_COPYRIGHT; copyright_print = 1; } device_set_desc_copy(dev, "Neterion Xframe 10 Gigabit Ethernet Adapter"); retValue = BUS_PROBE_DEFAULT; } } LEAVE_FUNCTION return retValue; } /****************************************** * xge_init_params * Parameters: HAL device configuration * structure, device pointer * Return: None * Description: Sets parameter values in * xge_hal_device_config_t structure ******************************************/ void xge_init_params(xge_hal_device_config_t *dconfig, device_t dev) { int index, revision; device_t checkdev; ENTER_FUNCTION #define SAVE_PARAM(to, what, value) to.what = value; #define GET_PARAM(str_kenv, to, param, hardcode) { \ static int param##__LINE__; \ if(testenv(str_kenv) == 1) { \ getenv_int(str_kenv, ¶m##__LINE__); \ } \ else { \ param##__LINE__ = hardcode; \ } \ SAVE_PARAM(to, param, param##__LINE__); \ } #define GET_PARAM_MAC(str_kenv, param, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).mac), param, hardcode); #define GET_PARAM_FIFO(str_kenv, param, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).fifo), param, hardcode); #define GET_PARAM_FIFO_QUEUE(str_kenv, param, qindex, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).fifo.queue[qindex]), param, hardcode); #define GET_PARAM_FIFO_QUEUE_TTI(str_kenv, param, qindex, tindex, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).fifo.queue[qindex].tti[tindex]), \ param, hardcode); #define GET_PARAM_RING(str_kenv, param, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).ring), param, hardcode); #define GET_PARAM_RING_QUEUE(str_kenv, param, qindex, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).ring.queue[qindex]), param, hardcode); #define GET_PARAM_RING_QUEUE_RTI(str_kenv, param, qindex, hardcode) \ GET_PARAM(str_kenv, ((*dconfig).ring.queue[qindex].rti), param, \ hardcode); dconfig->mtu = XGE_DEFAULT_INITIAL_MTU; dconfig->pci_freq_mherz = XGE_DEFAULT_USER_HARDCODED; dconfig->device_poll_millis = XGE_HAL_DEFAULT_DEVICE_POLL_MILLIS; dconfig->link_stability_period = XGE_HAL_DEFAULT_LINK_STABILITY_PERIOD; dconfig->mac.rmac_bcast_en = XGE_DEFAULT_MAC_RMAC_BCAST_EN; dconfig->fifo.alignment_size = XGE_DEFAULT_FIFO_ALIGNMENT_SIZE; GET_PARAM("hw.xge.latency_timer", (*dconfig), latency_timer, XGE_DEFAULT_LATENCY_TIMER); GET_PARAM("hw.xge.max_splits_trans", (*dconfig), max_splits_trans, XGE_DEFAULT_MAX_SPLITS_TRANS); GET_PARAM("hw.xge.mmrb_count", (*dconfig), mmrb_count, XGE_DEFAULT_MMRB_COUNT); GET_PARAM("hw.xge.shared_splits", (*dconfig), shared_splits, XGE_DEFAULT_SHARED_SPLITS); GET_PARAM("hw.xge.isr_polling_cnt", (*dconfig), isr_polling_cnt, XGE_DEFAULT_ISR_POLLING_CNT); GET_PARAM("hw.xge.stats_refresh_time_sec", (*dconfig), stats_refresh_time_sec, XGE_DEFAULT_STATS_REFRESH_TIME_SEC); GET_PARAM_MAC("hw.xge.mac_tmac_util_period", tmac_util_period, XGE_DEFAULT_MAC_TMAC_UTIL_PERIOD); GET_PARAM_MAC("hw.xge.mac_rmac_util_period", rmac_util_period, XGE_DEFAULT_MAC_RMAC_UTIL_PERIOD); GET_PARAM_MAC("hw.xge.mac_rmac_pause_gen_en", rmac_pause_gen_en, XGE_DEFAULT_MAC_RMAC_PAUSE_GEN_EN); GET_PARAM_MAC("hw.xge.mac_rmac_pause_rcv_en", rmac_pause_rcv_en, XGE_DEFAULT_MAC_RMAC_PAUSE_RCV_EN); GET_PARAM_MAC("hw.xge.mac_rmac_pause_time", rmac_pause_time, XGE_DEFAULT_MAC_RMAC_PAUSE_TIME); GET_PARAM_MAC("hw.xge.mac_mc_pause_threshold_q0q3", mc_pause_threshold_q0q3, XGE_DEFAULT_MAC_MC_PAUSE_THRESHOLD_Q0Q3); GET_PARAM_MAC("hw.xge.mac_mc_pause_threshold_q4q7", mc_pause_threshold_q4q7, XGE_DEFAULT_MAC_MC_PAUSE_THRESHOLD_Q4Q7); GET_PARAM_FIFO("hw.xge.fifo_memblock_size", memblock_size, XGE_DEFAULT_FIFO_MEMBLOCK_SIZE); GET_PARAM_FIFO("hw.xge.fifo_reserve_threshold", reserve_threshold, XGE_DEFAULT_FIFO_RESERVE_THRESHOLD); GET_PARAM_FIFO("hw.xge.fifo_max_frags", max_frags, XGE_DEFAULT_FIFO_MAX_FRAGS); GET_PARAM_FIFO_QUEUE("hw.xge.fifo_queue_intr", intr, 0, XGE_DEFAULT_FIFO_QUEUE_INTR); GET_PARAM_FIFO_QUEUE("hw.xge.fifo_queue_max", max, 0, XGE_DEFAULT_FIFO_QUEUE_MAX); GET_PARAM_FIFO_QUEUE("hw.xge.fifo_queue_initial", initial, 0, XGE_DEFAULT_FIFO_QUEUE_INITIAL); for (index = 0; index < XGE_HAL_MAX_FIFO_TTI_NUM; index++) { dconfig->fifo.queue[0].tti[index].enabled = 1; dconfig->fifo.queue[0].configured = 1; GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_urange_a", urange_a, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_URANGE_A); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_urange_b", urange_b, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_URANGE_B); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_urange_c", urange_c, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_URANGE_C); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_ufc_a", ufc_a, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_UFC_A); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_ufc_b", ufc_b, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_UFC_B); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_ufc_c", ufc_c, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_UFC_C); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_ufc_d", ufc_d, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_UFC_D); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_timer_ci_en", timer_ci_en, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_TIMER_CI_EN); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_timer_ac_en", timer_ac_en, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_TIMER_AC_EN); GET_PARAM_FIFO_QUEUE_TTI("hw.xge.fifo_queue_tti_timer_val_us", timer_val_us, 0, index, XGE_DEFAULT_FIFO_QUEUE_TTI_TIMER_VAL_US); } GET_PARAM_RING("hw.xge.ring_memblock_size", memblock_size, XGE_DEFAULT_RING_MEMBLOCK_SIZE); GET_PARAM_RING("hw.xge.ring_strip_vlan_tag", strip_vlan_tag, XGE_DEFAULT_RING_STRIP_VLAN_TAG); for (index = 0; index < XGE_HAL_MIN_RING_NUM; index++) { dconfig->ring.queue[index].max_frm_len = XGE_HAL_RING_USE_MTU; dconfig->ring.queue[index].priority = 0; dconfig->ring.queue[index].configured = 1; dconfig->ring.queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_1; GET_PARAM_RING_QUEUE("hw.xge.ring_queue_max", max, index, XGE_DEFAULT_RING_QUEUE_MAX); GET_PARAM_RING_QUEUE("hw.xge.ring_queue_initial", initial, index, XGE_DEFAULT_RING_QUEUE_INITIAL); GET_PARAM_RING_QUEUE("hw.xge.ring_queue_dram_size_mb", dram_size_mb, index, XGE_DEFAULT_RING_QUEUE_DRAM_SIZE_MB); GET_PARAM_RING_QUEUE("hw.xge.ring_queue_indicate_max_pkts", indicate_max_pkts, index, XGE_DEFAULT_RING_QUEUE_INDICATE_MAX_PKTS); GET_PARAM_RING_QUEUE("hw.xge.ring_queue_backoff_interval_us", backoff_interval_us, index, XGE_DEFAULT_RING_QUEUE_BACKOFF_INTERVAL_US); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_ufc_a", ufc_a, index, XGE_DEFAULT_RING_QUEUE_RTI_UFC_A); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_ufc_b", ufc_b, index, XGE_DEFAULT_RING_QUEUE_RTI_UFC_B); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_ufc_c", ufc_c, index, XGE_DEFAULT_RING_QUEUE_RTI_UFC_C); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_ufc_d", ufc_d, index, XGE_DEFAULT_RING_QUEUE_RTI_UFC_D); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_timer_ac_en", timer_ac_en, index, XGE_DEFAULT_RING_QUEUE_RTI_TIMER_AC_EN); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_timer_val_us", timer_val_us, index, XGE_DEFAULT_RING_QUEUE_RTI_TIMER_VAL_US); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_urange_a", urange_a, index, XGE_DEFAULT_RING_QUEUE_RTI_URANGE_A); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_urange_b", urange_b, index, XGE_DEFAULT_RING_QUEUE_RTI_URANGE_B); GET_PARAM_RING_QUEUE_RTI("hw.xge.ring_queue_rti_urange_c", urange_c, index, XGE_DEFAULT_RING_QUEUE_RTI_URANGE_C); } if(dconfig->fifo.max_frags > (PAGE_SIZE/32)) { xge_os_printf("fifo_max_frags = %d", dconfig->fifo.max_frags); xge_os_printf("fifo_max_frags should be <= (PAGE_SIZE / 32) = %d", (int)(PAGE_SIZE / 32)); xge_os_printf("Using fifo_max_frags = %d", (int)(PAGE_SIZE / 32)); dconfig->fifo.max_frags = (PAGE_SIZE / 32); } checkdev = pci_find_device(VENDOR_ID_AMD, DEVICE_ID_8131_PCI_BRIDGE); if(checkdev != NULL) { /* Check Revision for 0x12 */ revision = pci_read_config(checkdev, xge_offsetof(xge_hal_pci_config_t, revision), 1); if(revision <= 0x12) { /* Set mmrb_count to 1k and max splits = 2 */ dconfig->mmrb_count = 1; dconfig->max_splits_trans = XGE_HAL_THREE_SPLIT_TRANSACTION; } } #ifdef XGE_FEATURE_LRO /* updating the LRO frame's sg size and frame len size. */ dconfig->lro_sg_size = 20; dconfig->lro_frm_len = 65536; #endif LEAVE_FUNCTION } /****************************************** * xge_driver_initialize * Parameters: None * Return: 0/1 * Description: Defines HAL-ULD callbacks * and initializes the HAL driver ******************************************/ int xge_driver_initialize(void) { xge_hal_uld_cbs_t uld_callbacks; xge_hal_driver_config_t driver_config; xge_hal_status_e status = XGE_HAL_OK; ENTER_FUNCTION /* Initialize HAL driver */ if(!hal_driver_init_count) { xge_os_memzero(&uld_callbacks, sizeof(xge_hal_uld_cbs_t)); /* * Initial and maximum size of the queue used to store the events * like Link up/down (xge_hal_event_e) */ driver_config.queue_size_initial = 1; driver_config.queue_size_max = 4; uld_callbacks.link_up = xgell_callback_link_up; uld_callbacks.link_down = xgell_callback_link_down; uld_callbacks.crit_err = xgell_callback_crit_err; uld_callbacks.event = xgell_callback_event; status = xge_hal_driver_initialize(&driver_config, &uld_callbacks); if(status != XGE_HAL_OK) { xge_os_printf("xgeX: Initialization failed (Status: %d)", status); goto xdi_out; } } hal_driver_init_count = hal_driver_init_count + 1; xge_hal_driver_debug_module_mask_set(0xffffffff); xge_hal_driver_debug_level_set(XGE_TRACE); xdi_out: LEAVE_FUNCTION return status; } /****************************************** * Function: xge_media_init * Parameters: Device pointer * Return: None * Description: Initializes, adds and sets * media ******************************************/ void xge_media_init(device_t devc) { xgelldev_t *lldev = (xgelldev_t *)device_get_softc(devc); ENTER_FUNCTION /* Initialize Media */ ifmedia_init(&lldev->xge_media, IFM_IMASK, xge_ifmedia_change, xge_ifmedia_status); /* Add supported media */ ifmedia_add(&lldev->xge_media, IFM_ETHER | IFM_1000_SX | IFM_FDX, 0, NULL); ifmedia_add(&lldev->xge_media, IFM_ETHER | IFM_1000_SX, 0, NULL); ifmedia_add(&lldev->xge_media, IFM_ETHER | IFM_AUTO, 0, NULL); ifmedia_add(&lldev->xge_media, IFM_ETHER | IFM_10G_SR, 0, NULL); ifmedia_add(&lldev->xge_media, IFM_ETHER | IFM_10G_LR, 0, NULL); /* Set media */ ifmedia_set(&lldev->xge_media, IFM_ETHER | IFM_AUTO); LEAVE_FUNCTION } /* * xge_pci_space_save * Save PCI configuration space * @dev Device structure */ void xge_pci_space_save(device_t dev) { ENTER_FUNCTION struct pci_devinfo *dinfo = NULL; dinfo = device_get_ivars(dev); xge_trace(XGE_TRACE, "Saving PCI configuration space"); pci_cfg_save(dev, dinfo, 0); LEAVE_FUNCTION } /* * xge_pci_space_restore * Restore saved PCI configuration space * @dev Device structure */ void xge_pci_space_restore(device_t dev) { ENTER_FUNCTION struct pci_devinfo *dinfo = NULL; dinfo = device_get_ivars(dev); xge_trace(XGE_TRACE, "Restoring PCI configuration space"); pci_cfg_restore(dev, dinfo); LEAVE_FUNCTION } /****************************************** * xge_attach * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Connects the driver to the * system if the probe routine returned success ******************************************/ int xge_attach(device_t dev) { xge_hal_device_config_t *device_config; xge_hal_ring_config_t *pRingConfig; xge_hal_device_attr_t attr; xgelldev_t *lldev; xge_hal_device_t *hldev; pci_info_t *pci_info; struct ifnet *ifnetp; char *mesg; char *desc; int rid; int rid0; int rid1; int error; u64 val64 = 0; int retValue = 0; int mode = 0; int buffer_index, buffer_length, index; ENTER_FUNCTION device_config = xge_malloc(sizeof(xge_hal_device_config_t)); if(!device_config) { xge_ctrace(XGE_ERR, "Malloc of device config failed"); retValue = ENOMEM; goto attach_out_config; } lldev = (xgelldev_t *) device_get_softc(dev); if(!lldev) { xge_ctrace(XGE_ERR, "Adapter softc structure allocation failed"); retValue = ENOMEM; goto attach_out; } lldev->device = dev; /* Initialize mutex */ if(mtx_initialized(&lldev->xge_lock) == 0) { mtx_init((&lldev->xge_lock), "xge", MTX_NETWORK_LOCK, MTX_DEF); } error = xge_driver_initialize(); if(error != XGE_HAL_OK) { xge_ctrace(XGE_ERR, "Initializing driver failed"); freeResources(dev, 1); retValue = ENXIO; goto attach_out; } /* HAL device */ hldev = (xge_hal_device_t *)xge_malloc(sizeof(xge_hal_device_t)); if(!hldev) { xge_trace(XGE_ERR, "Allocating memory for xge_hal_device_t failed"); freeResources(dev, 2); retValue = ENOMEM; goto attach_out; } lldev->devh = hldev; /* Our private structure */ pci_info = (pci_info_t*) xge_malloc(sizeof(pci_info_t)); if(!pci_info) { xge_trace(XGE_ERR, "Allocating memory for pci_info_t failed"); freeResources(dev, 3); retValue = ENOMEM; goto attach_out; } lldev->pdev = pci_info; pci_info->device = dev; /* Set bus master */ pci_enable_busmaster(dev); /* Get virtual address for BAR0 */ rid0 = PCIR_BAR(0); pci_info->regmap0 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid0, RF_ACTIVE); if(pci_info->regmap0 == NULL) { xge_trace(XGE_ERR, "NULL handler for BAR0"); freeResources(dev, 4); retValue = ENOMEM; goto attach_out; } attr.bar0 = (char *)pci_info->regmap0; pci_info->bar0resource = (busresource_t*) xge_malloc(sizeof(busresource_t)); if(pci_info->bar0resource == NULL) { xge_trace(XGE_ERR, "Allocating memory for bar0resources failed"); freeResources(dev, 5); retValue = ENOMEM; goto attach_out; } ((struct busresources *)(pci_info->bar0resource))->bus_tag = rman_get_bustag(pci_info->regmap0); ((struct busresources *)(pci_info->bar0resource))->bus_handle = rman_get_bushandle(pci_info->regmap0); ((struct busresources *)(pci_info->bar0resource))->bar_start_addr = pci_info->regmap0; /* Get virtual address for BAR1 */ rid1 = PCIR_BAR(2); pci_info->regmap1 = bus_alloc_resource_any(dev, SYS_RES_MEMORY, &rid1, RF_ACTIVE); if(pci_info->regmap1 == NULL) { xge_trace(XGE_ERR, "NULL handler for BAR1"); freeResources(dev, 6); retValue = ENOMEM; goto attach_out; } attr.bar1 = (char *)pci_info->regmap1; pci_info->bar1resource = (busresource_t*) xge_malloc(sizeof(busresource_t)); if(pci_info->bar1resource == NULL) { xge_trace(XGE_ERR, "Allocating memory for bar0resources failed"); freeResources(dev, 7); retValue = ENOMEM; goto attach_out; } ((struct busresources *)(pci_info->bar1resource))->bus_tag = rman_get_bustag(pci_info->regmap1); ((struct busresources *)(pci_info->bar1resource))->bus_handle = rman_get_bushandle(pci_info->regmap1); ((struct busresources *)(pci_info->bar1resource))->bar_start_addr = pci_info->regmap1; /* Save PCI config space */ xge_pci_space_save(dev); attr.regh0 = (busresource_t *) pci_info->bar0resource; attr.regh1 = (busresource_t *) pci_info->bar1resource; attr.irqh = lldev->irqhandle; attr.cfgh = pci_info; attr.pdev = pci_info; /* Initialize device configuration parameters */ xge_init_params(device_config, dev); /* Initialize HAL device */ error = xge_hal_device_initialize(hldev, &attr, device_config); if(error != XGE_HAL_OK) { switch(error) { case XGE_HAL_ERR_DRIVER_NOT_INITIALIZED: xge_trace(XGE_ERR, "XGE_HAL_ERR_DRIVER_NOT_INITIALIZED"); break; case XGE_HAL_ERR_OUT_OF_MEMORY: xge_trace(XGE_ERR, "XGE_HAL_ERR_OUT_OF_MEMORY"); break; case XGE_HAL_ERR_BAD_SUBSYSTEM_ID: xge_trace(XGE_ERR, "XGE_HAL_ERR_BAD_SUBSYSTEM_ID"); break; case XGE_HAL_ERR_INVALID_MAC_ADDRESS: xge_trace(XGE_ERR, "XGE_HAL_ERR_INVALID_MAC_ADDRESS"); break; case XGE_HAL_INF_MEM_STROBE_CMD_EXECUTING: xge_trace(XGE_ERR, "XGE_HAL_INF_MEM_STROBE_CMD_EXECUTING"); break; case XGE_HAL_ERR_SWAPPER_CTRL: xge_trace(XGE_ERR, "XGE_HAL_ERR_SWAPPER_CTRL"); break; case XGE_HAL_ERR_DEVICE_IS_NOT_QUIESCENT: xge_trace(XGE_ERR, "XGE_HAL_ERR_DEVICE_IS_NOT_QUIESCENT"); break; } xge_trace(XGE_ERR, "Initializing HAL device failed (error: %d)\n", error); freeResources(dev, 8); retValue = ENXIO; goto attach_out; } desc = (char *) malloc(100, M_DEVBUF, M_NOWAIT); if(desc == NULL) { retValue = ENOMEM; } else { sprintf(desc, "%s (Rev %d) Driver v%s \n%s: Serial Number: %s ", hldev->vpd_data.product_name, hldev->revision, DRIVER_VERSION, device_get_nameunit(dev), hldev->vpd_data.serial_num); printf("%s: Xframe%s %s\n", device_get_nameunit(dev), ((hldev->device_id == XGE_PCI_DEVICE_ID_XENA_2) ? "I": "II"), desc); free(desc, M_DEVBUF); } if(pci_get_device(dev) == XGE_PCI_DEVICE_ID_HERC_2) { error = xge_hal_mgmt_reg_read(hldev, 0, xge_offsetof(xge_hal_pci_bar0_t, pci_info), &val64); if(error != XGE_HAL_OK) { xge_trace(XGE_ERR, "Error for getting bus speed"); } mesg = (char *) xge_malloc(20); if(mesg == NULL) { freeResources(dev, 8); retValue = ENOMEM; goto attach_out; } sprintf(mesg, "%s: Device is on %s bit", device_get_nameunit(dev), (val64 & BIT(8)) ? "32":"64"); mode = (u8)((val64 & vBIT(0xF, 0, 4)) >> 60); switch(mode) { case 0x00: xge_os_printf("%s PCI 33MHz bus", mesg); break; case 0x01: xge_os_printf("%s PCI 66MHz bus", mesg); break; case 0x02: xge_os_printf("%s PCIX(M1) 66MHz bus", mesg); break; case 0x03: xge_os_printf("%s PCIX(M1) 100MHz bus", mesg); break; case 0x04: xge_os_printf("%s PCIX(M1) 133MHz bus", mesg); break; case 0x05: xge_os_printf("%s PCIX(M2) 133MHz bus", mesg); break; case 0x06: xge_os_printf("%s PCIX(M2) 200MHz bus", mesg); break; case 0x07: xge_os_printf("%s PCIX(M2) 266MHz bus", mesg); break; } free(mesg, M_DEVBUF); } xge_hal_device_private_set(hldev, lldev); error = xge_interface_setup(dev); if(error != 0) { retValue = error; goto attach_out; } ifnetp = lldev->ifnetp; ifnetp->if_mtu = device_config->mtu; xge_media_init(dev); /* Interrupt */ rid = 0; lldev->irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &rid, RF_SHAREABLE | RF_ACTIVE); if(lldev->irq == NULL) { xge_trace(XGE_ERR, "NULL handler for IRQ"); freeResources(dev, 10); retValue = ENOMEM; goto attach_out; } /* Associate interrupt handler with the device */ error = bus_setup_intr(dev, lldev->irq, INTR_TYPE_NET | INTR_MPSAFE, #if __FreeBSD_version > 700030 xge_intr_filter, #endif (void *)xge_intr, lldev, &lldev->irqhandle); if(error != 0) { xge_trace(XGE_ERR, "Associating interrupt handler with device failed"); freeResources(dev, 11); retValue = ENXIO; goto attach_out; } /* Create DMA tags */ error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent */ PAGE_SIZE, /* Alignment */ 0, /* Bounds */ BUS_SPACE_MAXADDR, /* Low Address */ BUS_SPACE_MAXADDR, /* High Address */ NULL, /* Filter Function */ NULL, /* Filter Function Arguments */ MCLBYTES * MAX_SEGS, /* Maximum Size */ MAX_SEGS, /* Number of Segments */ MCLBYTES, /* Maximum Segment Size */ BUS_DMA_ALLOCNOW, /* Flags */ NULL, /* Lock Function */ NULL, /* Lock Function Arguments */ (&lldev->dma_tag_tx)); /* DMA Tag */ if(error != 0) { xge_trace(XGE_ERR, "Tx DMA tag creation failed"); freeResources(dev, 12); retValue = ENOMEM; goto attach_out; } error = bus_dma_tag_create( bus_get_dma_tag(dev), /* Parent */ PAGE_SIZE, /* Alignment */ 0, /* Bounds */ BUS_SPACE_MAXADDR, /* Low Address */ BUS_SPACE_MAXADDR, /* High Address */ NULL, /* Filter Function */ NULL, /* Filter Function Arguments */ MJUMPAGESIZE, /* Maximum Size */ 1, /* Number of Segments */ MJUMPAGESIZE, /* Maximum Segment Size */ BUS_DMA_ALLOCNOW, /* Flags */ NULL, /* Lock Function */ NULL, /* Lock Function Arguments */ (&lldev->dma_tag_rx)); /* DMA Tag */ if(error != 0) { xge_trace(XGE_ERR, "Rx DMA tag creation failed"); freeResources(dev, 13); retValue = ENOMEM; goto attach_out; } /*Updating lldev->buffer_mode parameter*/ pRingConfig = &(hldev->config.ring); if((device_config->mtu + XGE_HAL_MAC_HEADER_MAX_SIZE) <= PAGE_SIZE) { #if defined(XGE_FEATURE_BUFFER_MODE_3) xge_os_printf("%s: 3 Buffer Mode Enabled", device_get_nameunit(dev)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; } pRingConfig->scatter_mode = XGE_HAL_RING_QUEUE_SCATTER_MODE_A; lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = XGE_HAL_TCPIP_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[2] = device_config->mtu; lldev->rxd_mbuf_cnt = 3; #else #if defined(XGE_FEATURE_BUFFER_MODE_2) xge_os_printf("%s: 2 Buffer Mode Enabled", device_get_nameunit(dev)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; } pRingConfig->scatter_mode = XGE_HAL_RING_QUEUE_SCATTER_MODE_B; lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_2; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = device_config->mtu; lldev->rxd_mbuf_cnt = 2; #else lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_1; lldev->rxd_mbuf_len[0] = device_config->mtu; lldev->rxd_mbuf_cnt = 1; #endif #endif } else { xge_os_printf("%s: 5 Buffer Mode Enabled", device_get_nameunit(dev)); xge_os_memzero(lldev->rxd_mbuf_len, sizeof(lldev->rxd_mbuf_len)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_5; } lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_5; buffer_length = device_config->mtu; buffer_index = 2; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = XGE_HAL_TCPIP_HEADER_MAX_SIZE; while(buffer_length > PAGE_SIZE) { buffer_length -= PAGE_SIZE; lldev->rxd_mbuf_len[buffer_index] = PAGE_SIZE; buffer_index++; } BUFALIGN(buffer_length); lldev->rxd_mbuf_len[buffer_index] = buffer_length; lldev->rxd_mbuf_cnt = buffer_index; } #ifdef XGE_FEATURE_LRO xge_os_printf("%s: LRO (Large Receive Offload) Enabled", device_get_nameunit(dev)); #endif #ifdef XGE_FEATURE_TSO xge_os_printf("%s: TSO (TCP Segmentation Offload) enabled", device_get_nameunit(dev)); #endif attach_out: free(device_config, M_DEVBUF); attach_out_config: LEAVE_FUNCTION return retValue; } /****************************************** * freeResources * Parameters: Device structure, error (used * to branch freeing) * Return: None * Description: Frees allocated resources ******************************************/ void freeResources(device_t dev, int error) { xgelldev_t *lldev; pci_info_t *pci_info; xge_hal_device_t *hldev; int rid, status; ENTER_FUNCTION /* LL Device */ lldev = (xgelldev_t *) device_get_softc(dev); pci_info = lldev->pdev; /* HAL Device */ hldev = lldev->devh; switch(error) { case 0: status = bus_dma_tag_destroy(lldev->dma_tag_rx); if(status) { xge_trace(XGE_ERR, "Rx DMA tag destroy failed"); } case 13: status = bus_dma_tag_destroy(lldev->dma_tag_tx); if(status) { xge_trace(XGE_ERR, "Tx DMA tag destroy failed"); } case 12: /* Teardown interrupt handler - device association */ bus_teardown_intr(dev, lldev->irq, lldev->irqhandle); case 11: /* Release IRQ */ bus_release_resource(dev, SYS_RES_IRQ, 0, lldev->irq); case 10: /* Media */ ifmedia_removeall(&lldev->xge_media); /* Detach Ether */ ether_ifdetach(lldev->ifnetp); if_free(lldev->ifnetp); xge_hal_device_private_set(hldev, NULL); xge_hal_device_disable(hldev); case 9: /* HAL Device */ xge_hal_device_terminate(hldev); case 8: /* Restore PCI configuration space */ xge_pci_space_restore(dev); /* Free bar1resource */ free(pci_info->bar1resource, M_DEVBUF); case 7: /* Release BAR1 */ rid = PCIR_BAR(2); bus_release_resource(dev, SYS_RES_MEMORY, rid, pci_info->regmap1); case 6: /* Free bar0resource */ free(pci_info->bar0resource, M_DEVBUF); case 5: /* Release BAR0 */ rid = PCIR_BAR(0); bus_release_resource(dev, SYS_RES_MEMORY, rid, pci_info->regmap0); case 4: /* Disable Bus Master */ pci_disable_busmaster(dev); /* Free pci_info_t */ lldev->pdev = NULL; free(pci_info, M_DEVBUF); case 3: /* Free device configuration struct and HAL device */ free(hldev, M_DEVBUF); case 2: /* Terminate HAL driver */ hal_driver_init_count = hal_driver_init_count - 1; if(!hal_driver_init_count) { xge_hal_driver_terminate(); } case 1: if(mtx_initialized(&lldev->xge_lock) != 0) { mtx_destroy(&lldev->xge_lock); } } LEAVE_FUNCTION } /****************************************** * xge_detach * Parameters: Device structure * Return: 0 * Description: Detaches the driver from the * kernel subsystem. ******************************************/ int xge_detach(device_t dev) { xgelldev_t *lldev = (xgelldev_t *)device_get_softc(dev); ENTER_FUNCTION mtx_lock(&lldev->xge_lock); lldev->in_detach = 1; xge_stop(lldev); mtx_unlock(&lldev->xge_lock); freeResources(dev, 0); LEAVE_FUNCTION return 0; } /****************************************** * xge_shutdown * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Gets called when the system * is about to be shutdown. ******************************************/ int xge_shutdown(device_t dev) { xgelldev_t *lldev = (xgelldev_t *) device_get_softc(dev); ENTER_FUNCTION mtx_lock(&lldev->xge_lock); xge_stop(lldev); mtx_unlock(&lldev->xge_lock); LEAVE_FUNCTION return 0; } /****************************************** * Function: xge_interface_setup * Parameters: Device pointer * Return: 0/ENXIO/ENOMEM * Description: Sets up the interface * through ifnet pointer ******************************************/ int xge_interface_setup(device_t dev) { u8 mcaddr[ETHER_ADDR_LEN]; xge_hal_status_e status_code; xgelldev_t *lldev = (xgelldev_t *)device_get_softc(dev); struct ifnet *ifnetp; xge_hal_device_t *hldev = lldev->devh; int retValue = 0; ENTER_FUNCTION /* Get the MAC address of the device */ status_code = xge_hal_device_macaddr_get(hldev, 0, &mcaddr); if(status_code != XGE_HAL_OK) { switch(status_code) { case XGE_HAL_INF_MEM_STROBE_CMD_EXECUTING: xge_trace(XGE_ERR, "Failed to retrieve MAC address (timeout)"); break; case XGE_HAL_ERR_OUT_OF_MAC_ADDRESSES: xge_trace(XGE_ERR, "Invalid MAC address index"); break; default: xge_trace(XGE_TRACE, "Default Case"); break; } freeResources(dev, 9); retValue = ENXIO; goto ifsetup_out; } /* Get interface ifnet structure for this Ether device */ ifnetp = lldev->ifnetp = if_alloc(IFT_ETHER); if(ifnetp == NULL) { xge_trace(XGE_ERR, "Allocating/getting ifnet structure failed"); freeResources(dev, 9); retValue = ENOMEM; goto ifsetup_out; } /* Initialize interface ifnet structure */ if_initname(ifnetp, device_get_name(dev), device_get_unit(dev)); ifnetp->if_mtu = XGE_HAL_DEFAULT_MTU; /* * TODO: Can't set more than 2Gbps. -- Higher value results in overflow. * But there is no effect in performance even if you set this to 10 Mbps */ ifnetp->if_baudrate = IF_Gbps(2); ifnetp->if_init = xge_init; ifnetp->if_softc = lldev; ifnetp->if_flags = IFF_BROADCAST | IFF_SIMPLEX | IFF_MULTICAST; ifnetp->if_ioctl = xge_ioctl; ifnetp->if_start = xge_send; /* TODO: Check and assign optimal value */ ifnetp->if_snd.ifq_maxlen = IFQ_MAXLEN; ifnetp->if_capabilities = IFCAP_VLAN_HWTAGGING | IFCAP_VLAN_MTU | IFCAP_HWCSUM; ifnetp->if_capenable = ifnetp->if_capabilities; #ifdef XGE_FEATURE_TSO ifnetp->if_capabilities |= IFCAP_TSO4; ifnetp->if_capenable |= IFCAP_TSO4; #endif /* Attach the interface */ ether_ifattach(ifnetp, mcaddr); ifsetup_out: LEAVE_FUNCTION return retValue; } /****************************************** * xgell_callback_link_up * Parameters: Per adapter xgelldev_t * structure pointer as void * * Return: None * Description: Called by HAL to notify * hardware link up state change ******************************************/ void xgell_callback_link_up(void *userdata) { xgelldev_t *lldev = (xgelldev_t *)userdata; struct ifnet *ifnetp = lldev->ifnetp; ENTER_FUNCTION ifnetp->if_flags &= ~IFF_DRV_OACTIVE; if_link_state_change(ifnetp, LINK_STATE_UP); LEAVE_FUNCTION } /****************************************** * xgell_callback_link_down * Parameters: Per adapter xgelldev_t * structure pointer as void * * Return: None * Description: Called by HAL to notify * hardware link up state change ******************************************/ void xgell_callback_link_down(void *userdata) { xgelldev_t *lldev = (xgelldev_t *)userdata; struct ifnet *ifnetp = lldev->ifnetp; ENTER_FUNCTION ifnetp->if_flags |= IFF_DRV_OACTIVE; if_link_state_change(ifnetp, LINK_STATE_DOWN); LEAVE_FUNCTION } /****************************************** * xgell_callback_crit_err * Parameters: Per adapter xgelldev_t * structure pointer as void *, event, * serr_data -> * Return: None * Description: Called by HAL on serious * error event ******************************************/ void xgell_callback_crit_err(void *userdata, xge_hal_event_e type, u64 serr_data) { ENTER_FUNCTION xge_trace(XGE_ERR, "Critical Error"); xgell_reset(userdata); LEAVE_FUNCTION } /****************************************** * xgell_callback_event * Parameters: Queue item * Return: None * Description: Called by HAL in case of * some unknown to HAL events. ******************************************/ void xgell_callback_event(xge_queue_item_t *item) { xgelldev_t *lldev = NULL; xge_hal_device_t *hldev = NULL; struct ifnet *ifnetp = NULL; ENTER_FUNCTION hldev = item->context; lldev = xge_hal_device_private(hldev); ifnetp = lldev->ifnetp; if(item->event_type == XGE_LL_EVENT_TRY_XMIT_AGAIN) { if(lldev->initialized) { if(xge_hal_channel_dtr_count(lldev->fifo_channel_0) > 0) { ifnetp->if_flags &= ~IFF_DRV_OACTIVE; } else { /* try next time */ xge_queue_produce_context( xge_hal_device_queue(lldev->devh), XGE_LL_EVENT_TRY_XMIT_AGAIN, lldev->devh); } } } else if(item->event_type == XGE_LL_EVENT_DEVICE_RESETTING) { xgell_reset(item->context); } LEAVE_FUNCTION } /****************************************** * Function: xge_ifmedia_change * Parameters: Pointer to ifnet structure * Return: 0 for success, EINVAL if media * type is not IFM_ETHER. * Description: Media change driver callback ******************************************/ int xge_ifmedia_change(struct ifnet *ifnetp) { xgelldev_t *lldev = ifnetp->if_softc; struct ifmedia *ifmediap = &lldev->xge_media; ENTER_FUNCTION LEAVE_FUNCTION return (IFM_TYPE(ifmediap->ifm_media) != IFM_ETHER) ? EINVAL:0; } /****************************************** * Function: xge_ifmedia_status * Parameters: Pointer to ifnet structure * ifmediareq structure pointer * through which status of media * will be returned. * Return: None * Description: Media status driver callback ******************************************/ void xge_ifmedia_status(struct ifnet *ifnetp, struct ifmediareq *ifmr) { xge_hal_status_e status; u64 regvalue; xgelldev_t *lldev = ifnetp->if_softc; xge_hal_device_t *hldev = lldev->devh; ENTER_FUNCTION ifmr->ifm_status = IFM_AVALID; ifmr->ifm_active = IFM_ETHER; status = xge_hal_mgmt_reg_read(hldev, 0, xge_offsetof(xge_hal_pci_bar0_t, adapter_status), ®value); if(status != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting adapter status failed"); return; } if((regvalue & (XGE_HAL_ADAPTER_STATUS_RMAC_REMOTE_FAULT | XGE_HAL_ADAPTER_STATUS_RMAC_LOCAL_FAULT)) == 0) { ifmr->ifm_status |= IFM_ACTIVE; ifmr->ifm_active |= IFM_10G_SR | IFM_FDX; if_link_state_change(ifnetp, LINK_STATE_UP); } else { if_link_state_change(ifnetp, LINK_STATE_DOWN); } LEAVE_FUNCTION } /****************************************** * Function: xge_ioctl * Parameters: Pointer to ifnet structure, * command -> indicates requests, * data -> passed values (if any) * Return: * Description: IOCTL entry point. Called * when the user wants to * configure the interface ******************************************/ int xge_ioctl(struct ifnet *ifnetp, unsigned long command, caddr_t data) { struct ifmedia *ifmediap; xge_hal_stats_hw_info_t *hw_stats; xge_hal_pci_config_t *pci_conf; xge_hal_device_config_t *device_conf; xge_hal_stats_sw_err_t *tcode; xge_hal_stats_device_info_t *intr; bar0reg_t *reg; xge_hal_status_e status_code; xge_hal_device_t *hldev; void *regInfo; u64 value; u64 offset; char *pAccess; char *version; int retValue = 0, index = 0, buffer_mode = 0; struct ifreq *ifreqp = (struct ifreq *) data; xgelldev_t *lldev = ifnetp->if_softc; ifmediap = &lldev->xge_media; hldev = lldev->devh; if(lldev->in_detach) { return retValue; } switch(command) { /* Set/Get ifnet address */ case SIOCSIFADDR: case SIOCGIFADDR: ether_ioctl(ifnetp, command, data); break; /* Set ifnet MTU */ case SIOCSIFMTU: retValue = changeMtu(lldev, ifreqp->ifr_mtu); break; /* Set ifnet flags */ case SIOCSIFFLAGS: mtx_lock(&lldev->xge_lock); if(ifnetp->if_flags & IFF_UP) { /* Link status is UP */ if(!(ifnetp->if_drv_flags & IFF_DRV_RUNNING)) { xge_init_locked(lldev); } xge_disable_promisc(lldev); xge_enable_promisc(lldev); } else { /* Link status is DOWN */ /* If device is in running, make it down */ if(ifnetp->if_drv_flags & IFF_DRV_RUNNING) { xge_stop(lldev); } } mtx_unlock(&lldev->xge_lock); break; /* Add/delete multicast address */ case SIOCADDMULTI: case SIOCDELMULTI: if(ifnetp->if_drv_flags & IFF_DRV_RUNNING) { xge_setmulti(lldev); } break; /* Set/Get net media */ case SIOCSIFMEDIA: case SIOCGIFMEDIA: retValue = ifmedia_ioctl(ifnetp, ifreqp, ifmediap, command); break; /* Set capabilities */ case SIOCSIFCAP: mtx_lock(&lldev->xge_lock); int mask = 0; mask = ifreqp->ifr_reqcap ^ ifnetp->if_capenable; #if defined(__FreeBSD_version) && (__FreeBSD_version >= 700026) if(mask & IFCAP_TSO4) { if(ifnetp->if_capenable & IFCAP_TSO4) { ifnetp->if_capenable &= ~IFCAP_TSO4; ifnetp->if_hwassist &= ~CSUM_TSO; } /*enable tso only if txcsum is enabled*/ if(ifnetp->if_capenable & IFCAP_TXCSUM) { ifnetp->if_capenable |= IFCAP_TSO4; ifnetp->if_hwassist |= CSUM_TSO; } } #endif mtx_unlock(&lldev->xge_lock); break; /* Custom IOCTL 0 : * Used to get Statistics & PCI configuration through application */ case SIOCGPRIVATE_0: pAccess = (char*) ifreqp->ifr_data; if(*pAccess == XGE_QUERY_STATS) { mtx_lock(&lldev->xge_lock); status_code = xge_hal_stats_hw(hldev, &hw_stats); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting statistics failed (Status: %d)", status_code); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; } copyout(hw_stats, ifreqp->ifr_data, sizeof(xge_hal_stats_hw_info_t)); mtx_unlock(&lldev->xge_lock); } else if(*pAccess == XGE_QUERY_PCICONF) { pci_conf = xge_malloc(sizeof(xge_hal_pci_config_t)); if(pci_conf == NULL) { return(ENOMEM); } mtx_lock(&lldev->xge_lock); status_code = xge_hal_mgmt_pci_config(hldev, pci_conf, sizeof(xge_hal_pci_config_t)); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting PCIconfiguration failed (Status: %d)", status_code); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; } copyout(pci_conf, ifreqp->ifr_data, sizeof(xge_hal_pci_config_t)); mtx_unlock(&lldev->xge_lock); free(pci_conf, M_DEVBUF); } else if(*pAccess ==XGE_QUERY_INTRSTATS) { intr = xge_malloc(sizeof(xge_hal_stats_device_info_t)); if(intr == NULL) { return(ENOMEM); } mtx_lock(&lldev->xge_lock); status_code =xge_hal_mgmt_device_stats(hldev, intr, sizeof(xge_hal_stats_device_info_t)); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting intr statistics failed (Status: %d)", status_code); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; } copyout(intr, ifreqp->ifr_data, sizeof(xge_hal_stats_device_info_t)); mtx_unlock(&lldev->xge_lock); free(intr, M_DEVBUF); } else if(*pAccess == XGE_QUERY_TCODE) { tcode = xge_malloc(sizeof(xge_hal_stats_sw_err_t)); if(tcode == NULL) { return(ENOMEM); } mtx_lock(&lldev->xge_lock); status_code =xge_hal_mgmt_sw_stats(hldev, tcode, sizeof(xge_hal_stats_sw_err_t)); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting tcode statistics failed (Status: %d)", status_code); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; } copyout(tcode, ifreqp->ifr_data, sizeof(xge_hal_stats_sw_err_t)); mtx_unlock(&lldev->xge_lock); free(tcode, M_DEVBUF); } else if(*pAccess ==XGE_READ_VERSION) { version = xge_malloc(BUFFER_SIZE); if(version == NULL) { return(ENOMEM); } mtx_lock(&lldev->xge_lock); strcpy(version,DRIVER_VERSION); copyout(version, ifreqp->ifr_data, BUFFER_SIZE); mtx_unlock(&lldev->xge_lock); free(version, M_DEVBUF); } else if(*pAccess == XGE_QUERY_DEVCONF) { device_conf = xge_malloc(sizeof(xge_hal_device_config_t)); if(device_conf == NULL) { return(ENOMEM); } mtx_lock(&lldev->xge_lock); status_code = xge_hal_mgmt_device_config(hldev, device_conf, sizeof(xge_hal_device_config_t)); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting devconfig failed (Status: %d)", status_code); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; } if(copyout(device_conf, ifreqp->ifr_data, sizeof(xge_hal_device_config_t)) != 0) { xge_trace(XGE_ERR, "Device configuration copyout erro"); } mtx_unlock(&lldev->xge_lock); free(device_conf, M_DEVBUF); } else if(*pAccess == XGE_QUERY_BUFFER_MODE) { buffer_mode = lldev->buffer_mode; if(copyout(&buffer_mode, ifreqp->ifr_data, sizeof(int)) != 0) { xge_trace(XGE_ERR, "Error with copyout of buffermode"); retValue = EINVAL; } } else if((*pAccess == XGE_SET_BUFFER_MODE_1) || (*pAccess == XGE_SET_BUFFER_MODE_2) || (*pAccess == XGE_SET_BUFFER_MODE_3) || (*pAccess == XGE_SET_BUFFER_MODE_5)) { switch(*pAccess) { case XGE_SET_BUFFER_MODE_1: *pAccess = 'Y'; break; case XGE_SET_BUFFER_MODE_2: case XGE_SET_BUFFER_MODE_3: case XGE_SET_BUFFER_MODE_5: *pAccess = 'N'; break; } if(copyout(pAccess, ifreqp->ifr_data, sizeof(pAccess)) != 0) { xge_trace(XGE_ERR, "Copyout of chgbufmode result failed"); } } else { xge_trace(XGE_TRACE, "Nothing is matching"); } break; /* * Custom IOCTL 1 : * Used to get BAR0 register values through application program */ case SIOCGPRIVATE_1: reg = (bar0reg_t *) ifreqp->ifr_data; if(strcmp(reg->option,"-r") == 0) { offset = reg->offset; value = 0x0000; mtx_lock(&lldev->xge_lock); status_code = xge_hal_mgmt_reg_read(hldev, 0, offset, &value ); if(status_code == XGE_HAL_OK) { reg->value = value; } else { xge_trace(XGE_ERR, "Getting register value failed"); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; break; } copyout(reg, ifreqp->ifr_data, sizeof(bar0reg_t)); mtx_unlock(&lldev->xge_lock); } else if(strcmp(reg->option,"-w") == 0) { offset = reg->offset; value = reg->value; mtx_lock(&lldev->xge_lock); status_code = xge_hal_mgmt_reg_write(hldev, 0, offset, value ); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting register value failed"); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; break; } value = 0x0000; status_code = xge_hal_mgmt_reg_read(hldev, 0, offset, &value); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Getting register value failed"); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; break; } if(reg->value != value) { mtx_unlock(&lldev->xge_lock); retValue = EINVAL; break; } mtx_unlock(&lldev->xge_lock); } else { offset = 0x0000; value = 0x0000; regInfo = (void *)ifreqp->ifr_data; mtx_lock(&lldev->xge_lock); for(index = 0, offset = 0; offset <= XGE_OFFSET_OF_LAST_REG; index++, offset += 0x0008) { status_code = xge_hal_mgmt_reg_read(hldev, 0, offset, &value); if(status_code == XGE_HAL_OK) { *( ( u64 *)( ( u64 * )regInfo + index ) ) = value; } else { xge_trace(XGE_ERR, "Getting register value failed"); mtx_unlock(&lldev->xge_lock); retValue = EINVAL; break; } } copyout(regInfo, ifreqp->ifr_data, sizeof(xge_hal_pci_bar0_t)); mtx_unlock(&lldev->xge_lock); } break; default: retValue = EINVAL; break; } return retValue; } /****************************************** * Function: xge_init * Parameters: Pointer to per-device * xgelldev_t structure as void*. * Return: None * Description: Init entry point. ******************************************/ void xge_init(void *plldev) { ENTER_FUNCTION xgelldev_t *lldev = (xgelldev_t *)plldev; mtx_lock(&lldev->xge_lock); xge_init_locked(lldev); mtx_unlock(&lldev->xge_lock); LEAVE_FUNCTION } void xge_init_locked(void *pdevin) { ENTER_FUNCTION xgelldev_t *lldev = (xgelldev_t *)pdevin; struct ifnet *ifnetp = lldev->ifnetp; device_t dev = lldev->device; mtx_assert((&lldev->xge_lock), MA_OWNED); /* If device is in running state, initializing is not required */ if(ifnetp->if_drv_flags & IFF_DRV_RUNNING) { return; } /* Initializing timer */ callout_init(&lldev->timer, CALLOUT_MPSAFE); xge_initialize(dev, XGE_HAL_CHANNEL_OC_NORMAL); LEAVE_FUNCTION } /****************************************** * Function: xge_timer * Parameters: Pointer to per-device * xgelldev_t structure as void*. * Return: None * Description: Polls the changes. ******************************************/ void xge_timer(void *devp) { xgelldev_t *lldev = (xgelldev_t *)devp; xge_hal_device_t *hldev = lldev->devh; /* Poll for changes */ xge_hal_device_poll(hldev); /* Reset timer */ callout_reset(&lldev->timer, hz, xge_timer, lldev); return; } /****************************************** * Function: xge_stop * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Deactivates the interface * (Called on "ifconfig down" ******************************************/ void xge_stop(xgelldev_t *lldev) { struct ifnet *ifnetp = lldev->ifnetp; device_t dev = lldev->device; ENTER_FUNCTION mtx_assert((&lldev->xge_lock), MA_OWNED); /* If device is not in "Running" state, return */ if (!(ifnetp->if_drv_flags & IFF_DRV_RUNNING)) { goto xfstop_out; } xge_terminate(dev, XGE_HAL_CHANNEL_OC_NORMAL); xfstop_out: LEAVE_FUNCTION return; } /* * xge_intr_filter * * ISR filter function * @handle softc/lldev per device structure */ int xge_intr_filter(void *handle) { xgelldev_t *lldev = NULL; xge_hal_device_t *hldev = NULL; xge_hal_pci_bar0_t *bar0 = NULL; device_t dev = NULL; u16 retValue = FILTER_STRAY; u64 val64 = 0; lldev = (xgelldev_t *)handle; hldev = lldev->devh; dev = lldev->device; bar0 = (xge_hal_pci_bar0_t *)hldev->bar0; val64 = xge_os_pio_mem_read64(lldev->pdev, hldev->regh0, &bar0->general_int_status); retValue = (!val64) ? FILTER_STRAY : FILTER_SCHEDULE_THREAD; return retValue; } /****************************************** * xge_intr * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Interrupt service routine ******************************************/ void xge_intr(void *plldev) { xge_hal_status_e status; xgelldev_t *lldev = (xgelldev_t *)plldev; xge_hal_device_t *hldev = (xge_hal_device_t *)lldev->devh; struct ifnet *ifnetp = lldev->ifnetp; mtx_lock(&lldev->xge_lock); if(ifnetp->if_drv_flags & IFF_DRV_RUNNING) { status = xge_hal_device_handle_irq(hldev); if(!(IFQ_DRV_IS_EMPTY(&ifnetp->if_snd))) { xge_send_locked(ifnetp); } } mtx_unlock(&lldev->xge_lock); return; } /******************************************** * Function : xgell_rx_open * Parameters: Queue index, channel * open/close/reopen flag * Return: 0 or ENODEV * Description: Initialize and open all Rx * channels. ******************************************/ int xgell_rx_open(int qid, xgelldev_t *lldev, xge_hal_channel_reopen_e rflag) { u64 adapter_status = 0x0; int retValue = 0; xge_hal_status_e status_code; ENTER_FUNCTION xge_hal_channel_attr_t attr = { .post_qid = qid, .compl_qid = 0, .callback = xgell_rx_compl, .per_dtr_space = sizeof(xgell_rx_priv_t), .flags = 0, .type = XGE_HAL_CHANNEL_TYPE_RING, .userdata = lldev, .dtr_init = xgell_rx_initial_replenish, .dtr_term = xgell_rx_term }; /* If device is not ready, return */ if(xge_hal_device_status(lldev->devh, &adapter_status)) { xge_trace(XGE_ERR, "Device is not ready. Adapter status: 0x%llx", (unsigned long long) adapter_status); retValue = -ENODEV; goto rxopen_out; } /* Open ring channel */ status_code = xge_hal_channel_open(lldev->devh, &attr, &lldev->ring_channel[qid], rflag); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Can not open Rx RING channel, Status: %d\n", status_code); retValue = -ENODEV; goto rxopen_out; } rxopen_out: LEAVE_FUNCTION return retValue; } /****************************************** * Function: xgell_tx_open * Parameters: Channel * open/close/reopen flag * Return: 0 or ENODEV * Description: Initialize and open all Tx * channels. ******************************************/ int xgell_tx_open(xgelldev_t *lldev, xge_hal_channel_reopen_e tflag) { xge_hal_status_e status_code; u64 adapter_status = 0x0; int retValue = 0; ENTER_FUNCTION xge_hal_channel_attr_t attr = { .post_qid = 0, .compl_qid = 0, .callback = xgell_tx_compl, .per_dtr_space = sizeof(xgell_tx_priv_t), .flags = 0, .type = XGE_HAL_CHANNEL_TYPE_FIFO, .userdata = lldev, .dtr_init = xgell_tx_initial_replenish, .dtr_term = xgell_tx_term }; /* If device is not ready, return */ if(xge_hal_device_status(lldev->devh, &adapter_status)) { xge_trace(XGE_ERR, "Device is not ready. Adapter status: 0x%llx\n", (unsigned long long) adapter_status); retValue = -ENODEV; goto txopen_out; } /* Open FIFO channel */ status_code = xge_hal_channel_open(lldev->devh, &attr, &lldev->fifo_channel_0, tflag); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "Can not open Tx FIFO channel, Status: %d\n", status_code); retValue = -ENODEV; goto txopen_out; } txopen_out: LEAVE_FUNCTION return retValue; } /****************************************** * Function: xgell_channel_open * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Opens both Rx and Tx channels. ******************************************/ int xgell_channel_open(xgelldev_t *lldev, xge_hal_channel_reopen_e option) { int status = XGE_HAL_OK; int index = 0; int index2 = 0; ENTER_FUNCTION /* Open ring (Rx) channel */ for(index = 0; index < XGE_RING_COUNT; index++) { if((status = xgell_rx_open(index, lldev, option))) { xge_trace(XGE_ERR, "Opening Rx channel failed (Status: %d)\n", status); for(index2 = 0; index2 < index; index2++) { xge_hal_channel_close(lldev->ring_channel[index2], option); } return status; } } #ifdef XGE_FEATURE_LRO status = xge_hal_lro_init(1, lldev->devh); if (status != XGE_HAL_OK) { xge_trace(XGE_ERR, "cannot init Rx LRO got status code %d", status); return -ENODEV; } #endif /* Open FIFO (Tx) channel */ if((status = xgell_tx_open(lldev, option))) { xge_trace(XGE_ERR, "Opening Tx channel failed (Status: %d)\n", status); for(index = 0; index < XGE_RING_COUNT; index++) { xge_hal_channel_close(lldev->ring_channel[index], option); } } LEAVE_FUNCTION return status; } /****************************************** * Function: xgell_channel_close * Parameters: Per adapter xgelldev_t * structure pointer * Return: 0 for success, non-zero for * failure * Description: Closes both Tx and Rx channels ******************************************/ int xgell_channel_close(xgelldev_t *lldev, xge_hal_channel_reopen_e option) { int index; ENTER_FUNCTION DELAY(1000 * 1000); /* Close FIFO (Tx) channel */ xge_hal_channel_close(lldev->fifo_channel_0, option); /* Close Ring (Rx) channel */ for(index = 0; index < XGE_RING_COUNT; index++) { xge_hal_channel_close(lldev->ring_channel[index], option); } LEAVE_FUNCTION return 0; } /****************************************** * Function: dmamap_cb * Parameters: Parameter passed from dmamap * function, Segment, Number of * segments, error (if any) * Return: None * Description: Callback function used for * DMA mapping ******************************************/ void dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int error) { if(!error) { *(bus_addr_t *) arg = segs->ds_addr; } } /****************************************** * Function: xgell_reset * Parameters: Per adapter xgelldev_t * structure pointer * Return: HAL status code/EPERM * Description: Resets the device ******************************************/ void xgell_reset(xgelldev_t *lldev) { device_t dev = lldev->device; ENTER_FUNCTION xge_trace(XGE_TRACE, "Reseting the chip"); mtx_lock(&lldev->xge_lock); /* If the device is not initialized, return */ if(!lldev->initialized) { goto xreset_out; } xge_terminate(dev, XGE_HAL_CHANNEL_OC_NORMAL); xge_initialize(dev, XGE_HAL_CHANNEL_OC_NORMAL); xreset_out: LEAVE_FUNCTION mtx_unlock(&lldev->xge_lock); return; } /****************************************** * Function: xge_setmulti * Parameters: Per adapter xgelldev_t * structure pointer * Return: None * Description: Set an address as a multicast * address ******************************************/ void xge_setmulti(xgelldev_t *lldev) { ENTER_FUNCTION struct ifmultiaddr *ifma; u8 *lladdr; xge_hal_device_t *hldev = (xge_hal_device_t *)lldev->devh; struct ifnet *ifnetp = lldev->ifnetp; int index = 0; int offset = 1; int table_size = 47; xge_hal_status_e status = XGE_HAL_OK; u8 initial_addr[]= {0xff, 0xff, 0xff, 0xff, 0xff, 0xff}; if((ifnetp->if_flags & IFF_MULTICAST) && (!lldev->all_multicast)) { status = xge_hal_device_mcast_enable(hldev); lldev->all_multicast = 1; } else if((ifnetp->if_flags & IFF_MULTICAST) && (lldev->all_multicast)) { status = xge_hal_device_mcast_disable(hldev); lldev->all_multicast = 0; } if(status != XGE_HAL_OK) { printf("Failed to %s multicast (status: %d)\n", (ifnetp->if_flags & IFF_ALLMULTI ? "enable" : "disable"), status); } /* Updating address list */ IF_ADDR_LOCK(ifnetp); index = 0; TAILQ_FOREACH(ifma, &ifnetp->if_multiaddrs, ifma_link) { if(ifma->ifma_addr->sa_family != AF_LINK) { continue; } lladdr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); index += 1; } IF_ADDR_UNLOCK(ifnetp); if((!lldev->all_multicast) && (index)) { lldev->macaddr_count = (index + 1); if(lldev->macaddr_count > table_size) { return; } /* Clear old addresses */ for(index = 0; index < 48; index++) { xge_hal_device_macaddr_set(hldev, (offset + index), initial_addr); } } /* Add new addresses */ IF_ADDR_LOCK(ifnetp); index = 0; TAILQ_FOREACH(ifma, &ifnetp->if_multiaddrs, ifma_link) { if(ifma->ifma_addr->sa_family != AF_LINK) { continue; } lladdr = LLADDR((struct sockaddr_dl *)ifma->ifma_addr); xge_hal_device_macaddr_set(hldev, (offset + index), lladdr); index += 1; } IF_ADDR_UNLOCK(ifnetp); LEAVE_FUNCTION } /****************************************** * Function: xge_enable_promisc * Parameters: Adapter structure * Return: None * Description: Enables promiscuous mode ******************************************/ void xge_enable_promisc(xgelldev_t *lldev) { struct ifnet *ifnetp = lldev->ifnetp; xge_hal_device_t *hldev = lldev->devh; xge_hal_pci_bar0_t *bar0 = NULL; u64 val64 = 0; ENTER_FUNCTION bar0 = (xge_hal_pci_bar0_t *) hldev->bar0; if(ifnetp->if_flags & IFF_PROMISC) { xge_hal_device_promisc_enable(lldev->devh); /* * When operating in promiscuous mode, don't strip the VLAN tag */ val64 = xge_os_pio_mem_read64(lldev->pdev, hldev->regh0, &bar0->rx_pa_cfg); val64 &= ~XGE_HAL_RX_PA_CFG_STRIP_VLAN_TAG_MODE(1); val64 |= XGE_HAL_RX_PA_CFG_STRIP_VLAN_TAG_MODE(0); xge_os_pio_mem_write64(lldev->pdev, hldev->regh0, val64, &bar0->rx_pa_cfg); xge_trace(XGE_TRACE, "Promiscuous mode ON"); } LEAVE_FUNCTION } /****************************************** * Function: xge_disable_promisc * Parameters: Adapter structure * Return: None * Description: Disables promiscuous mode ******************************************/ void xge_disable_promisc(xgelldev_t *lldev) { xge_hal_device_t *hldev = lldev->devh; xge_hal_pci_bar0_t *bar0 = NULL; u64 val64 = 0; ENTER_FUNCTION bar0 = (xge_hal_pci_bar0_t *) hldev->bar0; xge_hal_device_promisc_disable(lldev->devh); /* * Strip VLAN tag when operating in non-promiscuous mode */ val64 = xge_os_pio_mem_read64(lldev->pdev, hldev->regh0, &bar0->rx_pa_cfg); val64 &= ~XGE_HAL_RX_PA_CFG_STRIP_VLAN_TAG_MODE(1); val64 |= XGE_HAL_RX_PA_CFG_STRIP_VLAN_TAG_MODE(1); xge_os_pio_mem_write64(lldev->pdev, hldev->regh0, val64, &bar0->rx_pa_cfg); xge_trace(XGE_TRACE, "Promiscuous mode OFF"); LEAVE_FUNCTION } /****************************************** * Function: changeMtu * Parameters: Pointer to per-device * xgelldev_t structure, New * MTU size. * Return: None * Description: Changes MTU size to requested ******************************************/ int changeMtu(xgelldev_t *lldev, int NewMtu) { struct ifnet *ifnetp = lldev->ifnetp; xge_hal_device_t *hldev = lldev->devh; int retValue = 0; ENTER_FUNCTION do { /* Check requested MTU size for boundary */ if(xge_hal_device_mtu_check(hldev, NewMtu) != XGE_HAL_OK) { xge_trace(XGE_ERR, "Invalid MTU"); retValue = EINVAL; break; } if(lldev->initialized != 0) { mtx_lock(&lldev->xge_lock); if_down(ifnetp); xge_stop(lldev); ifnetp->if_mtu = NewMtu; changeBufmode(lldev, NewMtu); xge_init_locked((void *)lldev); if_up(ifnetp); mtx_unlock(&lldev->xge_lock); } else { ifnetp->if_mtu = NewMtu; changeBufmode(lldev, NewMtu); } } while(FALSE); LEAVE_FUNCTION return retValue; } /****************************************** * Function: changeBufmode * Parameters: Pointer to per-device * xgelldev_t structure, New * MTU size. * Return: None * Description: Updates RingConfiguration structure * depending the NewMtu size. ******************************************/ int changeBufmode (xgelldev_t *lldev, int NewMtu) { xge_hal_ring_config_t * pRingConfig; xge_hal_device_t *hldev = lldev->devh; device_t dev = lldev->device; int buffer_length = 0, buffer_index = 0, index; pRingConfig = &(hldev->config.ring); xge_os_memzero(lldev->rxd_mbuf_len, sizeof(lldev->rxd_mbuf_len)); if((NewMtu + XGE_HAL_MAC_HEADER_MAX_SIZE) <= MJUMPAGESIZE) { #if defined(XGE_FEATURE_BUFFER_MODE_3) xge_os_printf("%s: 3 Buffer Mode Enabled", device_get_nameunit(dev)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; } pRingConfig->scatter_mode = XGE_HAL_RING_QUEUE_SCATTER_MODE_A; lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = XGE_HAL_TCPIP_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[2] = NewMtu; lldev->rxd_mbuf_cnt = 3; #else #if defined(XGE_FEATURE_BUFFER_MODE_2) xge_os_printf("%s: 2 Buffer Mode Enabled", device_get_nameunit(dev)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_3; } pRingConfig->scatter_mode = XGE_HAL_RING_QUEUE_SCATTER_MODE_B; lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_2; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = NewMtu; lldev->rxd_mbuf_cnt = 2; #else for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_1; } pRingConfig->scatter_mode = XGE_HAL_RING_QUEUE_SCATTER_MODE_A; lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_1; lldev->rxd_mbuf_len[0] = NewMtu; lldev->rxd_mbuf_cnt = 1; #endif #endif } else { #if defined(XGE_FEATURE_BUFFER_MODE_3) || defined (XGE_FEATURE_BUFFER_MODE_2) xge_os_printf("2 or 3 Buffer mode is not supported for given MTU"); xge_os_printf("So changing buffer mode to 5 buffer mode\n"); #endif xge_os_printf("%s: 5 Buffer Mode Enabled", device_get_nameunit(dev)); for(index = 0; index < XGE_RING_COUNT; index++) { pRingConfig->queue[index].buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_5; } lldev->buffer_mode = XGE_HAL_RING_QUEUE_BUFFER_MODE_5; buffer_length = NewMtu; buffer_index = 2; lldev->rxd_mbuf_len[0] = XGE_HAL_MAC_HEADER_MAX_SIZE; lldev->rxd_mbuf_len[1] = XGE_HAL_TCPIP_HEADER_MAX_SIZE; while(buffer_length > MJUMPAGESIZE) { buffer_length -= MJUMPAGESIZE; lldev->rxd_mbuf_len[buffer_index] = MJUMPAGESIZE; buffer_index++; } BUFALIGN(buffer_length); lldev->rxd_mbuf_len[buffer_index] = buffer_length; lldev->rxd_mbuf_cnt = buffer_index+1; } return XGE_HAL_OK; } /************************************************************* * xge_initialize * * @dev: Device structure * @option: Normal/Reset option for channels * * Called by both init and reset functions to enable device, interrupts, and to * open channels. * **************************************************************/ void xge_initialize(device_t dev, xge_hal_channel_reopen_e option) { ENTER_FUNCTION struct ifaddr *ifaddrp; struct sockaddr_dl *sockaddrp; unsigned char *macaddr; xgelldev_t *lldev = (xgelldev_t *) device_get_softc(dev); xge_hal_device_t *hldev = lldev->devh; struct ifnet *ifnetp = lldev->ifnetp; int status = XGE_HAL_OK; xge_trace(XGE_TRACE, "Set MTU size"); status = xge_hal_device_mtu_set(hldev, ifnetp->if_mtu); if(status != XGE_HAL_OK) { xge_trace(XGE_ERR, "Setting HAL device MTU failed (Status: %d)", status); goto init_sub_out; } /* Enable HAL device */ xge_hal_device_enable(hldev); /* Get MAC address and update in HAL */ ifaddrp = ifnetp->if_addr; sockaddrp = (struct sockaddr_dl *)ifaddrp->ifa_addr; sockaddrp->sdl_type = IFT_ETHER; sockaddrp->sdl_alen = ifnetp->if_addrlen; macaddr = LLADDR(sockaddrp); xge_trace(XGE_TRACE, "Setting MAC address: %02x:%02x:%02x:%02x:%02x:%02x\n", *macaddr, *(macaddr + 1), *(macaddr + 2), *(macaddr + 3), *(macaddr + 4), *(macaddr + 5)); status = xge_hal_device_macaddr_set(hldev, 0, macaddr); if(status != XGE_HAL_OK) { xge_trace(XGE_ERR, "Setting MAC address failed (Status: %d)\n", status); } /* Opening channels */ mtx_unlock(&lldev->xge_lock); status = xgell_channel_open(lldev, option); mtx_lock(&lldev->xge_lock); if(status != 0) { goto init_sub_out; } /* Set appropriate flags */ ifnetp->if_drv_flags |= IFF_DRV_RUNNING; ifnetp->if_flags &= ~IFF_DRV_OACTIVE; /* Checksum capability */ ifnetp->if_hwassist = (ifnetp->if_capenable & IFCAP_TXCSUM) ? (CSUM_TCP | CSUM_UDP) : 0; #ifdef XGE_FEATURE_TSO if(ifnetp->if_capenable & IFCAP_TSO4) ifnetp->if_hwassist |= CSUM_TSO; #endif /* Enable interrupts */ xge_hal_device_intr_enable(hldev); callout_reset(&lldev->timer, 10*hz, xge_timer, lldev); /* Disable promiscuous mode */ xge_trace(XGE_TRACE, "If opted, enable promiscuous mode"); xge_enable_promisc(lldev); /* Device is initialized */ lldev->initialized = 1; xge_os_mdelay(1000); init_sub_out: LEAVE_FUNCTION return; } /******************************************************* * xge_terminate * * @dev: Device structure * @option: Normal/Reset option for channels * * Called by both stop and reset functions to disable device, interrupts, and to * close channels. ******************************************************/ void xge_terminate(device_t dev, xge_hal_channel_reopen_e option) { ENTER_FUNCTION xgelldev_t *lldev = (xgelldev_t *)device_get_softc(dev); xge_hal_device_t *hldev = lldev->devh; struct ifnet *ifnetp = lldev->ifnetp; /* Set appropriate flags */ ifnetp->if_drv_flags &= ~(IFF_DRV_RUNNING | IFF_DRV_OACTIVE); /* Stop timer */ callout_stop(&lldev->timer); /* Disable interrupts */ xge_hal_device_intr_disable(hldev); mtx_unlock(&lldev->xge_lock); xge_queue_flush(xge_hal_device_queue(lldev->devh)); mtx_lock(&lldev->xge_lock); /* Disable HAL device */ if(xge_hal_device_disable(hldev) != XGE_HAL_OK) { xge_trace(XGE_ERR, "Disabling HAL device failed"); } /* Close Tx and Rx channels */ xgell_channel_close(lldev, option); /* Reset HAL device */ xge_hal_device_reset(hldev); xge_os_mdelay(1000); lldev->initialized = 0; if_link_state_change(ifnetp, LINK_STATE_DOWN); LEAVE_FUNCTION } /****************************************** * Function: xgell_set_mbuf_cflags * Parameters: mbuf structure pointer * Return: None * Description: This fuction will set the csum_flag of the mbuf ******************************************/ void xgell_set_mbuf_cflags(mbuf_t pkt) { pkt->m_pkthdr.csum_flags = CSUM_IP_CHECKED; pkt->m_pkthdr.csum_flags |= CSUM_IP_VALID; pkt->m_pkthdr.csum_flags |= (CSUM_DATA_VALID | CSUM_PSEUDO_HDR); pkt->m_pkthdr.csum_data = htons(0xffff); } #ifdef XGE_FEATURE_LRO /****************************************** * Function: xgell_lro_flush_sessions * Parameters: Per adapter xgelldev_t * Return: None * Description: This function will flush the LRO session and send the * accumulated LRO packet to Upper layer. ******************************************/ void xgell_lro_flush_sessions(xgelldev_t *lldev) { lro_t *lro; struct ifnet *ifnetp = lldev->ifnetp; xge_hal_device_t *hldev = (xge_hal_device_t *)lldev->devh; while (NULL != (lro = xge_hal_lro_get_next_session(hldev))) { xgell_set_mbuf_cflags(lro->os_buf); /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, lro->os_buf); mtx_lock(&lldev->xge_lock); xge_hal_lro_close_session(lro); } } /****************************************** * Function: xgell_accumulate_large_rx * Parameters: Descriptor info structure, current mbuf structure, * packet length, Per adapter structure, Rx Desc private structure * Return: None * Description: This function will accumulate packets to form the LRO * packets based on various condition. ******************************************/ void xgell_accumulate_large_rx(xge_hal_dtr_info_t *ext_info,mbuf_t pkt, int pkt_length, xgelldev_t *lldev, xgell_rx_priv_t *rxd_priv) { tcplro_t *tcp; lro_t *lro, *lro_end3; xge_hal_status_e status; unsigned char * temp; struct ifnet *ifnetp = lldev->ifnetp; status = xge_hal_accumulate_large_rx(pkt->m_data, &tcp, &pkt_length, &lro, ext_info, lldev->devh, &lro_end3); pkt->m_next = NULL; temp = (unsigned char *)tcp; if(status == XGE_HAL_INF_LRO_BEGIN) { pkt->m_flags |= M_PKTHDR; pkt->m_pkthdr.rcvif = ifnetp; lro->os_buf = lro->os_buf_end = pkt; } else if(status == XGE_HAL_INF_LRO_CONT) { /* * Current mbuf will be combine to form LRO frame, * So mask the pkthdr of the flag variable for current mbuf */ pkt->m_flags = pkt->m_flags & 0xFFFD; //Mask pkthdr pkt->m_data = (u8 *)tcp; pkt->m_len = pkt_length; /* * Combine the current mbuf to the LRO frame and update * the LRO's pkthdr len accordingly */ lro->os_buf_end->m_next = pkt; lro->os_buf_end = pkt; lro->os_buf->m_pkthdr.len += pkt_length; } else if(status == XGE_HAL_INF_LRO_END_2) { lro->os_buf->m_flags |= M_EOR; /* Update the Checksum flags of the LRO frames */ xgell_set_mbuf_cflags(lro->os_buf); /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* * Current packet can not be combined with LRO frame. * Flush the previous LRO frames and send the current packet * seperately */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, lro->os_buf); (*ifnetp->if_input)(ifnetp, pkt); mtx_lock(&lldev->xge_lock); xge_hal_lro_close_session(lro); } else if(status == XGE_HAL_INF_LRO_END_1) { pkt->m_flags = pkt->m_flags & 0xFFFD; pkt->m_data = (u8 *)tcp; pkt->m_len = pkt_length; lro->os_buf_end->m_next = pkt; lro->os_buf->m_pkthdr.len += pkt_length; xgell_set_mbuf_cflags(lro->os_buf); lro->os_buf->m_flags |= M_EOR; /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, lro->os_buf); mtx_lock(&lldev->xge_lock); xge_hal_lro_close_session(lro); } else if(status == XGE_HAL_INF_LRO_END_3) { pkt->m_flags |= M_PKTHDR; pkt->m_len = pkt_length; pkt->m_pkthdr.len = pkt_length; lro_end3->os_buf = lro_end3->os_buf_end = pkt; lro->os_buf->m_flags |= M_EOR; xgell_set_mbuf_cflags(lro->os_buf); /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, lro->os_buf); mtx_lock(&lldev->xge_lock); xge_hal_lro_close_session(lro); } else if((status == XGE_HAL_INF_LRO_UNCAPABLE) || (status == XGE_HAL_INF_LRO_SESSIONS_XCDED)) { pkt->m_flags |= M_PKTHDR; pkt->m_len = pkt_length; pkt->m_pkthdr.len = pkt_length; /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, pkt); mtx_lock(&lldev->xge_lock); } } #endif /****************************************** * Function: xgell_rx_compl * Parameters: Channel handle, descriptor, * transfer code, userdata * (not used) * Return: HAL status code * Description: If the interrupt is because * of a received frame or if * the receive ring contains * fresh as yet un-processed * frames, this function is * called. ******************************************/ xge_hal_status_e xgell_rx_compl(xge_hal_channel_h channelh, xge_hal_dtr_h dtr, u8 t_code, void *userdata) { xge_hal_dtr_info_t ext_info; xge_hal_status_e status_code; struct ifnet *ifnetp; device_t dev; int index; mbuf_t mbuf_up = NULL; xgell_rx_priv_t *rxd_priv = NULL, old_rxd_priv; u16 vlan_tag; // ENTER_FUNCTION /*get the user data portion*/ xgelldev_t *lldev = xge_hal_channel_userdata(channelh); if(!lldev) { xge_ctrace(XGE_TRACE, "xgeX: %s: Failed to get user data", __FUNCTION__); return XGE_HAL_FAIL; } dev = lldev->device; mtx_assert((&lldev->xge_lock), MA_OWNED); /* get the interface pointer */ ifnetp = lldev->ifnetp; do { if(!(ifnetp->if_drv_flags & IFF_DRV_RUNNING)) { return XGE_HAL_FAIL; } if(t_code) { xge_trace(XGE_TRACE, "Packet dropped because of %d", t_code); xge_hal_device_handle_tcode(channelh, dtr, t_code); xge_hal_ring_dtr_post(channelh,dtr); continue; } /* Get the private data for this descriptor*/ rxd_priv = (xgell_rx_priv_t *) xge_hal_ring_dtr_private(channelh, dtr); if(!rxd_priv) { xge_trace(XGE_ERR, "Failed to get descriptor private data"); return XGE_HAL_FAIL; } /* Taking backup of rxd_priv structure details of current packet */ xge_os_memcpy(&old_rxd_priv, rxd_priv, sizeof(xgell_rx_priv_t)); /* Prepare one buffer to send it to upper layer -- since the upper * layer frees the buffer do not use rxd_priv->buffer * Meanwhile prepare a new buffer, do mapping, use it in the * current descriptor and post descriptor back to ring channel */ mbuf_up = rxd_priv->bufferArray[0]; /* Gets details of mbuf i.e., packet length */ xge_ring_dtr_get(mbuf_up, channelh, dtr, lldev, rxd_priv); status_code = (lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) ? xgell_get_buf(dtr, rxd_priv, lldev, 0) : xgell_get_buf_3b_5b(dtr, rxd_priv, lldev); if(status_code != XGE_HAL_OK) { xge_trace(XGE_ERR, "No memory"); /* * Do not deliver the received buffer to the stack. Instead, * Re-post the descriptor with the same buffer */ /* Get back previous rxd_priv structure before posting */ xge_os_memcpy(rxd_priv, &old_rxd_priv, sizeof(xgell_rx_priv_t)); xge_hal_ring_dtr_post(channelh, dtr); continue; } /* Get the extended information */ xge_hal_ring_dtr_info_get(channelh, dtr, &ext_info); if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { /* * As we have allocated a new mbuf for this descriptor, post * this descriptor with new mbuf back to ring channel */ vlan_tag = ext_info.vlan; xge_hal_ring_dtr_post(channelh, dtr); if ((!(ext_info.proto & XGE_HAL_FRAME_PROTO_IP_FRAGMENTED) && (ext_info.proto & XGE_HAL_FRAME_PROTO_TCP_OR_UDP) && (ext_info.l3_cksum == XGE_HAL_L3_CKSUM_OK) && (ext_info.l4_cksum == XGE_HAL_L4_CKSUM_OK))) { /* set Checksum Flag */ xgell_set_mbuf_cflags(mbuf_up); #ifdef XGE_FEATURE_LRO if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { xgell_accumulate_large_rx(&ext_info, mbuf_up, mbuf_up->m_len, lldev, rxd_priv); } #else /* Post-Read sync for buffers*/ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, mbuf_up); mtx_lock(&lldev->xge_lock); #endif } else { /* * Packet with erroneous checksum , let the upper layer * deal with it */ /* Post-Read sync for buffers*/ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); #ifdef XGE_FEATURE_LRO xgell_lro_flush_sessions(lldev); #endif if (vlan_tag) { mbuf_up->m_pkthdr.ether_vtag = vlan_tag; mbuf_up->m_flags |= M_VLANTAG; } /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, mbuf_up); mtx_lock(&lldev->xge_lock); } } else { /* * As we have allocated a new mbuf for this descriptor, post * this descriptor with new mbuf back to ring channel */ xge_hal_ring_dtr_post(channelh, dtr); if ((!(ext_info.proto & XGE_HAL_FRAME_PROTO_IP_FRAGMENTED) && (ext_info.proto & XGE_HAL_FRAME_PROTO_TCP_OR_UDP) && (ext_info.l3_cksum == XGE_HAL_L3_CKSUM_OK) && (ext_info.l4_cksum == XGE_HAL_L4_CKSUM_OK))) { /* set Checksum Flag */ xgell_set_mbuf_cflags(mbuf_up); #ifdef XGE_FEATURE_LRO if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { xgell_accumulate_large_rx(&ext_info, mbuf_up, mbuf_up->m_len, lldev, rxd_priv); } #else /* Post-Read sync for buffers*/ for(index = 0; index < lldev->rxd_mbuf_cnt; index++) { /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map, BUS_DMASYNC_POSTREAD); } /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, mbuf_up); mtx_lock(&lldev->xge_lock); #endif } else { /* * Packet with erroneous checksum , let the upper layer * deal with it */ for(index = 0; index < lldev->rxd_mbuf_cnt; index++) { /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map, BUS_DMASYNC_POSTREAD); } #ifdef XGE_FEATURE_LRO xgell_lro_flush_sessions(lldev); #endif /* Send it up */ mtx_unlock(&lldev->xge_lock); (*ifnetp->if_input)(ifnetp, mbuf_up); mtx_lock(&lldev->xge_lock); } } } while(xge_hal_ring_dtr_next_completed(channelh, &dtr, &t_code) == XGE_HAL_OK); #ifdef XGE_FEATURE_LRO xgell_lro_flush_sessions(lldev); #endif // LEAVE_FUNCTION return XGE_HAL_OK; } /****************************************** * Function: xge_ring_dtr_get * Parameters: mbuf pointer, channel handler * descriptot, Per adapter xgelldev_t * structure pointer, * Rx private structure * Return: HAL status code * Description: Updates the mbuf lengths * depending on packet lengths. ******************************************/ int xge_ring_dtr_get(mbuf_t mbuf_up, xge_hal_channel_h channelh, xge_hal_dtr_h dtr, xgelldev_t *lldev, xgell_rx_priv_t *rxd_priv) { mbuf_t m; int pkt_length[5]={0,0}, pkt_len=0; dma_addr_t dma_data[5]; int index; m = mbuf_up; pkt_len = 0; if(lldev->buffer_mode != XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { xge_os_memzero(pkt_length, sizeof(pkt_length)); /* * Retrieve data of interest from the completed descriptor -- This * returns the packet length */ if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_5) { xge_hal_ring_dtr_5b_get(channelh, dtr, dma_data, pkt_length); } else { xge_hal_ring_dtr_3b_get(channelh, dtr, dma_data, pkt_length); } for(index = 0; index < lldev->rxd_mbuf_cnt; index++) { m->m_len = pkt_length[index]; if(index < (lldev->rxd_mbuf_cnt-1)) { m->m_next = rxd_priv->bufferArray[index + 1]; m = m->m_next; } else { m->m_next = NULL; } pkt_len+=pkt_length[index]; } /* * Since 2 buffer mode is an exceptional case where data is in 3rd * buffer but not in 2nd buffer */ if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_2) { m->m_len = pkt_length[2]; pkt_len+=pkt_length[2]; } /* * Update length of newly created buffer to be sent up with packet * length */ mbuf_up->m_pkthdr.len = pkt_len; } else { /* * Retrieve data of interest from the completed descriptor -- This * returns the packet length */ xge_hal_ring_dtr_1b_get(channelh, dtr,&dma_data[0], &pkt_length[0]); /* * Update length of newly created buffer to be sent up with packet * length */ mbuf_up->m_len = mbuf_up->m_pkthdr.len = pkt_length[0]; } return XGE_HAL_OK; } /****************************************** * Function: xge_send * Parameters: Pointer to ifnet structure * Return: None * Description: Transmit entry point ******************************************/ void xge_send(struct ifnet *ifnetp) { xgelldev_t *lldev = ifnetp->if_softc; mtx_lock(&lldev->xge_lock); xge_send_locked(ifnetp); mtx_unlock(&lldev->xge_lock); } void xge_send_locked(struct ifnet *ifnetp) { xge_hal_dtr_h dtr; static bus_dma_segment_t segs[MAX_SEGS]; xge_hal_status_e status_code; unsigned int max_fragments; xgelldev_t *lldev = ifnetp->if_softc; xge_hal_channel_h channelh = lldev->fifo_channel_0; mbuf_t m_head = NULL; mbuf_t m_buf = NULL; xgell_tx_priv_t *ll_tx_priv = NULL; register unsigned int count = 0; unsigned int nsegs = 0; u16 vlan_tag; max_fragments = ((xge_hal_fifo_t *)channelh)->config->max_frags; mtx_assert((&lldev->xge_lock), MA_OWNED); /* If device is not initialized, return */ if((!lldev->initialized) || (!(ifnetp->if_drv_flags & IFF_DRV_RUNNING))) { xge_trace(XGE_ERR, "Device is not initialized"); return; } /* * Get the number of free descriptors in the FIFO channel and return if * the count is less than the XGELL_TX_LEVEL_LOW -- the low threshold */ count = xge_hal_channel_dtr_count(channelh); if(count <= XGELL_TX_LEVEL_LOW) { ifnetp->if_drv_flags |= IFF_DRV_OACTIVE; xge_trace(XGE_TRACE, "Free descriptor count %d/%d at low threshold", count, XGELL_TX_LEVEL_LOW); /* Serialized -- through queue */ xge_queue_produce_context(xge_hal_device_queue(lldev->devh), XGE_LL_EVENT_TRY_XMIT_AGAIN, lldev); return; } /* This loop will be executed for each packet in the kernel maintained * queue -- each packet can be with fragments as an mbuf chain */ while((ifnetp->if_snd.ifq_head) && (xge_hal_channel_dtr_count(channelh) > XGELL_TX_LEVEL_LOW)) { IF_DEQUEUE(&ifnetp->if_snd, m_head); for(count = 0, m_buf = m_head; m_buf != NULL; m_buf = m_buf->m_next) { if(m_buf->m_len) { count += 1; } } if(count >= max_fragments) { m_buf = m_defrag(m_head, M_DONTWAIT); if(m_buf != NULL) { m_head = m_buf; } } /* Reserve descriptors */ status_code = xge_hal_fifo_dtr_reserve(channelh, &dtr); if(status_code) { switch(status_code) { case XGE_HAL_INF_CHANNEL_IS_NOT_READY: xge_trace(XGE_ERR, "Channel is not ready"); break; case XGE_HAL_INF_OUT_OF_DESCRIPTORS: xge_trace(XGE_ERR, "Out of descriptors"); break; default: xge_trace(XGE_ERR, "Reserving (Tx) descriptors failed. Status %d", status_code); } goto out2; break; } vlan_tag = (m_head->m_flags & M_VLANTAG) ? m_head->m_pkthdr.ether_vtag : 0; xge_hal_fifo_dtr_vlan_set(dtr, vlan_tag); /* Update Tx private structure for this descriptor */ ll_tx_priv = xge_hal_fifo_dtr_private(dtr); ll_tx_priv->buffer = m_head; /* * Do mapping -- Required DMA tag has been created in xge_init * function and DMA maps have already been created in the * xgell_tx_replenish function. * Returns number of segments through nsegs */ if(bus_dmamap_load_mbuf_sg(lldev->dma_tag_tx, ll_tx_priv->dma_map, m_head, segs, &nsegs, BUS_DMA_NOWAIT)) { xge_trace(XGE_ERR, "DMA map load with segments failed"); goto out2; } /* Set descriptor buffer for header and each fragment/segment */ count = 0; do { xge_hal_fifo_dtr_buffer_set(channelh, dtr, count, (dma_addr_t)htole64(segs[count].ds_addr), segs[count].ds_len); count = count + 1; } while(count < nsegs); /* Pre-write Sync of mapping */ bus_dmamap_sync(lldev->dma_tag_tx, ll_tx_priv->dma_map, BUS_DMASYNC_PREWRITE); #ifdef XGE_FEATURE_TSO if((m_head->m_pkthdr.csum_flags & CSUM_TSO) != 0) { xge_hal_fifo_dtr_mss_set(dtr, m_head->m_pkthdr.tso_segsz); } #endif /* Checksum */ if(ifnetp->if_hwassist > 0) { xge_hal_fifo_dtr_cksum_set_bits(dtr, XGE_HAL_TXD_TX_CKO_IPV4_EN | XGE_HAL_TXD_TX_CKO_TCP_EN | XGE_HAL_TXD_TX_CKO_UDP_EN); } /* Post descriptor to FIFO channel */ xge_hal_fifo_dtr_post(channelh, dtr); /* Send the same copy of mbuf packet to BPF (Berkely Packet Filter) * listener so that we can use tools like tcpdump */ ETHER_BPF_MTAP(ifnetp, m_head); } goto out1; out2: /* Prepend the packet back to queue */ IF_PREPEND(&ifnetp->if_snd, m_head); out1: ifnetp->if_timer = 15; } /****************************************** * Function: xgell_get_buf * Parameters: Per adapter xgelldev_t * structure pointer, descriptor, * Rx private structure, rxd_priv buffer * buffer index for mapping * Return: HAL status code * Description: Gets buffer from system mbuf * buffer pool. ******************************************/ int xgell_get_buf(xge_hal_dtr_h dtrh, xgell_rx_priv_t *rxd_priv, xgelldev_t *lldev, int index) { register mbuf_t mp = NULL; struct ifnet *ifnetp = lldev->ifnetp; int retValue = XGE_HAL_OK; bus_addr_t paddr; int BUFLEN = 0, CLUSTLEN = 0; if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { CLUSTLEN = MJUMPAGESIZE; BUFLEN = MJUMPAGESIZE; } else { BUFLEN = lldev->rxd_mbuf_len[index]; if(BUFLEN < MCLBYTES) { CLUSTLEN = MCLBYTES; } else { CLUSTLEN = MJUMPAGESIZE; } } /* Get mbuf with attached cluster */ mp = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, CLUSTLEN); if(!mp) { xge_trace(XGE_ERR, "Out of memory to allocate mbuf"); retValue = XGE_HAL_FAIL; goto getbuf_out; } /* Update mbuf's length, packet length and receive interface */ mp->m_len = mp->m_pkthdr.len = BUFLEN; mp->m_pkthdr.rcvif = ifnetp; /* Unload DMA map of mbuf in current descriptor */ bus_dmamap_unload(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map); /* Load DMA map */ if(bus_dmamap_load(lldev->dma_tag_rx , rxd_priv->dmainfo[index].dma_map, mtod(mp, void*), mp->m_len, dmamap_cb , &paddr , 0)) { xge_trace(XGE_ERR, "Loading DMA map failed"); m_freem(mp); retValue = XGE_HAL_FAIL; goto getbuf_out; } /* Update descriptor private data */ rxd_priv->bufferArray[index] = mp; rxd_priv->dmainfo[index].dma_phyaddr = htole64(paddr); /* Pre-Read/Write sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map, BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); /* Set descriptor buffer */ if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { xge_hal_ring_dtr_1b_set(dtrh, rxd_priv->dmainfo[0].dma_phyaddr, MJUMPAGESIZE); } getbuf_out: return retValue; } /****************************************** * Function: xgell_get_buf_3b_5b * Parameters: Per adapter xgelldev_t * structure pointer, descriptor, * Rx private structure * Return: HAL status code * Description: Gets buffers from system mbuf * buffer pool. ******************************************/ int xgell_get_buf_3b_5b(xge_hal_dtr_h dtrh, xgell_rx_priv_t *rxd_priv, xgelldev_t *lldev) { bus_addr_t dma_pointers[5]; int dma_sizes[5]; int retValue = XGE_HAL_OK, index; int newindex = 0; for(index = 0; index < lldev->rxd_mbuf_cnt; index++) { retValue = xgell_get_buf(dtrh, rxd_priv, lldev, index); if(retValue != XGE_HAL_OK) { for(newindex = 0; newindex < index; newindex++) { m_freem(rxd_priv->bufferArray[newindex]); } return retValue; } } for(index = 0; index < lldev->buffer_mode; index++) { if(lldev->rxd_mbuf_len[index] != 0) { dma_pointers[index] = rxd_priv->dmainfo[index].dma_phyaddr; dma_sizes[index] = lldev->rxd_mbuf_len[index]; } else { dma_pointers[index] = rxd_priv->dmainfo[index-1].dma_phyaddr; dma_sizes[index] = 1; } } /* Assigning second buffer to third pointer in 2 buffer mode */ if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_2) { dma_pointers[2] = dma_pointers[1]; dma_sizes[2] = dma_sizes[1]; dma_sizes[1] = 1; } if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_5) { xge_hal_ring_dtr_5b_set(dtrh, dma_pointers, dma_sizes); } else { xge_hal_ring_dtr_3b_set(dtrh, dma_pointers, dma_sizes); } return retValue; } /****************************************** * Function: xgell_tx_compl * Parameters: Channel handle, descriptor, * transfer code, * userdata -> per adapter * xgelldev_t structure as void * * Return: HAL status code * Description: If an interrupt was raised * to indicate DMA complete of * the Tx packet, this function * is called. It identifies the * last TxD whose buffer was * freed and frees all skbs * whose data have already DMA'ed * into the NICs internal memory. ******************************************/ xge_hal_status_e xgell_tx_compl(xge_hal_channel_h channelh, xge_hal_dtr_h dtr, u8 t_code, void *userdata) { xgell_tx_priv_t *ll_tx_priv; mbuf_t m_buffer; xgelldev_t *lldev = (xgelldev_t *)userdata; struct ifnet *ifnetp = lldev->ifnetp; ifnetp->if_timer = 0; /* For each completed descriptor: Get private structure, free buffer, * do unmapping, and free descriptor */ do { if(t_code) { xge_trace(XGE_TRACE, "t_code %d", t_code); xge_hal_device_handle_tcode(channelh, dtr, t_code); } ll_tx_priv = xge_hal_fifo_dtr_private(dtr); m_buffer = ll_tx_priv->buffer; bus_dmamap_unload(lldev->dma_tag_tx, ll_tx_priv->dma_map); m_freem(m_buffer); ll_tx_priv->buffer = NULL; xge_hal_fifo_dtr_free(channelh, dtr); } while(xge_hal_fifo_dtr_next_completed(channelh, &dtr, &t_code) == XGE_HAL_OK); ifnetp->if_drv_flags &= ~IFF_DRV_OACTIVE; return XGE_HAL_OK; } /****************************************** * Function: xgell_tx_initial_replenish * Parameters: Channel handle, descriptor, * index (not used), userdata * (not used), channel * open/close/reopen option. * Return: HAL status code * Description: Creates DMA maps to be used * for Tx ******************************************/ xge_hal_status_e xgell_tx_initial_replenish(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, int index, void *userdata, xge_hal_channel_reopen_e reopen) { xgell_tx_priv_t *txd_priv = NULL; int retValue = XGE_HAL_OK; device_t dev = NULL; /* Get the user data portion from channel handle */ xgelldev_t *lldev = xge_hal_channel_userdata(channelh); if(lldev == NULL) { xge_trace(XGE_ERR, "Failed to get user data"); retValue = XGE_HAL_FAIL; goto txinit_out; } dev = lldev->device; /* Get the private data */ txd_priv = (xgell_tx_priv_t *) xge_hal_fifo_dtr_private(dtrh); if(txd_priv == NULL) { xge_trace(XGE_ERR, "Failed to get descriptor private data"); retValue = XGE_HAL_FAIL; goto txinit_out; } /* Create DMA map for this descriptor */ if(bus_dmamap_create(lldev->dma_tag_tx, BUS_DMA_NOWAIT, &txd_priv->dma_map)) { xge_trace(XGE_ERR, "DMA map creation for Tx descriptor failed"); retValue = XGE_HAL_FAIL; goto txinit_out; } txinit_out: return retValue; } /****************************************** * Function: xgell_rx_initial_replenish * Parameters: Channel handle, descriptor, * ring index, userdata * (not used), channel * open/close/reopen option. * Return: HAL status code * Description: Replenish descriptor with * rx_buffer in Rx buffer pool. ******************************************/ xge_hal_status_e xgell_rx_initial_replenish(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, int index, void *userdata, xge_hal_channel_reopen_e reopen) { xgell_rx_priv_t *rxd_priv = NULL; int retValue = XGE_HAL_OK; struct ifnet *ifnetp; device_t dev; int index1, index2; /* Get the user data portion from channel handle */ xgelldev_t *lldev = xge_hal_channel_userdata(channelh); if(lldev == NULL) { xge_ctrace(XGE_ERR, "xgeX: %s: Failed to get user data", __FUNCTION__); retValue = XGE_HAL_FAIL; goto rxinit_out; } dev = lldev->device; /* Get the private data */ rxd_priv = (xgell_rx_priv_t *) xge_hal_ring_dtr_private(channelh, dtrh); if(rxd_priv == NULL) { xge_trace(XGE_ERR, "Failed to get descriptor private data"); retValue = XGE_HAL_FAIL; goto rxinit_out; } rxd_priv->bufferArray = malloc(((sizeof(rxd_priv->bufferArray)) * (lldev->rxd_mbuf_cnt)), M_DEVBUF, M_NOWAIT); if(rxd_priv->bufferArray == NULL) { xge_trace(XGE_ERR, "Failed to allocate buffers for Rxd private structure"); retValue = XGE_HAL_FAIL; goto rxinit_out; } ifnetp = lldev->ifnetp; if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { /* Create DMA map for these descriptors*/ if(bus_dmamap_create(lldev->dma_tag_rx , BUS_DMA_NOWAIT, &rxd_priv->dmainfo[0].dma_map)) { xge_trace(XGE_ERR, "DMA map creation for Rx descriptor failed"); retValue = XGE_HAL_FAIL; goto rxinit_err_out; } /* Get a buffer, attach it to this descriptor */ retValue = xgell_get_buf(dtrh, rxd_priv, lldev, 0); } else { for(index1 = 0; index1 < lldev->rxd_mbuf_cnt; index1++) { /* Create DMA map for this descriptor */ if(bus_dmamap_create(lldev->dma_tag_rx , BUS_DMA_NOWAIT , &rxd_priv->dmainfo[index1].dma_map)) { xge_trace(XGE_ERR, "Jumbo DMA map creation for Rx descriptor failed"); for(index2 = index1 - 1; index2 >= 0; index2--) { bus_dmamap_destroy(lldev->dma_tag_rx, rxd_priv->dmainfo[index2].dma_map); } retValue = XGE_HAL_FAIL; goto rxinit_err_out; } } retValue = xgell_get_buf_3b_5b(dtrh, rxd_priv, lldev); } if(retValue != XGE_HAL_OK) { for(index1 = 0; index1 < lldev->rxd_mbuf_cnt; index1++) { bus_dmamap_destroy(lldev->dma_tag_rx, rxd_priv->dmainfo[index1].dma_map); } goto rxinit_err_out; } else { goto rxinit_out; } rxinit_err_out: free(rxd_priv->bufferArray,M_DEVBUF); rxinit_out: return retValue; } /****************************************** * Function: xgell_rx_term * Parameters: Channel handle, descriptor, * descriptor state, userdata * (not used), channel * open/close/reopen option. * Return: None * Description: Called by HAL to terminate * all DTRs for ring channels. ******************************************/ void xgell_rx_term(xge_hal_channel_h channelh, xge_hal_dtr_h dtrh, xge_hal_dtr_state_e state, void *userdata, xge_hal_channel_reopen_e reopen) { xgell_rx_priv_t *rxd_priv; xgelldev_t *lldev; struct ifnet *ifnetp; device_t dev; int index; // ENTER_FUNCTION /* Descriptor state is not "Posted" */ if(state != XGE_HAL_DTR_STATE_POSTED) { xge_ctrace(XGE_ERR, "xgeX: %s: Descriptor not posted\n", __FUNCTION__); goto rxterm_out; } /* Get the user data portion */ lldev = xge_hal_channel_userdata(channelh); dev = lldev->device; ifnetp = lldev->ifnetp; /* Get the private data */ rxd_priv = (xgell_rx_priv_t *) xge_hal_ring_dtr_private(channelh, dtrh); if(lldev->buffer_mode == XGE_HAL_RING_QUEUE_BUFFER_MODE_1) { /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map, BUS_DMASYNC_POSTREAD); /* Do unmapping and destory DMA map */ bus_dmamap_unload(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map); m_freem(rxd_priv->bufferArray[0]); bus_dmamap_destroy(lldev->dma_tag_rx, rxd_priv->dmainfo[0].dma_map); } else { for(index = 0; index < lldev->rxd_mbuf_cnt; index++) { /* Post-Read sync */ bus_dmamap_sync(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map, BUS_DMASYNC_POSTREAD); /* Do unmapping and destory DMA map */ bus_dmamap_unload(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map); bus_dmamap_destroy(lldev->dma_tag_rx, rxd_priv->dmainfo[index].dma_map); /* Free the buffer */ m_free(rxd_priv->bufferArray[index]); } } free(rxd_priv->bufferArray,M_DEVBUF); /* Free the descriptor */ xge_hal_ring_dtr_free(channelh, dtrh); rxterm_out: // LEAVE_FUNCTION return; } /****************************************** * Function: xgell_tx_term * Parameters: Channel handle, descriptor, * descriptor state, userdata * (not used), channel * open/close/reopen option. * Return: None * Description: Called by HAL to terminate * all DTRs for fifo channels. ******************************************/ void xgell_tx_term(xge_hal_channel_h channelh, xge_hal_dtr_h dtr, xge_hal_dtr_state_e state, void *userdata, xge_hal_channel_reopen_e reopen) { xgell_tx_priv_t *ll_tx_priv = xge_hal_fifo_dtr_private(dtr); xgelldev_t *lldev = (xgelldev_t *)userdata; // ENTER_FUNCTION /* Destroy DMA map */ bus_dmamap_destroy(lldev->dma_tag_tx, ll_tx_priv->dma_map); // LEAVE_FUNCTION } /****************************************** * xge_methods * * FreeBSD device interface entry points ******************************************/ static device_method_t xge_methods[] = { DEVMETHOD(device_probe, xge_probe), DEVMETHOD(device_attach, xge_attach), DEVMETHOD(device_detach, xge_detach), DEVMETHOD(device_shutdown, xge_shutdown), {0, 0} }; static driver_t xge_driver = { "nxge", xge_methods, sizeof(xgelldev_t), }; static devclass_t xge_devclass; DRIVER_MODULE(nxge, pci, xge_driver, xge_devclass, 0, 0);