freebsd-nq/sys/dev/nxge/if_nxge.c
Robert Watson 07cb08fd48 Directly initialize nxge's ifaddrp pointer to ifnetp->if_addr rather
than indirecting through ifaddr_byindex, which makes things easier with
respect to virtualized network stacks.

Submitted by:	Marko Zec <zec at icir dot org>
Reviewed by:	Leonid Grossman <Leonid dot Grossman at neterion dot com>
Approved by:	re (kensmith)
2007-07-12 10:03:29 +00:00

3416 lines
103 KiB
C

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