diff options
Diffstat (limited to 'sys/dev/nxge/if_nxge.c')
-rw-r--r-- | sys/dev/nxge/if_nxge.c | 3415 |
1 files changed, 3415 insertions, 0 deletions
diff --git a/sys/dev/nxge/if_nxge.c b/sys/dev/nxge/if_nxge.c new file mode 100644 index 0000000..6687b4b --- /dev/null +++ b/sys/dev/nxge/if_nxge.c @@ -0,0 +1,3415 @@ +/*- + * 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 <dev/nxge/if_nxge.h> +#include <dev/nxge/xge-osdep.h> +#include <net/if_arp.h> +#include <sys/types.h> +#include <net/if.h> +#include <net/if_vlan_var.h> + +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", + (PAGE_SIZE / 32)); + xge_os_printf("Using fifo_max_frags = %d", (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 = ifaddr_byindex(ifnetp->if_index); + 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); |