/* * Copyright (C) 2005 - 2008 ServerEngines * All rights reserved. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version 2 * as published by the Free Software Foundation. The full GNU General * Public License is included in this distribution in the file called COPYING. * * Contact Information: * linux-drivers@serverengines.com * * ServerEngines * 209 N. Fair Oaks Ave * Sunnyvale, CA 94085 */ #include #include "hwlib.h" #include "bestatus.h" /* *--------------------------------------------------------- * Function: be_eth_sq_create_ex * Creates an ethernet send ring - extended version with * additional parameters. * pfob - * rd - ring address * length_in_bytes - * type - The type of ring to create. * ulp - The requested ULP number for the ring. * This should be zero based, i.e. 0,1,2. This must * be valid NIC ULP based on the firmware config. * All doorbells for this ring must be sent to * this ULP. The first network ring allocated for * each ULP are higher performance than subsequent rings. * cq_object - cq object for completions * ex_parameters - Additional parameters (that may increase in * future revisions). These parameters are only used * for certain ring types -- see * struct be_eth_sq_parameters for details. * eth_sq - * return status - BE_SUCCESS (0) on success. Negative error code on failure. *--------------------------------------------------------- */ int be_eth_sq_create_ex(struct be_function_object *pfob, struct ring_desc *rd, u32 length, u32 type, u32 ulp, struct be_cq_object *cq_object, struct be_eth_sq_parameters *ex_parameters, struct be_ethsq_object *eth_sq) { struct FWCMD_COMMON_ETH_TX_CREATE *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; u32 n; unsigned long irql; ASSERT(rd); ASSERT(eth_sq); ASSERT(ex_parameters); spin_lock_irqsave(&pfob->post_lock, irql); memset(eth_sq, 0, sizeof(*eth_sq)); eth_sq->parent_function = pfob; eth_sq->bid = 0xFFFFFFFF; eth_sq->cq_object = cq_object; /* Translate hwlib interface to arm interface. */ switch (type) { case BE_ETH_TX_RING_TYPE_FORWARDING: type = ETH_TX_RING_TYPE_FORWARDING; break; case BE_ETH_TX_RING_TYPE_STANDARD: type = ETH_TX_RING_TYPE_STANDARD; break; case BE_ETH_TX_RING_TYPE_BOUND: ASSERT(ex_parameters->port < 2); type = ETH_TX_RING_TYPE_BOUND; break; default: TRACE(DL_ERR, "Invalid eth tx ring type:%d", type); return BE_NOT_OK; break; } wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { ASSERT(wrb); TRACE(DL_ERR, "No free MCC WRBs in create EQ."); status = BE_STATUS_NO_MCC_WRB; goto Error; } /* NIC must be supported by the current config. */ ASSERT(pfob->fw_config.nic_ulp_mask); /* * The ulp parameter must select a valid NIC ULP * for the current config. */ ASSERT((1 << ulp) & pfob->fw_config.nic_ulp_mask); /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_ETH_TX_CREATE); fwcmd->header.request.port_number = ex_parameters->port; AMAP_SET_BITS_PTR(ETX_CONTEXT, pd_id, &fwcmd->params.request.context, 0); n = be_ring_length_to_encoding(length, sizeof(struct ETH_WRB_AMAP)); AMAP_SET_BITS_PTR(ETX_CONTEXT, tx_ring_size, &fwcmd->params.request.context, n); AMAP_SET_BITS_PTR(ETX_CONTEXT, cq_id_send, &fwcmd->params.request.context, cq_object->cq_id); n = pfob->pci_function_number; AMAP_SET_BITS_PTR(ETX_CONTEXT, func, &fwcmd->params.request.context, n); fwcmd->params.request.type = type; fwcmd->params.request.ulp_num = (1 << ulp); fwcmd->params.request.num_pages = DIV_ROUND_UP(length, PAGE_SIZE); ASSERT(PAGES_SPANNED(rd->va, rd->length) >= fwcmd->params.request.num_pages); /* Create a page list for the FWCMD. */ be_rd_to_pa_list(rd, fwcmd->params.request.pages, ARRAY_SIZE(fwcmd->params.request.pages)); status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != BE_SUCCESS) { TRACE(DL_ERR, "MCC to create etx queue failed."); goto Error; } /* save the butler ID */ eth_sq->bid = fwcmd->params.response.cid; /* add a reference to the corresponding CQ */ atomic_inc(&cq_object->ref_count); Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine destroys an ethernet send queue EthSq - EthSq Handle returned from EthSqCreate This function always return BE_SUCCESS. This function frees memory allocated by EthSqCreate for the EthSq Object. */ int be_eth_sq_destroy(struct be_ethsq_object *eth_sq) { int status = 0; /* Send fwcmd to destroy the queue. */ status = be_function_ring_destroy(eth_sq->parent_function, eth_sq->bid, FWCMD_RING_TYPE_ETH_TX, NULL, NULL, NULL, NULL); ASSERT(status == 0); /* Derefence any associated CQs. */ atomic_dec(ð_sq->cq_object->ref_count); return status; } /* This routine attempts to set the transmit flow control parameters. FunctionObject - Handle to a function object txfc_enable - transmit flow control enable - true for enable, false for disable rxfc_enable - receive flow control enable - true for enable, false for disable Returns BE_SUCCESS if successfull, otherwise a useful int error code is returned. IRQL: < DISPATCH_LEVEL This function always fails in non-privileged machine context. */ int be_eth_set_flow_control(struct be_function_object *pfob, bool txfc_enable, bool rxfc_enable) { struct FWCMD_COMMON_SET_FLOW_CONTROL *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_SET_FLOW_CONTROL); fwcmd->params.request.rx_flow_control = rxfc_enable; fwcmd->params.request.tx_flow_control = txfc_enable; /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "set flow control fwcmd failed."); goto error; } error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine attempts to get the transmit flow control parameters. pfob - Handle to a function object txfc_enable - transmit flow control enable - true for enable, false for disable rxfc_enable - receive flow control enable - true for enable, false for disable Returns BE_SUCCESS if successfull, otherwise a useful int error code is returned. IRQL: < DISPATCH_LEVEL This function always fails in non-privileged machine context. */ int be_eth_get_flow_control(struct be_function_object *pfob, bool *txfc_enable, bool *rxfc_enable) { struct FWCMD_COMMON_GET_FLOW_CONTROL *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_GET_FLOW_CONTROL); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "get flow control fwcmd failed."); goto error; } *txfc_enable = fwcmd->params.response.tx_flow_control; *rxfc_enable = fwcmd->params.response.rx_flow_control; error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* *--------------------------------------------------------- * Function: be_eth_set_qos * This function sets the ethernet transmit Quality of Service (QoS) * characteristics of BladeEngine for the domain. All ethernet * transmit rings of the domain will evenly share the bandwidth. * The exeception to sharing is the host primary (super) ethernet * transmit ring as well as the host ethernet forwarding ring * for missed offload data. * pfob - * max_bps - the maximum bits per second in units of * 10 Mbps (valid 0-100) * max_pps - the maximum packets per second in units * of 1 Kpps (0 indicates no limit) * return status - BE_SUCCESS (0) on success. Negative error code on failure. *--------------------------------------------------------- */ int be_eth_set_qos(struct be_function_object *pfob, u32 max_bps, u32 max_pps) { struct FWCMD_COMMON_SET_QOS *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_SET_QOS); /* Set fields in fwcmd */ fwcmd->params.request.max_bits_per_second_NIC = max_bps; fwcmd->params.request.max_packets_per_second_NIC = max_pps; fwcmd->params.request.valid_flags = QOS_BITS_NIC | QOS_PKTS_NIC; /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) TRACE(DL_ERR, "network set qos fwcmd failed."); error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* *--------------------------------------------------------- * Function: be_eth_get_qos * This function retrieves the ethernet transmit Quality of Service (QoS) * characteristics for the domain. * max_bps - the maximum bits per second in units of * 10 Mbps (valid 0-100) * max_pps - the maximum packets per second in units of * 1 Kpps (0 indicates no limit) * return status - BE_SUCCESS (0) on success. Negative error code on failure. *--------------------------------------------------------- */ int be_eth_get_qos(struct be_function_object *pfob, u32 *max_bps, u32 *max_pps) { struct FWCMD_COMMON_GET_QOS *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_GET_QOS); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "network get qos fwcmd failed."); goto error; } *max_bps = fwcmd->params.response.max_bits_per_second_NIC; *max_pps = fwcmd->params.response.max_packets_per_second_NIC; error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* *--------------------------------------------------------- * Function: be_eth_set_frame_size * This function sets the ethernet maximum frame size. The previous * values are returned. * pfob - * tx_frame_size - maximum transmit frame size in bytes * rx_frame_size - maximum receive frame size in bytes * return status - BE_SUCCESS (0) on success. Negative error code on failure. *--------------------------------------------------------- */ int be_eth_set_frame_size(struct be_function_object *pfob, u32 *tx_frame_size, u32 *rx_frame_size) { struct FWCMD_COMMON_SET_FRAME_SIZE *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_SET_FRAME_SIZE); fwcmd->params.request.max_tx_frame_size = *tx_frame_size; fwcmd->params.request.max_rx_frame_size = *rx_frame_size; /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "network set frame size fwcmd failed."); goto error; } *tx_frame_size = fwcmd->params.response.chip_max_tx_frame_size; *rx_frame_size = fwcmd->params.response.chip_max_rx_frame_size; error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine creates a Ethernet receive ring. pfob - handle to a function object rq_base_va - base VA for the default receive ring. this must be exactly 8K in length and continguous physical memory. cq_object - handle to a previously created CQ to be associated with the RQ. pp_eth_rq - pointer to an opqaue handle where an eth receive object is returned. Returns BE_SUCCESS if successfull, , otherwise a useful int error code is returned. IRQL: < DISPATCH_LEVEL this function allocates a struct be_ethrq_object *object. there must be no more than 1 of these per function object, unless the function object supports RSS (is networking and on the host). the rq_base_va must point to a buffer of exactly 8K. the erx::host_cqid (or host_stor_cqid) register and erx::ring_page registers will be updated as appropriate on return */ int be_eth_rq_create(struct be_function_object *pfob, struct ring_desc *rd, struct be_cq_object *cq_object, struct be_cq_object *bcmc_cq_object, struct be_ethrq_object *eth_rq) { int status = 0; struct MCC_WRB_AMAP *wrb = NULL; struct FWCMD_COMMON_ETH_RX_CREATE *fwcmd = NULL; unsigned long irql; /* MPU will set the */ ASSERT(rd); ASSERT(eth_rq); spin_lock_irqsave(&pfob->post_lock, irql); eth_rq->parent_function = pfob; eth_rq->cq_object = cq_object; wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto Error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_ETH_RX_CREATE); fwcmd->params.request.num_pages = 2; /* required length */ fwcmd->params.request.cq_id = cq_object->cq_id; if (bcmc_cq_object) fwcmd->params.request.bcmc_cq_id = bcmc_cq_object->cq_id; else fwcmd->params.request.bcmc_cq_id = 0xFFFF; /* Create a page list for the FWCMD. */ be_rd_to_pa_list(rd, fwcmd->params.request.pages, ARRAY_SIZE(fwcmd->params.request.pages)); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != BE_SUCCESS) { TRACE(DL_ERR, "fwcmd to map eth rxq frags failed."); goto Error; } /* Save the ring ID for cleanup. */ eth_rq->rid = fwcmd->params.response.id; atomic_inc(&cq_object->ref_count); Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine destroys an Ethernet receive queue eth_rq - ethernet receive queue handle returned from eth_rq_create Returns BE_SUCCESS on success and an appropriate int on failure. This function frees resourcs allocated by EthRqCreate. The erx::host_cqid (or host_stor_cqid) register and erx::ring_page registers will be updated as appropriate on return IRQL: < DISPATCH_LEVEL */ static void be_eth_rq_destroy_internal_cb(void *context, int status, struct MCC_WRB_AMAP *wrb) { struct be_ethrq_object *eth_rq = (struct be_ethrq_object *) context; if (status != BE_SUCCESS) { TRACE(DL_ERR, "Destroy eth rq failed in internal callback.\n"); } else { /* Dereference any CQs associated with this queue. */ atomic_dec(ð_rq->cq_object->ref_count); } return; } int be_eth_rq_destroy(struct be_ethrq_object *eth_rq) { int status = BE_SUCCESS; /* Send fwcmd to destroy the RQ. */ status = be_function_ring_destroy(eth_rq->parent_function, eth_rq->rid, FWCMD_RING_TYPE_ETH_RX, NULL, NULL, be_eth_rq_destroy_internal_cb, eth_rq); return status; } /* *--------------------------------------------------------------------------- * Function: be_eth_rq_destroy_options * Destroys an ethernet receive ring with finer granularity options * than the standard be_eth_rq_destroy() API function. * eth_rq - * flush - Set to 1 to flush the ring, set to 0 to bypass the flush * cb - Callback function on completion * cb_context - Callback context * return status - BE_SUCCESS (0) on success. Negative error code on failure. *---------------------------------------------------------------------------- */ int be_eth_rq_destroy_options(struct be_ethrq_object *eth_rq, bool flush, mcc_wrb_cqe_callback cb, void *cb_context) { struct FWCMD_COMMON_RING_DESTROY *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = BE_SUCCESS; struct be_function_object *pfob = NULL; unsigned long irql; pfob = eth_rq->parent_function; spin_lock_irqsave(&pfob->post_lock, irql); TRACE(DL_INFO, "Destroy eth_rq ring id:%d, flush:%d", eth_rq->rid, flush); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { ASSERT(wrb); TRACE(DL_ERR, "No free MCC WRBs in destroy eth_rq ring."); status = BE_STATUS_NO_MCC_WRB; goto Error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_RING_DESTROY); fwcmd->params.request.id = eth_rq->rid; fwcmd->params.request.ring_type = FWCMD_RING_TYPE_ETH_RX; fwcmd->params.request.bypass_flush = ((0 == flush) ? 1 : 0); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, cb, cb_context, be_eth_rq_destroy_internal_cb, eth_rq, fwcmd, NULL); if (status != BE_SUCCESS && status != BE_PENDING) { TRACE(DL_ERR, "eth_rq ring destroy failed. id:%d, flush:%d", eth_rq->rid, flush); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine queries the frag size for erx. pfob - handle to a function object frag_size_bytes - erx frag size in bytes that is/was set. Returns BE_SUCCESS if successfull, otherwise a useful int error code is returned. IRQL: < DISPATCH_LEVEL */ int be_eth_rq_get_frag_size(struct be_function_object *pfob, u32 *frag_size_bytes) { struct FWCMD_ETH_GET_RX_FRAG_SIZE *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; ASSERT(frag_size_bytes); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); return BE_STATUS_NO_MCC_WRB; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, ETH_GET_RX_FRAG_SIZE); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "get frag size fwcmd failed."); goto error; } *frag_size_bytes = 1 << fwcmd->params.response.actual_fragsize_log2; error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine attempts to set the frag size for erx. If the frag size is already set, the attempt fails and the current frag size is returned. pfob - Handle to a function object frag_size - Erx frag size in bytes that is/was set. current_frag_size_bytes - Pointer to location where currrent frag is to be rturned Returns BE_SUCCESS if successfull, otherwise a useful int error code is returned. IRQL: < DISPATCH_LEVEL This function always fails in non-privileged machine context. */ int be_eth_rq_set_frag_size(struct be_function_object *pfob, u32 frag_size, u32 *frag_size_bytes) { struct FWCMD_ETH_SET_RX_FRAG_SIZE *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; unsigned long irql; ASSERT(frag_size_bytes); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto error; } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, ETH_SET_RX_FRAG_SIZE); ASSERT(frag_size >= 128 && frag_size <= 16 * 1024); /* This is the log2 of the fragsize. This is not the exact * ERX encoding. */ fwcmd->params.request.new_fragsize_log2 = __ilog2_u32(frag_size); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, NULL, NULL, NULL, NULL, fwcmd, NULL); if (status != 0) { TRACE(DL_ERR, "set frag size fwcmd failed."); goto error; } *frag_size_bytes = 1 << fwcmd->params.response.actual_fragsize_log2; error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine gets or sets a mac address for a domain given the port and mac. FunctionObject - Function object handle. port1 - Set to TRUE if this function will set/get the Port 1 address. Only the host may set this to TRUE. mac1 - Set to TRUE if this function will set/get the MAC 1 address. Only the host may set this to TRUE. write - Set to TRUE if this function should write the mac address. mac_address - Buffer of the mac address to read or write. Returns BE_SUCCESS if successfull, otherwise a useful int is returned. IRQL: < DISPATCH_LEVEL */ int be_rxf_mac_address_read_write(struct be_function_object *pfob, bool port1, /* VM must always set to false */ bool mac1, /* VM must always set to false */ bool mgmt, bool write, bool permanent, u8 *mac_address, mcc_wrb_cqe_callback cb, /* optional */ void *cb_context) /* optional */ { int status = BE_SUCCESS; union { struct FWCMD_COMMON_NTWK_MAC_QUERY *query; struct FWCMD_COMMON_NTWK_MAC_SET *set; } fwcmd = {NULL}; struct MCC_WRB_AMAP *wrb = NULL; u32 type = 0; unsigned long irql; struct be_mcc_wrb_response_copy rc; spin_lock_irqsave(&pfob->post_lock, irql); ASSERT(mac_address); ASSERT(port1 == false); ASSERT(mac1 == false); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { TRACE(DL_ERR, "MCC wrb peek failed."); status = BE_STATUS_NO_MCC_WRB; goto Error; } if (mgmt) { type = MAC_ADDRESS_TYPE_MANAGEMENT; } else { if (pfob->type == BE_FUNCTION_TYPE_NETWORK) type = MAC_ADDRESS_TYPE_NETWORK; else type = MAC_ADDRESS_TYPE_STORAGE; } if (write) { /* Prepares an embedded fwcmd, including * request/response sizes. */ fwcmd.set = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_MAC_SET); fwcmd.set->params.request.invalidate = 0; fwcmd.set->params.request.mac1 = (mac1 ? 1 : 0); fwcmd.set->params.request.port = (port1 ? 1 : 0); fwcmd.set->params.request.type = type; /* Copy the mac address to set. */ fwcmd.set->params.request.mac.SizeOfStructure = sizeof(fwcmd.set->params.request.mac); memcpy(fwcmd.set->params.request.mac.MACAddress, mac_address, ETH_ALEN); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, cb, cb_context, NULL, NULL, fwcmd.set, NULL); } else { /* * Prepares an embedded fwcmd, including * request/response sizes. */ fwcmd.query = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_MAC_QUERY); fwcmd.query->params.request.mac1 = (mac1 ? 1 : 0); fwcmd.query->params.request.port = (port1 ? 1 : 0); fwcmd.query->params.request.type = type; fwcmd.query->params.request.permanent = permanent; rc.length = FIELD_SIZEOF(struct FWCMD_COMMON_NTWK_MAC_QUERY, params.response.mac.MACAddress); rc.fwcmd_offset = offsetof(struct FWCMD_COMMON_NTWK_MAC_QUERY, params.response.mac.MACAddress); rc.va = mac_address; /* Post the f/w command (with a copy for the response) */ status = be_function_post_mcc_wrb(pfob, wrb, NULL, cb, cb_context, NULL, NULL, fwcmd.query, &rc); } if (status < 0) { TRACE(DL_ERR, "mac set/query failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine writes data to context memory. pfob - Function object handle. mac_table - Set to the 128-bit multicast address hash table. Returns BE_SUCCESS if successfull, otherwise a useful int is returned. IRQL: < DISPATCH_LEVEL */ int be_rxf_multicast_config(struct be_function_object *pfob, bool promiscuous, u32 num, u8 *mac_table, mcc_wrb_cqe_callback cb, /* optional */ void *cb_context, struct be_multicast_q_ctxt *q_ctxt) { int status = BE_SUCCESS; struct FWCMD_COMMON_NTWK_MULTICAST_SET *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; ASSERT(num <= ARRAY_SIZE(fwcmd->params.request.mac)); if (num > ARRAY_SIZE(fwcmd->params.request.mac)) { TRACE(DL_ERR, "Too many multicast addresses. BE supports %d.", (int) ARRAY_SIZE(fwcmd->params.request.mac)); return BE_NOT_OK; } spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_MULTICAST_SET); fwcmd->params.request.promiscuous = promiscuous; if (!promiscuous) { fwcmd->params.request.num_mac = num; if (num > 0) { ASSERT(mac_table); memcpy(fwcmd->params.request.mac, mac_table, ETH_ALEN * num); } } /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, fwcmd, NULL); if (status < 0) { TRACE(DL_ERR, "multicast fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* This routine adds or removes a vlan tag from the rxf table. FunctionObject - Function object handle. VLanTag - VLan tag to add or remove. Add - Set to TRUE if this will add a vlan tag Returns BE_SUCCESS if successfull, otherwise a useful int is returned. IRQL: < DISPATCH_LEVEL */ int be_rxf_vlan_config(struct be_function_object *pfob, bool promiscuous, u32 num, u16 *vlan_tag_array, mcc_wrb_cqe_callback cb, /* optional */ void *cb_context, struct be_vlan_q_ctxt *q_ctxt) /* optional */ { int status = BE_SUCCESS; struct FWCMD_COMMON_NTWK_VLAN_CONFIG *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; if (num > ARRAY_SIZE(fwcmd->params.request.vlan_tag)) { TRACE(DL_ERR, "Too many VLAN tags."); return BE_NOT_OK; } spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_VLAN_CONFIG); fwcmd->params.request.promiscuous = promiscuous; if (!promiscuous) { fwcmd->params.request.num_vlan = num; if (num > 0) { ASSERT(vlan_tag_array); memcpy(fwcmd->params.request.vlan_tag, vlan_tag_array, num * sizeof(vlan_tag_array[0])); } } /* Post the commadn */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, fwcmd, NULL); if (status < 0) { TRACE(DL_ERR, "vlan fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } int be_rxf_link_status(struct be_function_object *pfob, struct BE_LINK_STATUS *link_status, mcc_wrb_cqe_callback cb, void *cb_context, struct be_link_status_q_ctxt *q_ctxt) { struct FWCMD_COMMON_NTWK_LINK_STATUS_QUERY *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; struct be_mcc_wrb_response_copy rc; ASSERT(link_status); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_LINK_STATUS_QUERY); rc.length = FIELD_SIZEOF(struct FWCMD_COMMON_NTWK_LINK_STATUS_QUERY, params.response); rc.fwcmd_offset = offsetof(struct FWCMD_COMMON_NTWK_LINK_STATUS_QUERY, params.response); rc.va = link_status; /* Post or queue the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, fwcmd, &rc); if (status < 0) { TRACE(DL_ERR, "link status fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } int be_rxf_query_eth_statistics(struct be_function_object *pfob, struct FWCMD_ETH_GET_STATISTICS *va_for_fwcmd, u64 pa_for_fwcmd, mcc_wrb_cqe_callback cb, void *cb_context, struct be_nonembedded_q_ctxt *q_ctxt) { struct MCC_WRB_AMAP *wrb = NULL; int status = 0; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; ASSERT(va_for_fwcmd); ASSERT(pa_for_fwcmd); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } TRACE(DL_INFO, "Query eth stats. fwcmd va:%p pa:0x%08x_%08x", va_for_fwcmd, upper_32_bits(pa_for_fwcmd), (u32)pa_for_fwcmd); /* Prepares an embedded fwcmd, including request/response sizes. */ va_for_fwcmd = BE_PREPARE_NONEMBEDDED_FWCMD(pfob, wrb, va_for_fwcmd, pa_for_fwcmd, ETH_GET_STATISTICS); /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, va_for_fwcmd, NULL); if (status < 0) { TRACE(DL_ERR, "eth stats fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } int be_rxf_promiscuous(struct be_function_object *pfob, bool enable_port0, bool enable_port1, mcc_wrb_cqe_callback cb, void *cb_context, struct be_promiscuous_q_ctxt *q_ctxt) { struct FWCMD_ETH_PROMISCUOUS *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, ETH_PROMISCUOUS); fwcmd->params.request.port0_promiscuous = enable_port0; fwcmd->params.request.port1_promiscuous = enable_port1; /* Post the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, fwcmd, NULL); if (status < 0) { TRACE(DL_ERR, "promiscuous fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; } /* *------------------------------------------------------------------------- * Function: be_rxf_filter_config * Configures BladeEngine ethernet receive filter settings. * pfob - * settings - Pointer to the requested filter settings. * The response from BladeEngine will be placed back * in this structure. * cb - optional * cb_context - optional * q_ctxt - Optional. Pointer to a previously allocated struct. * If the MCC WRB ring is full, this structure is * used to queue the operation. It will be posted * to the MCC ring when space becomes available. All * queued commands will be posted to the ring in * the order they are received. It is always valid * to pass a pointer to a generic * be_generic_q_ctxt. However, the specific * context structs are generally smaller than * the generic struct. * return pend_status - BE_SUCCESS (0) on success. * BE_PENDING (postive value) if the FWCMD * completion is pending. Negative error code on failure. *--------------------------------------------------------------------------- */ int be_rxf_filter_config(struct be_function_object *pfob, struct NTWK_RX_FILTER_SETTINGS *settings, mcc_wrb_cqe_callback cb, void *cb_context, struct be_rxf_filter_q_ctxt *q_ctxt) { struct FWCMD_COMMON_NTWK_RX_FILTER *fwcmd = NULL; struct MCC_WRB_AMAP *wrb = NULL; int status = 0; struct be_generic_q_ctxt *generic_ctxt = NULL; unsigned long irql; struct be_mcc_wrb_response_copy rc; ASSERT(settings); spin_lock_irqsave(&pfob->post_lock, irql); wrb = be_function_peek_mcc_wrb(pfob); if (!wrb) { if (q_ctxt && cb) { wrb = (struct MCC_WRB_AMAP *) &q_ctxt->wrb_header; generic_ctxt = (struct be_generic_q_ctxt *) q_ctxt; generic_ctxt->context.bytes = sizeof(*q_ctxt); } else { status = BE_STATUS_NO_MCC_WRB; goto Error; } } /* Prepares an embedded fwcmd, including request/response sizes. */ fwcmd = BE_PREPARE_EMBEDDED_FWCMD(pfob, wrb, COMMON_NTWK_RX_FILTER); memcpy(&fwcmd->params.request, settings, sizeof(*settings)); rc.length = FIELD_SIZEOF(struct FWCMD_COMMON_NTWK_RX_FILTER, params.response); rc.fwcmd_offset = offsetof(struct FWCMD_COMMON_NTWK_RX_FILTER, params.response); rc.va = settings; /* Post or queue the f/w command */ status = be_function_post_mcc_wrb(pfob, wrb, generic_ctxt, cb, cb_context, NULL, NULL, fwcmd, &rc); if (status < 0) { TRACE(DL_ERR, "RXF/ERX filter config fwcmd failed."); goto Error; } Error: spin_unlock_irqrestore(&pfob->post_lock, irql); if (pfob->pend_queue_driving && pfob->mcc) { pfob->pend_queue_driving = 0; be_drive_mcc_wrb_queue(pfob->mcc); } return status; }