/* * Copyright (c) 2011-2015, Emulex * 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. * * 3. Neither the name of the copyright holder nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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. * */ /** * @file elxsdkutil device specific API * */ #include #include #include #include #include #include #include #include #include #include #include "elxu_common.h" #include "elxu_device.h" #include "elxu_mgmt.h" #include "elx_pt_ioctl.h" #define MIN(x, y) ((x) < (y) ? (x) : (y)) static void dev_parse_wwn(char *, uint8_t *); static void dev_parse_mac(char *, uint8_t *); static elxu_device_t *device_list; /** * A table of information on device types */ typedef struct device_type_info_s { elxu_device_type_e type; char *name; } device_type_info_t; static device_type_info_t device_type_info[] = { {DEVICE_TYPE_LANCER, "Lancer"}, {DEVICE_TYPE_SKYHAWK, "Skyhawk"}, {DEVICE_TYPE_PRISM, "Prism"}, {DEVICE_TYPE_UNKNOWN, "Unknown"} }; /** * A table of information on protocols */ typedef struct device_protocol_info_s { elxu_device_protocol_e protocol; char *name; } device_protocol_info_t; static device_protocol_info_t device_protocol_info[] = { {DEVICE_PROTOCOL_FC, "FC"}, {DEVICE_PROTOCOL_FCOE, "FCoE"}, {DEVICE_PROTOCOL_ISCSI, "iSCSI"}, {DEVICE_PROTOCOL_NIC, "NIC"}, {DEVICE_PROTOCOL_UNKNOWN, "Unknown"} }; /** * A table mapping PCI Vendor/Device into device type */ typedef struct pci_device_map_s { uint16_t vendor; uint16_t device; elxu_device_type_e type; elxu_device_protocol_e protocol; } pci_device_map_t; pci_device_map_t pci_device_map[] = { {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FC, DEVICE_TYPE_LANCER, DEVICE_PROTOCOL_FC}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_G6, DEVICE_TYPE_LANCER, DEVICE_PROTOCOL_FC}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_PRISM, DEVICE_TYPE_PRISM, DEVICE_PROTOCOL_FC}, {PCI_VENDOR_ID_ATTO, PCI_DEVICE_ID_LANCER_G6_ATTO, DEVICE_TYPE_LANCER, DEVICE_PROTOCOL_FC}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_FCOE, DEVICE_TYPE_LANCER, DEVICE_PROTOCOL_FCOE}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_LANCER_NIC, DEVICE_TYPE_LANCER, DEVICE_PROTOCOL_NIC}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_NIC, DEVICE_TYPE_SKYHAWK, DEVICE_PROTOCOL_NIC}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_FCOE, DEVICE_TYPE_SKYHAWK, DEVICE_PROTOCOL_FCOE}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_ISCSI_TGT, DEVICE_TYPE_SKYHAWK, DEVICE_PROTOCOL_ISCSI}, {PCI_VENDOR_ID_EMULEX, PCI_DEVICE_ID_SKYHAWK_ISCSI_INI, DEVICE_TYPE_SKYHAWK, DEVICE_PROTOCOL_ISCSI}, }; /** * @ingroup dev_mgmt * @brief Create a list of devices * * @par Description * This function creates a global list of devices available to be managed. * * @param argc argc from command line * @param argv argv from command line * * @return Returns nothing. */ void elxu_build_device_list(int argc, char *argv[]) { char *hostname; char *path; int i; device_list = NULL; hostname = NULL; path = NULL; for (i=0; i= (argc-1)) || (path && hostname)) break; if (strcmp(argv[i], "--host") == 0){ hostname = argv[i+1]; } if (strcmp(argv[i], "--path") == 0){ path = argv[i+1]; } } if (hostname != NULL) { /* --host was specified, use user space driver */ device_list = os_build_device_list_uspace(hostname); } else { /* No --host option, direct kernel driver access */ device_list = os_build_device_list(path); } return; } extern void elxu_delete_device_list() { elxu_device_t *current; elxu_device_t *next; int rc; current = device_list; while (current) { next = current->next; /* desc is non-null for uspace socket interface, which will be closed by ocsu_close */ if (current->desc == NULL) { rc = os_remove_device(current); if (rc && (current->fd != -1)) { printf("Failed to remove %s file\n", current->device_file_name); } } free (current->modeldesc); free (current->serialnum); free(current); current = next; } } /** * @ingroup dev_mgmt * @brief Open a device file. * * @par Description * This function first calls the OS-specific function to open the * device file. Then, if the open is successful, the function will * retrieve a variety of information about the device and store it * in the elxu_device_t structure. * * NOTE: This function requires that the global device list has * already been built by elxu_build_device_list. * * @param devname A pointer to a device_name structure. This * structure contains a device index and optionally a * hostname if the device is accessed through the * userspace driver. * * @return Returns a pointer to an elxu_device_t structure on success; * NULL on failure. */ elxu_device_t *elxu_open_device(elxu_device_name_t *devname) { elxu_device_t *device; device = os_open_device(devname); if (device != NULL) { fill_device_details(device); } return device; } void fill_device_details(elxu_device_t *device) { int i; device->type = DEVICE_TYPE_UNKNOWN; for (i=0; ivendor == pci_device_map[i].vendor) && (device->device == pci_device_map[i].device)) { device->type = pci_device_map[i].type; device->protocol = pci_device_map[i].protocol; break; } } if (device->driver == DEVICE_DRIVER_OCS) { device->subsystem_vendor = strtoul(elxu_device_get_value(device, "/ocs/pci_subsystem_vendor"), NULL, 0); device->subsystem_device = strtoul(elxu_device_get_value(device, "/ocs/pci_subsystem_device"), NULL, 0); device->sli_intf = strtoul(elxu_device_get_value(device, "/ocs/sli4_intf_reg"), NULL, 0); device->asic_id = strtoul(elxu_device_get_value(device, "/ocs/asic_id_reg"), NULL, 0); strncpy(device->fwrev, elxu_device_get_value(device, "/ocs/fw_rev"), sizeof(device->fwrev) - 1); strncpy(device->ipl, elxu_device_get_value(device, "/ocs/ipl"), sizeof(device->ipl) - 1); device->modeldesc = strdup(elxu_device_get_value(device, "/ocs/desc")); device->serialnum = strdup(elxu_device_get_value(device, "/ocs/sn")); device->phy_port_num = strtoul(elxu_device_get_value(device, "/ocs/phy_port_num"), NULL, 0); // Format of WWNs is "0x20000090fa020d9e" if (is_fc(device)) { dev_parse_wwn(elxu_device_get_value(device, "/ocs/wwnn"), device->wwnn); dev_parse_wwn(elxu_device_get_value(device, "/ocs/wwpn"), device->wwpn); } // Format of MAC is "00:00:c9:ce:a9:50" if (is_iscsi(device)) dev_parse_mac(elxu_device_get_value(device, "/ocs/mac_address"), device->mac_addr); } } static void dev_parse_wwn(char *input, uint8_t* output) { char buf[] = "0x.."; int i; char *c; if (strlen(input) != 18) { return; } c = &(input[2]); for (i=0; i<8; i++) { buf[2] = *c; ++c; buf[3] = *c; ++c; output[i] = strtoul(buf, NULL, 0); } } /* * Input: String in the form 01:02:03:04:05:06 * Output: Array of sixe uint8_t containing 1, 2, 3, etc. */ static void dev_parse_mac(char *input, uint8_t* output) { char buf[] = "0x.."; int i; char *c; if (strlen(input) != 17) { return; } c = &(input[0]); for (i=0; i<6; i++) { buf[2] = *c; ++c; buf[3] = *c; ++c; output[i] = strtoul(buf, NULL, 0); ++c; } } /** * @ingroup dev_mgmt * @brief Send an ioctl to a device. * * @par Description * This function calls the OS-specific function to send an ioctl * to a device. * * * @param device The elxu device structure of the device. * @param req The ioctl request. * @param arg The ioctl argument. * * @return Returns zero on success; a non-zero error code on * failure. */ int elxu_ioctl_device(elxu_device_t *device, int req, void *arg) { return os_ioctl_device(device, req, arg); } /** * @ingroup dev_mgmt * @brief Close a device. * * @par Description * This function calls the OS-specific function to close a * device file, then frees any allocated memory associated with * that device. * * @param device The elxu device structure of the device. * * @return Returns nothing. */ void elxu_close_device (elxu_device_t *device) { if (device) { os_close_device(device); } } /** * @ingroup dev_mgmt * @brief Return the model description for a device. * * @par Description * This function returns the model description for a device. * The description is placed into a newly allocated string. The * caller is responsible for freeing the string when it is no * longer needed. * * * @param device The elxu device structure of the device. * * @return Returns a pointer to the allocated string containing * the desription. Returns NULL on error. */ char *elxu_get_modeldesc(elxu_device_t *device) { return strdup(device->modeldesc); } /** * @ingroup dev_mgmt * @brief Return the firmware revision for a device. * * @par Description * This function returns the firmware revision for a device. * The firmware revision is placed into a newly allocated * string. The caller is responsible for freeing the string when * it is no longer needed. * * * @param device The elxu device structure of the device. * * @return Returns a pointer to the allocated string containing * the firmware revision. Returns NULL on error. */ char *elxu_get_fwrev(elxu_device_t *device) { return device->fwrev; } /** * @ingroup dev_mgmt * @brief Return the IPL version for a device. * * @par Description * This function returns the IPL version for a device. * The IPL version is placed into a newly allocated * string. * * * @param device The elxu device structure of the device. * * @return Returns a pointer to the allocated string containing * the IPL version. Returns NULL on error. */ char *elxu_get_ipl(elxu_device_t *device) { return device->ipl; } /** * @ingroup dev_mgmt * @brief Return the serial number for a device. * * @par Description * This function returns the serial number for a device. The * serial number is placed into a newly allocated string. The * caller is responsible for freeing the string when it is no * longer needed. * * * @param device The elxu device structure of the device. * * @return Returns a pointer to the allocated string containing * the serial number. Returns NULL on error. */ char *elxu_get_serialnumber(elxu_device_t *device) { return strdup(device->serialnum); } /** * @ingroup dev_mgmt * @brief Return the physical port number for a device. * * @par Description * This function returns the physical port number for a device. The * physical port number is placed into a newly allocated string. The * caller is responsible for freeing the string when it is no * longer needed. * * * @param device The elxu device structure of the device. * * @return Returns a pointer to the allocated string containing * the serial number. Returns NULL on error. */ uint32_t elxu_get_phy_port_num(elxu_device_t *device) { return device->phy_port_num; } /** * @ingroup dev_mgmt * @brief Return the sli_intf value for a device. * * @par Description * This function returns the sli_intf value for a device. * * @param device The elxu device structure of the device. * * @return Returns a uint32_t containing the sli_intf value. */ uint32_t elxu_get_sliintf(elxu_device_t *device) { return device->sli_intf; } /** * @ingroup dev_mgmt * @brief Return the ASIC ID value for a device. * * @par Description * This function returns the ASIC ID value for a device. * * @param device The elxu device structure of the device. * * @return Returns a uint32_t containing the ASIC ID value. */ uint32_t elxu_get_asic_id(elxu_device_t *device) { return device->asic_id; } /** * @ingroup dev_mgmt * @brief Lookup a family name. * * @par Description * Given a family number, this function returns a string * containing the family name. * * @param family The family number. * * @return Returns a static string containing the family name. */ char *elxu_decode_family(uint32_t family) { static char result[80]; switch(family) { case 0x0a: strncpy(result, "Lancer A0", sizeof(result)); break; case 0x0b: strncpy(result, "Lancer B0", sizeof(result)); break; default: strncpy(result, "Unknown", sizeof(result)); break; } return result; } /** * @ingroup dev_mgmt * @brief Lookup an ASIC ID name. * * @par Description * Given a family number, this function returns a string * containing the ASIC ID name. * * @param asic_id The ASIC ID to lookup. * * @return Returns a static string containing the ASIC ID name. */ char *elxu_decode_asic_id(uint32_t asic_id) { static char result[80]; char temp[10]; /* Note: Although there are ASIC_ID definitions for Lancer, the ASIC_ID register only exists on chips with a SLI_Family of 0xF. Currently only Skyhawk and Corsair have family 0x0F. So although there is a case here for decoding the Lancer value we don't expect to encounter it. Lancer will get decoded by elxu_decode_family */ switch (asic_id & 0xff00) { case 0x0400: strncpy (result, "Skyhawk-R", sizeof(result)); break; case 0x0500: strncpy (result, "Corsair", sizeof(result)); break; case 0x0b00: strncpy (result, "Lancer", sizeof(result)); break; default: strncpy(result, "Unknown", sizeof(result)); return result; } strncat(result, " ", 1); /* Ugly ASCII magic here. Converting 0, 1, 2... to A, B, C... */ sprintf(temp, "%c", (((asic_id & 0x00f0) >> 4) + 'A')); strncat(result, temp, 1); sprintf(temp, "%d", (asic_id & 0x000f)); strncat(result, temp, 1); return result; } /** * @ingroup dev_mgmt * @brief Return the vendor name for a device. * * @par Description * This function returns the vendor name for a device. * * @param device The elxu device structure of the device. * * @return Returns the vendor name in a static string. */ const char *elxu_get_vendor_name (elxu_device_t *device) { switch (device->vendor) { case PCI_VENDOR_ID_EMULEX: return "Emulex"; case PCI_VENDOR_ID_ATTO: return "ATTO"; default: return "Unknown"; } } /** * @ingroup dev_mgmt * @brief Return the device name for a device. * * @par Description * This function returns the device name for a device. * This includes both the device name and the protocol. * eg: "Lancer FCoE" * * @param device The elxu device structure of the device. * * @return Returns the device name in a static string. */ const char *elxu_get_device_name(elxu_device_t *device) { char *device_type_string = "Unknown"; char *protocol_string = "Unknown"; static char result[80]; int i; for (i=0;itype) { device_type_string = device_type_info[i].name; break; } } for (i=0;iprotocol) { protocol_string = device_protocol_info[i].name; break; } } sprintf(result, "%s %s", device_type_string, protocol_string); return result; } /** * @ingroup dev_mgmt * @brief Return the PCI subsystem device value for a device. * * @par Description * This function returns the PCI subsystem device value for a * device. The value returned is a static string containing the * hex representation of the 16 bit subsystem device value. * * @param device The elxu device structure of the device. * * @return Returns the PCI subsystem device in a static string. */ const char *elxu_get_subsystem_device(elxu_device_t *device) { static char result[80]; sprintf(result, "0x%04x", device->subsystem_device); return result; } /** * @ingroup dev_mgmt * @brief Return the PCI subsystem vendor value for a device. * * @par Description * This function returns the PCI subsystem vendor value for a * device. The value returned is a static string containing the * hex representation of the 16 bit subsystem vendor value. * * @param device The elxu device structure of the device. * * @return Returns the PCI subsystem vendor in a static string. */ const char *elxu_get_subsystem_vendor(elxu_device_t *device) { static char result[80]; sprintf(result, "0x%04x", device->subsystem_vendor); return result; } /** * @ingroup dev_mgmt * @brief Return a pointer to a device based on command line args. * * @par Description * Given the command line arguments (argc and argv), determine which * device should be operated on. Assumes that elxu_build_device_list * has already been called. * * @param argc Argument count from the command line * @param argv Arguments from the command line * * @return Returns a pointer to a device structure, or NULL if the * device can't be determined. */ elxu_device_t *elxu_device_from_args(int argc, char *argv[]) { elxu_device_t *device; int i; int index = 0; for (i=0; ideviceIndex == index) { break; } device = device->next; } return device; } /** * @ingroup dev_mgmt * @brief Return a pointer to a device given an index * * @par Description * Given a device index, determine which * device should be operated on. Assumes that elxu_build_device_list * has already been called. * * @param index Device index to find. * * @return Returns a pointer to a device structure, or NULL if the * device can't be determined. */ elxu_device_t *elxu_device_by_index(int index) { elxu_device_t *device; device = device_list; while (device != NULL) { if (device->deviceIndex == index) { break; } device = device->next; } return device; } /** * @ingroup dev_mgmt * @brief Return a property value for a device * * @par Description * Given a device and a key, return the value for that key. * For the OCS driver this is just a wrapper around calls * to mgmt_get_value. The driver's mgmt interface will * supply the value. * * For the elx_pt driver we have to simulate the mgmt_get_value * function because the driver doesn't have the mgmt interface. * For a limited set of key this function will determine * the correct value in other ways. * * @param device Device to access. * @param key Name of the property to retrieve. * * @return Returns a pointer to a static char[] with the result. */ char *elxu_device_get_value(elxu_device_t *device, char *key) { static char result[1024]; if (device->driver == DEVICE_DRIVER_OCS) { return mgmt_get_value(device, key); } result[0] = '\0'; if (strcmp(key, "/ocs/businfo") == 0) { strcpy(result, device->bus_addr); } if (strlen(result) == 0) { printf("*** elxu_device_get_value called for non-OCS driver for key \"%s\"\n", key); } return result; } int elxu_lancer_run_loopback(elxu_device_t *device_p, const char *fname, int type) { int rc = 0, i; uint8_t *tx_buf = NULL; uint8_t *rx_buf = NULL; tx_buf = malloc(LOOPBACK_BUF_SIZE); if (tx_buf == NULL) { printf("%s: failed to allocate TX buffer\n", __func__); rc = -1; goto exit_lancer_run_loopback; } memset(tx_buf, 0, LOOPBACK_BUF_SIZE); /*Fill TX buffer wih predefined pattren 0 1 2 3 4 .. */ for (i = 0; i < LOOPBACK_BUF_SIZE; i++) tx_buf[i] = (char)i & 0xFF; rx_buf = malloc(LOOPBACK_BUF_SIZE); if (rx_buf == NULL) { printf("%s: failed to allocate RX buffer\n", __func__); rc = -1; goto exit_lancer_run_loopback; } memset(rx_buf, 0, LOOPBACK_BUF_SIZE); if ((type == INTERNAL_LOOPBACK) || (type == EXTERNAL_LOOPBACK)) { rc = elxu_device_exec(device_p, "/ocs/driver/link_loopback", tx_buf, LOOPBACK_BUF_SIZE, rx_buf, LOOPBACK_BUF_SIZE); } else if (type == PCI_LOOPBACK){ rc = elxu_device_exec(device_p, "/ocs/driver/pci_loopback", tx_buf, LOOPBACK_BUF_SIZE, rx_buf, LOOPBACK_BUF_SIZE); } else { printf("Invalid loopback type\n"); rc = -1; } printf("\n"); if (!rc) { /* Compare source and destination buffers only if rx frame was received, * skip comparision in case of timeout. */ rc = memcmp(tx_buf, rx_buf, LOOPBACK_BUF_SIZE); if (rc) { if (type != EXTERNAL_LOOPBACK) printf("Data comparision b/w tx and rx buf is failed\n"); rc = -1; } } exit_lancer_run_loopback: if (tx_buf) free(tx_buf); if (rx_buf) free(rx_buf); return rc; } int elxu_lancer_set_loopback(elxu_device_t *device_p, const char *fname, int *type) { int rc = 0; if ((*type == INTERNAL_LOOPBACK) || (*type == EXTERNAL_LOOPBACK)) { rc = elxu_device_exec(device_p, "/ocs/driver/set_loopback", type, 4, NULL, 0); } if (rc) { printf("%s: failed to set loopback mode\n", __func__); } return rc; } int elxu_lancer_get_link_state(elxu_device_t *device_p, uint32_t *status) { int rc = 0; rc = elxu_device_exec(device_p, "/ocs/driver/get_link_state", NULL, 0, status, sizeof(uint32_t)); if (rc) { printf("%s: Get link status mbox command failed\n", __func__); } return rc; } int elxu_lancer_set_link_state(elxu_device_t *device_p, uint32_t *status) { int rc = 0; rc = elxu_device_exec(device_p, "/ocs/driver/set_link_state", status, sizeof(uint32_t), NULL, 0); if (rc) { printf("%s: Set link status mbox command failed\n", __func__); } return rc; } int elx_pt_set_dump_location(elxu_device_t *device, uint32_t num_buffers, memref_t **dump_buffers, uint8_t fdb) { mbox_common_set_dump_location_t req; elx_pt_ioctl_mbox_t mbox; int rc; if (num_buffers > 1) { uint32_t sge_size = num_buffers * sizeof(sli4_sge_t); sli4_sge_t *sge; uint32_t i; memref_t *dma_sge = NULL; dma_sge = elx_pt_dma_alloc(device, sge_size); if (dma_sge == NULL) { printf("%s: failed to allocate dma memory\n", __func__); goto fail; } memset(dma_sge->vaddr, 0, sge_size); sge = dma_sge->vaddr; for (i = 0; i < num_buffers; i++) { sge[i].buffer_address_high = addr32_hi(dump_buffers[i]->paddr); sge[i].buffer_address_low = addr32_lo(dump_buffers[i]->paddr); sge[i].last = (i == num_buffers - 1 ? 1 : 0); sge[i].buffer_length = dump_buffers[i]->size; } bzero(&req, sizeof(req)); req.subsystem = MBOX_SUBSYSTEM_COMMON; req.opcode = OPCODE_COMMON_SET_DUMP_LOCATION; req.request_length = sizeof(req) + sizeof(mbox_hbd_t); req.buf_addr_low = addr32_lo(dma_sge->paddr); req.blp = 1; req.buf_addr_high = addr32_hi(dma_sge->paddr); req.buffer_length = sge_size; mbox.words = (uint32_t *)&req; mbox.num_words = (sizeof(req) + sizeof(mbox_hbd_t) + 3) / 4; rc = ioctl(device->fd, IOCTL_CMD_ELX_PT_MBOX, &mbox); if (rc) { printf("%s: IOCTL is failed\n", __func__); elx_pt_dma_free(dma_sge); goto fail; } elx_pt_dma_free(dma_sge); } return 0; fail: return -1; } /** * @ingroup dev_mgmt * @brief Execute a management function on a device * * @par Description * For the OCS driver this is just a wrapper around calls * to mgmt_exec. The driver's mgmt interface will * execute the function. * * For the elx_pt driver we have to simulate the mgmt_exec * function because the driver doesn't have the mgmt interface. * For a limited set of key this function will perform the * equivalent operation in other ways. * * @param device Device to access. * @param key Name of the property to retrieve. * * @return Returns a pointer to a static char[] with the result. */ int elxu_device_exec(elxu_device_t *device, char *name, void* arg_in, int arg_in_len, void* arg_out, int arg_out_len) { int result; if (device->driver == DEVICE_DRIVER_OCS) { return mgmt_exec(device, name, arg_in, arg_in_len, arg_out, arg_out_len); } result = -1; if (strcmp(name, "/ocs/driver/gendump") == 0) { /* * Dump is generated by doing a firmware reset with the DD * bit set */ mbox_fw_reset_pt(device, 1); result = 0; } if (strcmp(name, "/ocs/driver/dump_to_host") == 0) { #define ELX_PT_MAX_DMA_ALLOC 64*1024 int32_t num_buffers = (arg_out_len + (ELX_PT_MAX_DMA_ALLOC) - 1 )/(ELX_PT_MAX_DMA_ALLOC); memref_t **dma_buffers = NULL; uint32_t rem_bytes, i, offset; /* * Dump is generated by doing a firmware reset with the DD * bit set */ if ((arg_out_len == 0) || (arg_out == NULL)) { printf("%s: Input buffer is invalid\n", __func__); result = -1; goto fail; } /* Allocate a DMA buffers to hold the dump */ dma_buffers = malloc(sizeof(memref_t *) * num_buffers); if (dma_buffers == NULL) { printf("%s: failed to allocate dma_buffers memory\n", __func__); result = -1; goto fail; } rem_bytes = arg_out_len; for (i = 0; i < num_buffers; i++) { uint32_t num_bytes = MIN(rem_bytes, ELX_PT_MAX_DMA_ALLOC); dma_buffers[i] = elx_pt_dma_alloc(device, ELX_PT_MAX_DMA_ALLOC); if (dma_buffers[i] == NULL) { printf("%s: failed to allocate dma memory %d\n", __func__,i); result = -1; goto free_and_return; } rem_bytes -= num_bytes; } elx_pt_set_dump_location(device, num_buffers, dma_buffers, 0); mbox_fw_reset_pt(device, 1); offset = 0; result = 0; for (i = 0; i < num_buffers; i++) { memcpy((uint8_t *)arg_out + offset, dma_buffers[i]->vaddr, dma_buffers[i]->size); offset += dma_buffers[i]->size; } free_and_return: for (i = 0; i < num_buffers; i++) { if (dma_buffers[i]) elx_pt_dma_free(dma_buffers[i]); } free(dma_buffers); } if (strcmp(name, "/ocs/firmware_write") == 0) { memref_t *dma_buffer; int rc; uint8_t *chunk_p; size_t chunk_size; int remaining; mbox_common_write_object_t req; mbox_common_write_object_rsp_t *rsp; elx_pt_ioctl_mbox_t mbox; int change_status = 0; int last; #define FW_BUFFER_SIZE 64*1024 /* * The new firmware image is in memory at arg_in with a * length of arg_in_len. We need to send it in 64K chunks. */ /* Create a DMA buffer */ dma_buffer = elx_pt_dma_alloc(device, FW_BUFFER_SIZE); if (dma_buffer == NULL) { goto fail; } chunk_p = arg_in; remaining = arg_in_len; last = 0; rsp = (mbox_common_write_object_rsp_t *) &req; mbox.words = (uint32_t *)&req; mbox.num_words = (sizeof(req) + 3) / 4; result = 0; while (remaining > 0) { /* Copy the firmware to the DMA buffer */ chunk_size = (remaining > FW_BUFFER_SIZE ? FW_BUFFER_SIZE : remaining); memcpy(dma_buffer->vaddr, chunk_p, chunk_size); /* Send a COMMON_WRITE_OBJECT that points to the DMA buffer */ bzero(&req, sizeof(req)); req.subsystem = MBOX_SUBSYSTEM_COMMON; req.opcode = OPCODE_COMMON_WRITE_OBJECT; req.request_length = sizeof(req); strcpy((char *)req.object_name, "/prg/"); req.device_buffer_descriptor_count = 1; req.device_buffer_descriptor[0].buffer_address_high = addr32_hi(dma_buffer->paddr); req.device_buffer_descriptor[0].buffer_address_low = addr32_lo(dma_buffer->paddr); if (remaining <= FW_BUFFER_SIZE) { /* Set the EOF bit */ last = 1; } req.eof = last; req.desired_write_length = chunk_size; req.write_offset = chunk_p - (uint8_t *)arg_in; req.device_buffer_descriptor[0].buffer_length = chunk_size; rc = ioctl(device->fd, IOCTL_CMD_ELX_PT_MBOX, &mbox); if (rc != 0) { perror("ioctl"); result = -1; break; } if (rsp->status != 0) { printf("WRITE_OBJECT returned status %d\n", rsp->status); result = -1; break; } chunk_p += chunk_size; remaining -= chunk_size; if (last) { change_status = rsp->change_status; } printf("."); fflush(stdout); } printf("\n"); snprintf(arg_out, arg_out_len, "%d", change_status); /* Free the DMA buffer */ elx_pt_dma_free(dma_buffer); } if (result == -1) { printf("*** elxu_device_exec called for non-OCS driver for function \"%s\"\n", name); } fail: return result; } void set_protocol(elxu_device_t *device) { int i; for (i=0; ivendor == pci_device_map[i].vendor) && (device->device == pci_device_map[i].device)) { device->type = pci_device_map[i].type; device->protocol = pci_device_map[i].protocol; break; } } } /** * @brief Return true IFF the HBA is an iSCSI HBA * * Given a pointer to a device structure, return 1 if the HBA is iSCSI * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_iscsi(elxu_device_t *device_p) { if (device_p->protocol == DEVICE_PROTOCOL_ISCSI) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a NIC HBA * * Given a pointer to a device structure, return 1 if the HBA is NIC * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_nic(elxu_device_t *device_p) { if (device_p->protocol == DEVICE_PROTOCOL_NIC) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a FC HBA * * Given a pointer to a device structure, return 1 if the HBA is FC * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_fc(elxu_device_t *device_p) { if (device_p->protocol == DEVICE_PROTOCOL_FC) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a FCoE HBA * * Given a pointer to a device structure, return 1 if the HBA is FCoE * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_fcoe(elxu_device_t *device_p) { if (device_p->protocol == DEVICE_PROTOCOL_FCOE) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a Skyhawk * * Given a pointer to a device structure, return 1 if the HBA is Skyhawk * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_skyhawk(elxu_device_t *device_p) { if (device_p->type == DEVICE_TYPE_SKYHAWK) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a Lancer * * Given a pointer to a device structure, return 1 if the HBA is Lancer * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_lancer(elxu_device_t *device_p) { if (device_p->type == DEVICE_TYPE_LANCER) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a Lancer G5 * * Given a pointer to a device structure, return 1 if the HBA is Lancer * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_lancer_g5(elxu_device_t *device_p) { if (device_p->device == PCI_DEVICE_ID_LANCER_FC) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a Prism * * Given a pointer to a device structure, return 1 if the HBA is Prism * and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_prism(elxu_device_t *device_p) { if (device_p->type == DEVICE_TYPE_PRISM) { return 1; } else { return 0; } } /** * @brief Return true IFF the HBA is a Calypso * * Given a pointer to a device structure, return 1 if the HBA is * Calypso and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_calypso(elxu_device_t *device_p) { if ((device_p->subsystem_device == 0xe20d) || (device_p->subsystem_device == 0xe20f)) { return 1; } else { return 0; } } /** * @brief Return true IFF the device is managed by a user space driver * * Given a pointer to a device structure, return 1 if the driver is * user space and 0 if it is not. * * @param device_p A pointer to HBA node */ int is_uspace(elxu_device_t *device_p) { if (device_p->driver == DEVICE_DRIVER_USPACE) { return 1; } else { return 0; } }