ef270ab1b6
The ocs_fc(4) driver supports the following hardware: Emulex 16/8G FC GEN 5 HBAS LPe15004 FC Host Bus Adapters LPe160XX FC Host Bus Adapters Emulex 32/16G FC GEN 6 HBAS LPe3100X FC Host Bus Adapters LPe3200X FC Host Bus Adapters The driver supports target and initiator mode, and also supports FC-Tape. Note that the driver only currently works on little endian platforms. It is only included in the module build for amd64 and i386, and in GENERIC on amd64 only. Submitted by: Ram Kishore Vegesna <ram.vegesna@broadcom.com> Reviewed by: mav MFC after: 5 days Relnotes: yes Sponsored by: Broadcom Differential Revision: https://reviews.freebsd.org/D11423
2924 lines
81 KiB
C
2924 lines
81 KiB
C
/*-
|
|
* Copyright (c) 2017 Broadcom. All rights reserved.
|
|
* The term "Broadcom" refers to Broadcom Limited and/or its subsidiaries.
|
|
*
|
|
* 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.
|
|
*
|
|
* $FreeBSD$
|
|
*/
|
|
|
|
/**
|
|
* @file
|
|
* The ocs_mgmt top level functions for Fibre Channel.
|
|
*/
|
|
|
|
/**
|
|
* @defgroup mgmt Management Functions
|
|
*/
|
|
|
|
#include "ocs.h"
|
|
#include "ocs_mgmt.h"
|
|
#include "ocs_vpd.h"
|
|
|
|
#define SFP_PAGE_SIZE 128
|
|
|
|
/* Executables*/
|
|
|
|
static int ocs_mgmt_firmware_write(ocs_t *ocs, char *, void *buf, uint32_t buf_len, void*, uint32_t);
|
|
static int ocs_mgmt_firmware_reset(ocs_t *ocs, char *, void *buf, uint32_t buf_len, void*, uint32_t);
|
|
static int ocs_mgmt_function_reset(ocs_t *ocs, char *, void *buf, uint32_t buf_len, void*, uint32_t);
|
|
|
|
static void ocs_mgmt_fw_write_cb(int32_t status, uint32_t actual_write_length, uint32_t change_status, void *arg);
|
|
static int ocs_mgmt_force_assert(ocs_t *ocs, char *, void *buf, uint32_t buf_len, void*, uint32_t);
|
|
|
|
#if defined(OCS_INCLUDE_RAMD)
|
|
static int32_t
|
|
ocs_mgmt_read_phys(ocs_t *ocs, char *, void *, uint32_t , void *, uint32_t);
|
|
#endif
|
|
|
|
|
|
/* Getters */
|
|
|
|
static void get_nodes_count(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_desc(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_fw_rev(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_fw_rev2(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_ipl(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_wwnn(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_wwpn(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_fcid(ocs_t *, char *, ocs_textbuf_t *);
|
|
static void get_sn(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_pn(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_sli4_intf_reg(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_phy_port_num(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_asic_id(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_pci_vendor(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_pci_device(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_pci_subsystem_vendor(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_pci_subsystem_device(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_businfo(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_sfp_a0(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_sfp_a2(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_hw_rev1(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_hw_rev2(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_hw_rev3(ocs_t *, char *, ocs_textbuf_t*);
|
|
static void get_debug_mq_dump(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_debug_cq_dump(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_debug_wq_dump(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_debug_eq_dump(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_logmask(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_current_speed(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_current_topology(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_current_link_state(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_configured_speed(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_configured_topology(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_configured_link_state(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_linkcfg(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_req_wwnn(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_req_wwpn(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_nodedb_mask(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_profile_list(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_active_profile(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_port_protocol(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_driver_version(ocs_t*, char*, ocs_textbuf_t*);
|
|
static void get_chip_type(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_tgt_rscn_delay(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_tgt_rscn_period(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_inject_drop_cmd(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_inject_free_drop_cmd(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_inject_drop_data(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_inject_drop_resp(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_cmd_err_inject(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_cmd_delay_value(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_nv_wwpn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_nv_wwnn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_loglevel(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
static void get_node_abort_cnt(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf);
|
|
|
|
/* Setters */
|
|
static int set_debug_mq_dump(ocs_t*, char*, char*);
|
|
static int set_debug_cq_dump(ocs_t*, char*, char*);
|
|
static int set_debug_wq_dump(ocs_t*, char*, char*);
|
|
static int set_debug_eq_dump(ocs_t*, char*, char*);
|
|
static int set_logmask(ocs_t*, char*, char*);
|
|
static int set_configured_link_state(ocs_t*, char*, char*);
|
|
static int set_linkcfg(ocs_t*, char*, char*);
|
|
static int set_nodedb_mask(ocs_t*, char*, char*);
|
|
static int set_port_protocol(ocs_t*, char*, char*);
|
|
static int set_active_profile(ocs_t*, char*, char*);
|
|
static int set_tgt_rscn_delay(ocs_t*, char*, char*);
|
|
static int set_tgt_rscn_period(ocs_t*, char*, char*);
|
|
static int set_inject_drop_cmd(ocs_t*, char*, char*);
|
|
static int set_inject_free_drop_cmd(ocs_t*, char*, char*);
|
|
static int set_inject_drop_data(ocs_t*, char*, char*);
|
|
static int set_inject_drop_resp(ocs_t*, char*, char*);
|
|
static int set_cmd_err_inject(ocs_t*, char*, char*);
|
|
static int set_cmd_delay_value(ocs_t*, char*, char*);
|
|
static int set_nv_wwn(ocs_t*, char*, char*);
|
|
static int set_loglevel(ocs_t*, char*, char*);
|
|
|
|
static void ocs_mgmt_linkcfg_cb(int32_t status, uintptr_t value, void *arg);
|
|
#if defined(OCS_INCLUDE_RAMD)
|
|
static void* find_address_in_target(ocs_ramdisc_t **ramdisc_array, uint32_t ramdisc_count, uintptr_t target_addr);
|
|
#endif
|
|
|
|
ocs_mgmt_table_entry_t mgmt_table[] = {
|
|
{"nodes_count", get_nodes_count, NULL, NULL},
|
|
{"desc", get_desc, NULL, NULL},
|
|
{"fw_rev", get_fw_rev, NULL, NULL},
|
|
{"fw_rev2", get_fw_rev2, NULL, NULL},
|
|
{"ipl", get_ipl, NULL, NULL},
|
|
{"hw_rev1", get_hw_rev1, NULL, NULL},
|
|
{"hw_rev2", get_hw_rev2, NULL, NULL},
|
|
{"hw_rev3", get_hw_rev3, NULL, NULL},
|
|
{"wwnn", get_wwnn, NULL, NULL},
|
|
{"wwpn", get_wwpn, NULL, NULL},
|
|
{"fc_id", get_fcid, NULL, NULL},
|
|
{"sn", get_sn, NULL, NULL},
|
|
{"pn", get_pn, NULL, NULL},
|
|
{"sli4_intf_reg", get_sli4_intf_reg, NULL, NULL},
|
|
{"phy_port_num", get_phy_port_num, NULL, NULL},
|
|
{"asic_id_reg", get_asic_id, NULL, NULL},
|
|
{"pci_vendor", get_pci_vendor, NULL, NULL},
|
|
{"pci_device", get_pci_device, NULL, NULL},
|
|
{"pci_subsystem_vendor", get_pci_subsystem_vendor, NULL, NULL},
|
|
{"pci_subsystem_device", get_pci_subsystem_device, NULL, NULL},
|
|
{"businfo", get_businfo, NULL, NULL},
|
|
{"sfp_a0", get_sfp_a0, NULL, NULL},
|
|
{"sfp_a2", get_sfp_a2, NULL, NULL},
|
|
{"profile_list", get_profile_list, NULL, NULL},
|
|
{"driver_version", get_driver_version, NULL, NULL},
|
|
{"current_speed", get_current_speed, NULL, NULL},
|
|
{"current_topology", get_current_topology, NULL, NULL},
|
|
{"current_link_state", get_current_link_state, NULL, NULL},
|
|
{"chip_type", get_chip_type, NULL, NULL},
|
|
{"configured_speed", get_configured_speed, set_configured_speed, NULL},
|
|
{"configured_topology", get_configured_topology, set_configured_topology, NULL},
|
|
{"configured_link_state", get_configured_link_state, set_configured_link_state, NULL},
|
|
{"debug_mq_dump", get_debug_mq_dump, set_debug_mq_dump, NULL},
|
|
{"debug_cq_dump", get_debug_cq_dump, set_debug_cq_dump, NULL},
|
|
{"debug_wq_dump", get_debug_wq_dump, set_debug_wq_dump, NULL},
|
|
{"debug_eq_dump", get_debug_eq_dump, set_debug_eq_dump, NULL},
|
|
{"logmask", get_logmask, set_logmask, NULL},
|
|
{"loglevel", get_loglevel, set_loglevel, NULL},
|
|
{"linkcfg", get_linkcfg, set_linkcfg, NULL},
|
|
{"requested_wwnn", get_req_wwnn, set_req_wwnn, NULL},
|
|
{"requested_wwpn", get_req_wwpn, set_req_wwpn, NULL},
|
|
{"nodedb_mask", get_nodedb_mask, set_nodedb_mask, NULL},
|
|
{"port_protocol", get_port_protocol, set_port_protocol, NULL},
|
|
{"active_profile", get_active_profile, set_active_profile, NULL},
|
|
{"firmware_write", NULL, NULL, ocs_mgmt_firmware_write},
|
|
{"firmware_reset", NULL, NULL, ocs_mgmt_firmware_reset},
|
|
{"function_reset", NULL, NULL, ocs_mgmt_function_reset},
|
|
#if defined(OCS_INCLUDE_RAMD)
|
|
{"read_phys", NULL, NULL, ocs_mgmt_read_phys},
|
|
#endif
|
|
{"force_assert", NULL, NULL, ocs_mgmt_force_assert},
|
|
|
|
{"tgt_rscn_delay", get_tgt_rscn_delay, set_tgt_rscn_delay, NULL},
|
|
{"tgt_rscn_period", get_tgt_rscn_period, set_tgt_rscn_period, NULL},
|
|
{"inject_drop_cmd", get_inject_drop_cmd, set_inject_drop_cmd, NULL},
|
|
{"inject_free_drop_cmd", get_inject_free_drop_cmd, set_inject_free_drop_cmd, NULL},
|
|
{"inject_drop_data", get_inject_drop_data, set_inject_drop_data, NULL},
|
|
{"inject_drop_resp", get_inject_drop_resp, set_inject_drop_resp, NULL},
|
|
{"cmd_err_inject", get_cmd_err_inject, set_cmd_err_inject, NULL},
|
|
{"cmd_delay_value", get_cmd_delay_value, set_cmd_delay_value, NULL},
|
|
{"nv_wwpn", get_nv_wwpn, NULL, NULL},
|
|
{"nv_wwnn", get_nv_wwnn, NULL, NULL},
|
|
{"nv_wwn", NULL, set_nv_wwn, NULL},
|
|
{"node_abort_cnt", get_node_abort_cnt, NULL, NULL},
|
|
};
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Get a list of options supported by the driver.
|
|
*
|
|
* @par Description
|
|
* This is the top level "get list" handler for the driver. It
|
|
* performs the following:
|
|
* - Adds entries to the textbuf for any actions supported by this level in the driver.
|
|
* - Calls a back-end function to add any actions supported by the back-end.
|
|
* - Calls a function on each child (domain) to recursively add supported actions.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to accumulate the results.
|
|
*
|
|
* @return Returns 0 on success, or a negative value on failure.
|
|
*/
|
|
|
|
void
|
|
ocs_mgmt_get_list(ocs_t *ocs, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_domain_t *domain;
|
|
uint32_t i;
|
|
int access;
|
|
|
|
ocs_mgmt_start_unnumbered_section(textbuf, "ocs");
|
|
|
|
for (i=0;i<ARRAY_SIZE(mgmt_table);i++) {
|
|
access = 0;
|
|
if (mgmt_table[i].get_handler) {
|
|
access |= MGMT_MODE_RD;
|
|
}
|
|
if (mgmt_table[i].set_handler) {
|
|
access |= MGMT_MODE_WR;
|
|
}
|
|
if (mgmt_table[i].action_handler) {
|
|
access |= MGMT_MODE_EX;
|
|
}
|
|
ocs_mgmt_emit_property_name(textbuf, access, mgmt_table[i].name);
|
|
}
|
|
|
|
if ((ocs->mgmt_functions) && (ocs->mgmt_functions->get_list_handler)) {
|
|
ocs->mgmt_functions->get_list_handler(textbuf, ocs);
|
|
}
|
|
|
|
if ((ocs->tgt_mgmt_functions) && (ocs->tgt_mgmt_functions->get_list_handler)) {
|
|
ocs->tgt_mgmt_functions->get_list_handler(textbuf, &(ocs->tgt_ocs));
|
|
}
|
|
|
|
/* Have each of my children add their actions */
|
|
if (ocs_device_lock_try(ocs) == TRUE) {
|
|
|
|
/* If we get here then we are holding the device lock */
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if ((domain->mgmt_functions) && (domain->mgmt_functions->get_list_handler)) {
|
|
domain->mgmt_functions->get_list_handler(textbuf, domain);
|
|
}
|
|
}
|
|
ocs_device_unlock(ocs);
|
|
}
|
|
|
|
ocs_mgmt_end_unnumbered_section(textbuf, "ocs");
|
|
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Return the value of a management item.
|
|
*
|
|
* @par Description
|
|
* This is the top level "get" handler for the driver. It
|
|
* performs the following:
|
|
* - Checks that the qualifier portion of the name begins with my qualifier (ocs).
|
|
* - If the remaining part of the name matches a parameter that is known at this level,
|
|
* writes the value into textbuf.
|
|
* - If the name is not known, sends the request to the back-ends to fulfill (if possible).
|
|
* - If the request has not been fulfilled by the back-end,
|
|
* passes the request to each of the children (domains) to
|
|
* have them (recursively) try to respond.
|
|
*
|
|
* In passing the request to other entities, the request is considered to be answered
|
|
* when a response has been written into textbuf, indicated by textbuf->buffer_written
|
|
* being non-zero.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the status item to be retrieved.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return Returns 0 if the value was found and returned, or -1 if an error occurred.
|
|
*/
|
|
|
|
|
|
int
|
|
ocs_mgmt_get(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_domain_t *domain;
|
|
char qualifier[6];
|
|
int retval = -1;
|
|
uint32_t i;
|
|
|
|
ocs_mgmt_start_unnumbered_section(textbuf, "ocs");
|
|
|
|
|
|
snprintf(qualifier, sizeof(qualifier), "/ocs");
|
|
|
|
/* See if the name starts with my qualifier. If not then this request isn't for me */
|
|
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
|
|
char *unqualified_name = name + strlen(qualifier) + 1;
|
|
|
|
for (i=0;i<ARRAY_SIZE(mgmt_table);i++) {
|
|
if (ocs_strcmp(unqualified_name, mgmt_table[i].name) == 0) {
|
|
if (mgmt_table[i].get_handler) {
|
|
mgmt_table[i].get_handler(ocs, name, textbuf);
|
|
ocs_mgmt_end_unnumbered_section(textbuf, "ocs");
|
|
return 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ocs->mgmt_functions) && (ocs->mgmt_functions->get_handler)) {
|
|
retval = ocs->mgmt_functions->get_handler(textbuf, qualifier, (char*)name, ocs);
|
|
}
|
|
|
|
if (retval != 0) {
|
|
if ((ocs->tgt_mgmt_functions) && (ocs->tgt_mgmt_functions->get_handler)) {
|
|
retval = ocs->tgt_mgmt_functions->get_handler(textbuf, qualifier,
|
|
(char*)name, &(ocs->tgt_ocs));
|
|
}
|
|
}
|
|
|
|
if (retval != 0) {
|
|
/* The driver didn't handle it, pass it to each domain */
|
|
|
|
ocs_device_lock(ocs);
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if ((domain->mgmt_functions) && (domain->mgmt_functions->get_handler)) {
|
|
retval = domain->mgmt_functions->get_handler(textbuf, qualifier, (char*)name, domain);
|
|
}
|
|
|
|
if (retval == 0) {
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
ocs_device_unlock(ocs);
|
|
}
|
|
|
|
}
|
|
|
|
ocs_mgmt_end_unnumbered_section(textbuf, "ocs");
|
|
|
|
return retval;
|
|
}
|
|
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Set the value of a mgmt item.
|
|
*
|
|
* @par Description
|
|
* This is the top level "set" handler for the driver. It
|
|
* performs the following:
|
|
* - Checks that the qualifier portion of the name begins with my qualifier (ocs).
|
|
* - If the remaining part of the name matches a parameter that is known at this level,
|
|
* calls the correct function to change the configuration.
|
|
* - If the name is not known, sends the request to the back-ends to fulfill (if possible).
|
|
* - If the request has not been fulfilled by the back-end, passes the request to each of the
|
|
* children (domains) to have them (recursively) try to respond.
|
|
*
|
|
* In passing the request to other entities, the request is considered to be handled
|
|
* if the function returns 0.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the property to be changed.
|
|
* @param value Requested new value of the property.
|
|
*
|
|
* @return Returns 0 if the configuration value was updated, or -1 otherwise.
|
|
*/
|
|
|
|
int
|
|
ocs_mgmt_set(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs_domain_t *domain;
|
|
int result = -1;
|
|
char qualifier[80];
|
|
uint32_t i;
|
|
|
|
snprintf(qualifier, sizeof(qualifier), "/ocs");
|
|
|
|
/* If it doesn't start with my qualifier I don't know what to do with it */
|
|
if (ocs_strncmp(name, qualifier, strlen(qualifier)) == 0) {
|
|
char *unqualified_name = name + strlen(qualifier) +1;
|
|
|
|
/* See if it's a value I can set */
|
|
for (i=0;i<ARRAY_SIZE(mgmt_table);i++) {
|
|
if (ocs_strcmp(unqualified_name, mgmt_table[i].name) == 0) {
|
|
if (mgmt_table[i].set_handler) {
|
|
return mgmt_table[i].set_handler(ocs, name, value);
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((ocs->mgmt_functions) && (ocs->mgmt_functions->set_handler)) {
|
|
result = ocs->mgmt_functions->set_handler(qualifier, name, (char *)value, ocs);
|
|
}
|
|
|
|
if (result != 0) {
|
|
if ((ocs->tgt_mgmt_functions) && (ocs->tgt_mgmt_functions->set_handler)) {
|
|
result = ocs->tgt_mgmt_functions->set_handler(qualifier, name,
|
|
(char *)value, &(ocs->tgt_ocs));
|
|
}
|
|
}
|
|
|
|
/* If I didn't know how to set this config value pass the request to each of my children */
|
|
if (result != 0) {
|
|
ocs_device_lock(ocs);
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if ((domain->mgmt_functions) && (domain->mgmt_functions->set_handler)) {
|
|
result = domain->mgmt_functions->set_handler(qualifier, name, (char*)value, domain);
|
|
}
|
|
if (result == 0) {
|
|
break;
|
|
}
|
|
}
|
|
ocs_device_unlock(ocs);
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Perform a management action.
|
|
*
|
|
* @par Description
|
|
* This is the top level "exec" handler for the driver. It
|
|
* performs the following:
|
|
* - Checks that the qualifier portion of the name begins with my qualifier (ocs).
|
|
* - If the remaining part of the name matches an action that is known at this level,
|
|
* calls the correct function to perform the action.
|
|
* - If the name is not known, sends the request to the back-ends to fulfill (if possible).
|
|
* - If the request has not been fulfilled by the back-end, passes the request to each of the
|
|
* children (domains) to have them (recursively) try to respond.
|
|
*
|
|
* In passing the request to other entities, the request is considered to be handled
|
|
* if the function returns 0.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param action Name of the action to be performed.
|
|
* @param arg_in Pointer to an argument being passed to the action.
|
|
* @param arg_in_length Length of the argument pointed to by @c arg_in.
|
|
* @param arg_out Pointer to an argument being passed to the action.
|
|
* @param arg_out_length Length of the argument pointed to by @c arg_out.
|
|
*
|
|
* @return Returns 0 if the action was completed, or -1 otherwise.
|
|
*
|
|
*
|
|
*/
|
|
|
|
int
|
|
ocs_mgmt_exec(ocs_t *ocs, char *action, void *arg_in,
|
|
uint32_t arg_in_length, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
ocs_domain_t *domain;
|
|
int result = -1;
|
|
char qualifier[80];
|
|
uint32_t i;
|
|
|
|
snprintf(qualifier, sizeof(qualifier), "/ocs");
|
|
|
|
/* If it doesn't start with my qualifier I don't know what to do with it */
|
|
if (ocs_strncmp(action, qualifier, strlen(qualifier)) == 0) {
|
|
char *unqualified_name = action + strlen(qualifier) +1;
|
|
|
|
/* See if it's an action I can perform */
|
|
for (i=0;i<ARRAY_SIZE(mgmt_table); i++) {
|
|
if (ocs_strcmp(unqualified_name, mgmt_table[i].name) == 0) {
|
|
if (mgmt_table[i].action_handler) {
|
|
return mgmt_table[i].action_handler(ocs, action, arg_in, arg_in_length,
|
|
arg_out, arg_out_length);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
if ((ocs->mgmt_functions) && (ocs->mgmt_functions->exec_handler)) {
|
|
result = ocs->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length,
|
|
arg_out, arg_out_length, ocs);
|
|
}
|
|
|
|
if (result != 0) {
|
|
if ((ocs->tgt_mgmt_functions) && (ocs->tgt_mgmt_functions->exec_handler)) {
|
|
result = ocs->tgt_mgmt_functions->exec_handler(qualifier, action,
|
|
arg_in, arg_in_length, arg_out, arg_out_length,
|
|
&(ocs->tgt_ocs));
|
|
}
|
|
}
|
|
|
|
/* If I didn't know how to do this action pass the request to each of my children */
|
|
if (result != 0) {
|
|
ocs_device_lock(ocs);
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if ((domain->mgmt_functions) && (domain->mgmt_functions->exec_handler)) {
|
|
result = domain->mgmt_functions->exec_handler(qualifier, action, arg_in, arg_in_length, arg_out,
|
|
arg_out_length, domain);
|
|
}
|
|
if (result == 0) {
|
|
break;
|
|
}
|
|
}
|
|
ocs_device_unlock(ocs);
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
void
|
|
ocs_mgmt_get_all(ocs_t *ocs, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_domain_t *domain;
|
|
uint32_t i;
|
|
|
|
ocs_mgmt_start_unnumbered_section(textbuf, "ocs");
|
|
|
|
for (i=0;i<ARRAY_SIZE(mgmt_table);i++) {
|
|
if (mgmt_table[i].get_handler) {
|
|
mgmt_table[i].get_handler(ocs, mgmt_table[i].name, textbuf);
|
|
} else if (mgmt_table[i].action_handler) {
|
|
/* No get_handler, but there's an action_handler. Just report
|
|
the name */
|
|
ocs_mgmt_emit_property_name(textbuf, MGMT_MODE_EX, mgmt_table[i].name);
|
|
}
|
|
}
|
|
|
|
if ((ocs->mgmt_functions) && (ocs->mgmt_functions->get_all_handler)) {
|
|
ocs->mgmt_functions->get_all_handler(textbuf, ocs);
|
|
}
|
|
|
|
if ((ocs->tgt_mgmt_functions) && (ocs->tgt_mgmt_functions->get_all_handler)) {
|
|
ocs->tgt_mgmt_functions->get_all_handler(textbuf, &(ocs->tgt_ocs));
|
|
}
|
|
|
|
ocs_device_lock(ocs);
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if ((domain->mgmt_functions) && (domain->mgmt_functions->get_all_handler)) {
|
|
domain->mgmt_functions->get_all_handler(textbuf, domain);
|
|
}
|
|
}
|
|
ocs_device_unlock(ocs);
|
|
|
|
ocs_mgmt_end_unnumbered_section(textbuf, "ocs");
|
|
}
|
|
|
|
#if defined(OCS_INCLUDE_RAMD)
|
|
static int32_t
|
|
ocs_mgmt_read_phys(ocs_t *ocs, char *name, void *arg_in, uint32_t arg_in_length, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
uint32_t length;
|
|
char addr_str[80];
|
|
uintptr_t target_addr;
|
|
void* vaddr = NULL;
|
|
ocs_ramdisc_t **ramdisc_array;
|
|
uint32_t ramdisc_count;
|
|
|
|
|
|
if ((arg_in == NULL) ||
|
|
(arg_in_length == 0) ||
|
|
(arg_out == NULL) ||
|
|
(arg_out_length == 0)) {
|
|
return -1;
|
|
}
|
|
|
|
if (arg_in_length > 80) {
|
|
arg_in_length = 80;
|
|
}
|
|
|
|
if (ocs_copy_from_user(addr_str, arg_in, arg_in_length)) {
|
|
ocs_log_test(ocs, "Failed to copy addr from user\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
target_addr = (uintptr_t)ocs_strtoul(addr_str, NULL, 0);
|
|
/* addr_str must be the physical address of a buffer that was reported
|
|
* in an SGL. Search ramdiscs looking for a segment that contains that
|
|
* physical address
|
|
*/
|
|
|
|
if (ocs->tgt_ocs.use_global_ramd) {
|
|
/* Only one target */
|
|
ramdisc_count = ocs->tgt_ocs.rdisc_count;
|
|
ramdisc_array = ocs->tgt_ocs.rdisc;
|
|
vaddr = find_address_in_target(ramdisc_array, ramdisc_count, target_addr);
|
|
} else {
|
|
/* Multiple targets. Each target is on a sport */
|
|
uint32_t domain_idx;
|
|
|
|
for (domain_idx=0; domain_idx<ocs->domain_instance_count; domain_idx++) {
|
|
ocs_domain_t *domain;
|
|
uint32_t sport_idx;
|
|
|
|
domain = ocs_domain_get_instance(ocs, domain_idx);
|
|
for (sport_idx=0; sport_idx < domain->sport_instance_count; sport_idx++) {
|
|
ocs_sport_t *sport;
|
|
|
|
sport = ocs_sport_get_instance(domain, sport_idx);
|
|
ramdisc_count = sport->tgt_sport.rdisc_count;
|
|
ramdisc_array = sport->tgt_sport.rdisc;
|
|
vaddr = find_address_in_target(ramdisc_array, ramdisc_count, target_addr);
|
|
|
|
if (vaddr != NULL) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
length = arg_out_length;
|
|
|
|
if (vaddr != NULL) {
|
|
|
|
if (ocs_copy_to_user(arg_out, vaddr, length)) {
|
|
ocs_log_test(ocs, "Failed to copy buffer to user\n");
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
} else {
|
|
|
|
return -EFAULT;
|
|
}
|
|
|
|
}
|
|
|
|
/*
|
|
* This function searches a target for a given physical address.
|
|
* The target is made up of a number of LUNs, each represented by
|
|
* a ocs_ramdisc_t.
|
|
*/
|
|
static void* find_address_in_target(ocs_ramdisc_t **ramdisc_array, uint32_t ramdisc_count, uintptr_t target_addr)
|
|
{
|
|
void *vaddr = NULL;
|
|
uint32_t ramdisc_idx;
|
|
|
|
/* Check each ramdisc */
|
|
for (ramdisc_idx=0; ramdisc_idx<ramdisc_count; ramdisc_idx++) {
|
|
uint32_t segment_idx;
|
|
ocs_ramdisc_t *rdisc;
|
|
rdisc = ramdisc_array[ramdisc_idx];
|
|
/* Check each segment in the ramdisc */
|
|
for (segment_idx=0; segment_idx<rdisc->segment_count; segment_idx++) {
|
|
ramdisc_segment_t *segment = rdisc->segments[segment_idx];
|
|
uintptr_t segment_start;
|
|
uintptr_t segment_end;
|
|
uint32_t offset;
|
|
|
|
segment_start = segment->data_segment.phys;
|
|
segment_end = segment->data_segment.phys + segment->data_segment.size - 1;
|
|
if ((target_addr >= segment_start) && (target_addr <= segment_end)) {
|
|
/* Found the target address */
|
|
offset = target_addr - segment_start;
|
|
vaddr = (uint32_t*)segment->data_segment.virt + offset;
|
|
}
|
|
|
|
if (rdisc->dif_separate) {
|
|
segment_start = segment->dif_segment.phys;
|
|
segment_end = segment->data_segment.phys + segment->dif_segment.size - 1;
|
|
if ((target_addr >= segment_start) && (target_addr <= segment_end)) {
|
|
/* Found the target address */
|
|
offset = target_addr - segment_start;
|
|
vaddr = (uint32_t*)segment->dif_segment.virt + offset;
|
|
}
|
|
}
|
|
|
|
if (vaddr != NULL) {
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
if (vaddr != NULL) {
|
|
break;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
return vaddr;
|
|
}
|
|
#endif
|
|
|
|
|
|
|
|
static int32_t
|
|
ocs_mgmt_firmware_reset(ocs_t *ocs, char *name, void *buf, uint32_t buf_len, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
int rc = 0;
|
|
int index = 0;
|
|
uint8_t bus, dev, func;
|
|
ocs_t *other_ocs;
|
|
|
|
ocs_get_bus_dev_func(ocs, &bus, &dev, &func);
|
|
|
|
ocs_log_debug(ocs, "Resetting port\n");
|
|
if (ocs_hw_reset(&ocs->hw, OCS_HW_RESET_FIRMWARE)) {
|
|
ocs_log_test(ocs, "failed to reset port\n");
|
|
rc = -1;
|
|
} else {
|
|
ocs_log_debug(ocs, "successfully reset port\n");
|
|
|
|
/* now reset all functions on the same device */
|
|
|
|
while ((other_ocs = ocs_get_instance(index++)) != NULL) {
|
|
uint8_t other_bus, other_dev, other_func;
|
|
|
|
ocs_get_bus_dev_func(other_ocs, &other_bus, &other_dev, &other_func);
|
|
|
|
if ((bus == other_bus) && (dev == other_dev)) {
|
|
if (other_ocs->hw.state !=
|
|
OCS_HW_STATE_UNINITIALIZED) {
|
|
other_ocs->hw.state =
|
|
OCS_HW_STATE_QUEUES_ALLOCATED;
|
|
}
|
|
|
|
ocs_device_detach(other_ocs);
|
|
if (ocs_device_attach(other_ocs)) {
|
|
ocs_log_err(other_ocs,
|
|
"device %d attach failed \n", index);
|
|
rc = -1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
static int32_t
|
|
ocs_mgmt_function_reset(ocs_t *ocs, char *name, void *buf, uint32_t buf_len, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
int32_t rc;
|
|
|
|
ocs_device_detach(ocs);
|
|
rc = ocs_device_attach(ocs);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int32_t
|
|
ocs_mgmt_firmware_write(ocs_t *ocs, char *name, void *buf, uint32_t buf_len, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
int rc = 0;
|
|
uint32_t bytes_left;
|
|
uint32_t xfer_size;
|
|
uint32_t offset;
|
|
uint8_t *userp;
|
|
ocs_dma_t dma;
|
|
int last = 0;
|
|
ocs_mgmt_fw_write_result_t result;
|
|
uint32_t change_status = 0;
|
|
char status_str[80];
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "fw_write");
|
|
|
|
bytes_left = buf_len;
|
|
offset = 0;
|
|
userp = (uint8_t *)buf;
|
|
|
|
if (ocs_dma_alloc(ocs, &dma, FW_WRITE_BUFSIZE, 4096)) {
|
|
ocs_log_err(ocs, "ocs_mgmt_firmware_write: malloc failed");
|
|
return -ENOMEM;
|
|
}
|
|
|
|
while (bytes_left > 0) {
|
|
|
|
|
|
if (bytes_left > FW_WRITE_BUFSIZE) {
|
|
xfer_size = FW_WRITE_BUFSIZE;
|
|
} else {
|
|
xfer_size = bytes_left;
|
|
}
|
|
|
|
/* Copy xfer_size bytes from user space to kernel buffer */
|
|
if (ocs_copy_from_user(dma.virt, userp, xfer_size)) {
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* See if this is the last block */
|
|
if (bytes_left == xfer_size) {
|
|
last = 1;
|
|
}
|
|
|
|
/* Send the HW command */
|
|
ocs_hw_firmware_write(&ocs->hw, &dma, xfer_size, offset, last, ocs_mgmt_fw_write_cb, &result);
|
|
|
|
/* Wait for semaphore to be signaled when the command completes
|
|
* TODO: Should there be a timeout on this? If so, how long? */
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
rc = -ENXIO;
|
|
break;
|
|
}
|
|
|
|
if (result.actual_xfer == 0) {
|
|
ocs_log_test(ocs, "actual_write_length is %d\n", result.actual_xfer);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
/* Check status */
|
|
if (result.status != 0) {
|
|
ocs_log_test(ocs, "write returned status %d\n", result.status);
|
|
rc = -EFAULT;
|
|
break;
|
|
}
|
|
|
|
if (last) {
|
|
change_status = result.change_status;
|
|
}
|
|
|
|
bytes_left -= result.actual_xfer;
|
|
offset += result.actual_xfer;
|
|
userp += result.actual_xfer;
|
|
|
|
}
|
|
|
|
/* Create string with status and copy to userland */
|
|
if ((arg_out_length > 0) && (arg_out != NULL)) {
|
|
if (arg_out_length > sizeof(status_str)) {
|
|
arg_out_length = sizeof(status_str);
|
|
}
|
|
ocs_snprintf(status_str, arg_out_length, "%d", change_status);
|
|
if (ocs_copy_to_user(arg_out, status_str, arg_out_length)) {
|
|
ocs_log_test(ocs, "copy to user failed for change_status\n");
|
|
}
|
|
}
|
|
|
|
|
|
ocs_dma_free(ocs, &dma);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static void
|
|
ocs_mgmt_fw_write_cb(int32_t status, uint32_t actual_write_length, uint32_t change_status, void *arg)
|
|
{
|
|
ocs_mgmt_fw_write_result_t *result = arg;
|
|
|
|
result->status = status;
|
|
result->actual_xfer = actual_write_length;
|
|
result->change_status = change_status;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
typedef struct ocs_mgmt_sfp_result {
|
|
ocs_sem_t semaphore;
|
|
ocs_lock_t cb_lock;
|
|
int32_t running;
|
|
int32_t status;
|
|
uint32_t bytes_read;
|
|
uint32_t page_data[32];
|
|
} ocs_mgmt_sfp_result_t;
|
|
|
|
static void
|
|
ocs_mgmt_sfp_cb(void *os, int32_t status, uint32_t bytes_read, uint32_t *data, void *arg)
|
|
{
|
|
ocs_mgmt_sfp_result_t *result = arg;
|
|
ocs_t *ocs = os;
|
|
|
|
ocs_lock(&(result->cb_lock));
|
|
result->running++;
|
|
if(result->running == 2) {
|
|
/* get_sfp() has timed out */
|
|
ocs_unlock(&(result->cb_lock));
|
|
ocs_free(ocs, result, sizeof(ocs_mgmt_sfp_result_t));
|
|
return;
|
|
}
|
|
|
|
result->status = status;
|
|
result->bytes_read = bytes_read;
|
|
ocs_memcpy(&result->page_data, data, SFP_PAGE_SIZE);
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
ocs_unlock(&(result->cb_lock));
|
|
}
|
|
|
|
static int32_t
|
|
ocs_mgmt_get_sfp(ocs_t *ocs, uint16_t page, void *buf, uint32_t buf_len)
|
|
{
|
|
int rc = 0;
|
|
ocs_mgmt_sfp_result_t *result = ocs_malloc(ocs, sizeof(ocs_mgmt_sfp_result_t), OCS_M_ZERO | OCS_M_NOWAIT);;
|
|
|
|
ocs_sem_init(&(result->semaphore), 0, "get_sfp");
|
|
ocs_lock_init(ocs, &(result->cb_lock), "get_sfp");
|
|
|
|
/* Send the HW command */
|
|
ocs_hw_get_sfp(&ocs->hw, page, ocs_mgmt_sfp_cb, result);
|
|
|
|
/* Wait for semaphore to be signaled when the command completes */
|
|
if (ocs_sem_p(&(result->semaphore), 5 * 1000 * 1000) != 0) {
|
|
/* Timed out, callback will free memory */
|
|
ocs_lock(&(result->cb_lock));
|
|
result->running++;
|
|
if(result->running == 1) {
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
ocs_unlock(&(result->cb_lock));
|
|
return (-ENXIO);
|
|
}
|
|
/* sfp_cb() has already executed, proceed as normal */
|
|
ocs_unlock(&(result->cb_lock));
|
|
}
|
|
|
|
/* Check status */
|
|
if (result->status != 0) {
|
|
ocs_log_test(ocs, "read_transceiver_data returned status %d\n",
|
|
result->status);
|
|
rc = -EFAULT;
|
|
}
|
|
|
|
if (rc == 0) {
|
|
rc = (result->bytes_read > buf_len ? buf_len : result->bytes_read);
|
|
/* Copy the results back to the supplied buffer */
|
|
ocs_memcpy(buf, result->page_data, rc);
|
|
}
|
|
|
|
ocs_free(ocs, result, sizeof(ocs_mgmt_sfp_result_t));
|
|
return rc;
|
|
}
|
|
|
|
static int32_t
|
|
ocs_mgmt_force_assert(ocs_t *ocs, char *name, void *buf, uint32_t buf_len, void *arg_out, uint32_t arg_out_length)
|
|
{
|
|
ocs_assert(FALSE, 0);
|
|
}
|
|
|
|
static void
|
|
get_nodes_count(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_xport_t *xport = ocs->xport;
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "nodes_count", "%d", xport->nodes_count);
|
|
}
|
|
|
|
static void
|
|
get_driver_version(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "driver_version", ocs->driver_version);
|
|
}
|
|
|
|
static void
|
|
get_desc(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "desc", ocs->desc);
|
|
}
|
|
|
|
static void
|
|
get_fw_rev(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "fw_rev", ocs_hw_get_ptr(&ocs->hw, OCS_HW_FW_REV));
|
|
}
|
|
|
|
static void
|
|
get_fw_rev2(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "fw_rev2", ocs_hw_get_ptr(&ocs->hw, OCS_HW_FW_REV2));
|
|
}
|
|
|
|
static void
|
|
get_ipl(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "ipl", ocs_hw_get_ptr(&ocs->hw, OCS_HW_IPL));
|
|
}
|
|
|
|
static void
|
|
get_hw_rev1(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&ocs->hw, OCS_HW_HW_REV1, &value);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_rev1", "%u", value);
|
|
}
|
|
|
|
static void
|
|
get_hw_rev2(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&ocs->hw, OCS_HW_HW_REV2, &value);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_rev2", "%u", value);
|
|
}
|
|
|
|
static void
|
|
get_hw_rev3(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
ocs_hw_get(&ocs->hw, OCS_HW_HW_REV3, &value);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "hw_rev3", "%u", value);
|
|
}
|
|
|
|
static void
|
|
get_wwnn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint64_t *wwnn;
|
|
|
|
wwnn = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_NODE);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwnn", "0x%llx", (unsigned long long)ocs_htobe64(*wwnn));
|
|
}
|
|
|
|
static void
|
|
get_wwpn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint64_t *wwpn;
|
|
|
|
wwpn = ocs_hw_get_ptr(&ocs->hw, OCS_HW_WWN_PORT);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "wwpn", "0x%llx", (unsigned long long)ocs_htobe64(*wwpn));
|
|
}
|
|
|
|
static void
|
|
get_fcid(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
if (ocs->domain && ocs->domain->attached) {
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "0x%06x",
|
|
ocs->domain->sport->fc_id);
|
|
} else {
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "fc_id", "UNKNOWN");
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
get_sn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint8_t *pserial;
|
|
uint32_t len;
|
|
char sn_buf[256];
|
|
|
|
pserial = ocs_scsi_get_property_ptr(ocs, OCS_SCSI_SERIALNUMBER);
|
|
if (pserial) {
|
|
len = *pserial ++;
|
|
strncpy(sn_buf, (char*)pserial, len);
|
|
sn_buf[len] = '\0';
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "sn", sn_buf);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_pn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint8_t *pserial;
|
|
uint32_t len;
|
|
char sn_buf[256];
|
|
|
|
pserial = ocs_scsi_get_property_ptr(ocs, OCS_SCSI_PARTNUMBER);
|
|
if (pserial) {
|
|
len = *pserial ++;
|
|
strncpy(sn_buf, (char*)pserial, len);
|
|
sn_buf[len] = '\0';
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "pn", sn_buf);
|
|
} else {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "pn", ocs->model);
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_sli4_intf_reg(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "sli4_intf_reg", "0x%04x",
|
|
ocs_config_read32(ocs, SLI4_INTF_REG));
|
|
}
|
|
|
|
static void
|
|
get_phy_port_num(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
char *phy_port = NULL;
|
|
|
|
phy_port = ocs_scsi_get_property_ptr(ocs, OCS_SCSI_PORTNUM);
|
|
if (phy_port) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "phy_port_num", phy_port);
|
|
} else {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "phy_port_num", "unknown");
|
|
}
|
|
}
|
|
static void
|
|
get_asic_id(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "asic_id_reg", "0x%04x",
|
|
ocs_config_read32(ocs, SLI4_ASIC_ID_REG));
|
|
}
|
|
|
|
static void
|
|
get_chip_type(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t family;
|
|
uint32_t asic_id;
|
|
uint32_t asic_gen_num;
|
|
uint32_t asic_rev_num;
|
|
uint32_t rev_id;
|
|
char result_buf[80];
|
|
char tmp_buf[80];
|
|
|
|
family = (ocs_config_read32(ocs, SLI4_INTF_REG) & 0x00000f00) >> 8;
|
|
asic_id = ocs_config_read32(ocs, SLI4_ASIC_ID_REG);
|
|
asic_rev_num = asic_id & 0xff;
|
|
asic_gen_num = (asic_id & 0xff00) >> 8;
|
|
|
|
rev_id = ocs_config_read32(ocs, SLI4_PCI_CLASS_REVISION) & 0xff;
|
|
|
|
switch(family) {
|
|
case 0x00:
|
|
/* BE2 */
|
|
ocs_strncpy(result_buf, "BE2 A", sizeof(result_buf));
|
|
ocs_snprintf(tmp_buf, 2, "%d", rev_id);
|
|
strcat(result_buf, tmp_buf);
|
|
break;
|
|
case 0x01:
|
|
/* BE3 */
|
|
ocs_strncpy(result_buf, "BE3", sizeof(result_buf));
|
|
if (rev_id >= 0x10) {
|
|
strcat(result_buf, "-R");
|
|
}
|
|
ocs_snprintf(tmp_buf, 3, " %c", ((rev_id & 0xf0) >> 4) + 'A');
|
|
strcat(result_buf, tmp_buf);
|
|
ocs_snprintf(tmp_buf, 2, "%d", rev_id & 0x0f);
|
|
strcat(result_buf, tmp_buf);
|
|
break;
|
|
case 0x02:
|
|
/* Skyhawk A0 */
|
|
ocs_strncpy(result_buf, "Skyhawk A0", sizeof(result_buf));
|
|
break;
|
|
case 0x0a:
|
|
/* Lancer A0 */
|
|
ocs_strncpy(result_buf, "Lancer A", sizeof(result_buf));
|
|
ocs_snprintf(tmp_buf, 2, "%d", rev_id & 0x0f);
|
|
strcat(result_buf, tmp_buf);
|
|
break;
|
|
case 0x0b:
|
|
/* Lancer B0 or D0 */
|
|
ocs_strncpy(result_buf, "Lancer", sizeof(result_buf));
|
|
ocs_snprintf(tmp_buf, 3, " %c", ((rev_id & 0xf0) >> 4) + 'A');
|
|
strcat(result_buf, tmp_buf);
|
|
ocs_snprintf(tmp_buf, 2, "%d", rev_id & 0x0f);
|
|
strcat(result_buf, tmp_buf);
|
|
break;
|
|
case 0x0c:
|
|
ocs_strncpy(result_buf, "Lancer G6", sizeof(result_buf));
|
|
break;
|
|
case 0x0f:
|
|
/* Refer to ASIC_ID */
|
|
switch(asic_gen_num) {
|
|
case 0x00:
|
|
ocs_strncpy(result_buf, "BE2", sizeof(result_buf));
|
|
break;
|
|
case 0x03:
|
|
ocs_strncpy(result_buf, "BE3-R", sizeof(result_buf));
|
|
break;
|
|
case 0x04:
|
|
ocs_strncpy(result_buf, "Skyhawk-R", sizeof(result_buf));
|
|
break;
|
|
case 0x05:
|
|
ocs_strncpy(result_buf, "Corsair", sizeof(result_buf));
|
|
break;
|
|
case 0x0b:
|
|
ocs_strncpy(result_buf, "Lancer", sizeof(result_buf));
|
|
break;
|
|
case 0x0c:
|
|
ocs_strncpy(result_buf, "LancerG6", sizeof(result_buf));
|
|
break;
|
|
default:
|
|
ocs_strncpy(result_buf, "Unknown", sizeof(result_buf));
|
|
}
|
|
if (ocs_strcmp(result_buf, "Unknown") != 0) {
|
|
ocs_snprintf(tmp_buf, 3, " %c", ((asic_rev_num & 0xf0) >> 4) + 'A');
|
|
strcat(result_buf, tmp_buf);
|
|
ocs_snprintf(tmp_buf, 2, "%d", asic_rev_num & 0x0f);
|
|
strcat(result_buf, tmp_buf);
|
|
}
|
|
break;
|
|
default:
|
|
ocs_strncpy(result_buf, "Unknown", sizeof(result_buf));
|
|
}
|
|
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "chip_type", result_buf);
|
|
|
|
}
|
|
|
|
static void
|
|
get_pci_vendor(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "pci_vendor", "0x%04x", ocs->pci_vendor);
|
|
}
|
|
|
|
static void
|
|
get_pci_device(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "pci_device", "0x%04x", ocs->pci_device);
|
|
}
|
|
|
|
static void
|
|
get_pci_subsystem_vendor(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "pci_subsystem_vendor", "0x%04x", ocs->pci_subsystem_vendor);
|
|
}
|
|
|
|
static void
|
|
get_pci_subsystem_device(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "pci_subsystem_device", "0x%04x", ocs->pci_subsystem_device);
|
|
}
|
|
|
|
static void
|
|
get_tgt_rscn_delay(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "tgt_rscn_delay", "%ld", (unsigned long)ocs->tgt_rscn_delay_msec / 1000);
|
|
}
|
|
|
|
static void
|
|
get_tgt_rscn_period(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "tgt_rscn_period", "%ld", (unsigned long)ocs->tgt_rscn_period_msec / 1000);
|
|
}
|
|
|
|
static void
|
|
get_inject_drop_cmd(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "inject_drop_cmd", "%d",
|
|
(ocs->err_injection == INJECT_DROP_CMD ? 1:0));
|
|
}
|
|
|
|
static void
|
|
get_inject_free_drop_cmd(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "inject_free_drop_cmd", "%d",
|
|
(ocs->err_injection == INJECT_FREE_DROPPED ? 1:0));
|
|
}
|
|
|
|
static void
|
|
get_inject_drop_data(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "inject_drop_data", "%d",
|
|
(ocs->err_injection == INJECT_DROP_DATA ? 1:0));
|
|
}
|
|
|
|
static void
|
|
get_inject_drop_resp(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "inject_drop_resp", "%d",
|
|
(ocs->err_injection == INJECT_DROP_RESP ? 1:0));
|
|
}
|
|
|
|
static void
|
|
get_cmd_err_inject(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "cmd_err_inject", "0x%02x", ocs->cmd_err_inject);
|
|
}
|
|
|
|
static void
|
|
get_cmd_delay_value(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "cmd_delay_value", "%ld", (unsigned long)ocs->delay_value_msec);
|
|
}
|
|
|
|
static void
|
|
get_businfo(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "businfo", ocs->businfo);
|
|
}
|
|
|
|
static void
|
|
get_sfp_a0(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint8_t *page_data;
|
|
char *buf;
|
|
int i;
|
|
int32_t bytes_read;
|
|
|
|
page_data = ocs_malloc(ocs, SFP_PAGE_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (page_data == NULL) {
|
|
return;
|
|
}
|
|
|
|
buf = ocs_malloc(ocs, (SFP_PAGE_SIZE * 3) + 1, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (buf == NULL) {
|
|
ocs_free(ocs, page_data, SFP_PAGE_SIZE);
|
|
return;
|
|
}
|
|
|
|
bytes_read = ocs_mgmt_get_sfp(ocs, 0xa0, page_data, SFP_PAGE_SIZE);
|
|
|
|
if (bytes_read <= 0) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "sfp_a0", "(unknown)");
|
|
} else {
|
|
char *d = buf;
|
|
uint8_t *s = page_data;
|
|
int buffer_remaining = (SFP_PAGE_SIZE * 3) + 1;
|
|
int bytes_added;
|
|
|
|
for (i = 0; i < bytes_read; i++) {
|
|
bytes_added = ocs_snprintf(d, buffer_remaining, "%02x ", *s);
|
|
++s;
|
|
d += bytes_added;
|
|
buffer_remaining -= bytes_added;
|
|
}
|
|
*d = '\0';
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "sfp_a0", buf);
|
|
}
|
|
|
|
ocs_free(ocs, page_data, SFP_PAGE_SIZE);
|
|
ocs_free(ocs, buf, (3 * SFP_PAGE_SIZE) + 1);
|
|
}
|
|
|
|
static void
|
|
get_sfp_a2(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint8_t *page_data;
|
|
char *buf;
|
|
int i;
|
|
int32_t bytes_read;
|
|
|
|
page_data = ocs_malloc(ocs, SFP_PAGE_SIZE, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (page_data == NULL) {
|
|
return;
|
|
}
|
|
|
|
buf = ocs_malloc(ocs, (SFP_PAGE_SIZE * 3) + 1, OCS_M_ZERO | OCS_M_NOWAIT);
|
|
if (buf == NULL) {
|
|
ocs_free(ocs, page_data, SFP_PAGE_SIZE);
|
|
return;
|
|
}
|
|
|
|
bytes_read = ocs_mgmt_get_sfp(ocs, 0xa2, page_data, SFP_PAGE_SIZE);
|
|
|
|
if (bytes_read <= 0) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "sfp_a2", "(unknown)");
|
|
} else {
|
|
char *d = buf;
|
|
uint8_t *s = page_data;
|
|
int buffer_remaining = (SFP_PAGE_SIZE * 3) + 1;
|
|
int bytes_added;
|
|
|
|
for (i=0; i < bytes_read; i++) {
|
|
bytes_added = ocs_snprintf(d, buffer_remaining, "%02x ", *s);
|
|
++s;
|
|
d += bytes_added;
|
|
buffer_remaining -= bytes_added;
|
|
}
|
|
*d = '\0';
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "sfp_a2", buf);
|
|
}
|
|
|
|
ocs_free(ocs, page_data, SFP_PAGE_SIZE);
|
|
ocs_free(ocs, buf, (3 * SFP_PAGE_SIZE) + 1);
|
|
}
|
|
|
|
static void
|
|
get_debug_mq_dump(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RW, "debug_mq_dump",
|
|
ocs_debug_is_enabled(OCS_DEBUG_ENABLE_MQ_DUMP));
|
|
}
|
|
|
|
static void
|
|
get_debug_cq_dump(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RW, "debug_cq_dump",
|
|
ocs_debug_is_enabled(OCS_DEBUG_ENABLE_CQ_DUMP));
|
|
}
|
|
|
|
static void
|
|
get_debug_wq_dump(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RW, "debug_wq_dump",
|
|
ocs_debug_is_enabled(OCS_DEBUG_ENABLE_WQ_DUMP));
|
|
}
|
|
|
|
static void
|
|
get_debug_eq_dump(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_boolean(textbuf, MGMT_MODE_RW, "debug_eq_dump",
|
|
ocs_debug_is_enabled(OCS_DEBUG_ENABLE_EQ_DUMP));
|
|
}
|
|
|
|
static void
|
|
get_logmask(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "logmask", "0x%02x", ocs->logmask);
|
|
|
|
}
|
|
|
|
static void
|
|
get_loglevel(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "loglevel", "%d", loglevel);
|
|
|
|
}
|
|
|
|
static void
|
|
get_current_speed(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&(ocs->hw), OCS_HW_LINK_SPEED, &value);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "current_speed", "%d", value);
|
|
}
|
|
|
|
static void
|
|
get_configured_speed(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&(ocs->hw), OCS_HW_LINK_CONFIG_SPEED, &value);
|
|
if (value == 0) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "configured_speed", "auto");
|
|
} else {
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "configured_speed", "%d", value);
|
|
}
|
|
|
|
}
|
|
|
|
static void
|
|
get_current_topology(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&(ocs->hw), OCS_HW_TOPOLOGY, &value);
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "current_topology", "%d", value);
|
|
|
|
}
|
|
|
|
static void
|
|
get_configured_topology(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t value;
|
|
|
|
ocs_hw_get(&(ocs->hw), OCS_HW_CONFIG_TOPOLOGY, &value);
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "configured_topology", "%d", value);
|
|
|
|
}
|
|
|
|
static void
|
|
get_current_link_state(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_xport_stats_t value;
|
|
|
|
if (ocs_xport_status(ocs->xport, OCS_XPORT_PORT_STATUS, &value) == 0) {
|
|
if (value.value == OCS_XPORT_PORT_ONLINE) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "current_link_state", "online");
|
|
} else {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "current_link_state", "offline");
|
|
}
|
|
}
|
|
}
|
|
|
|
static void
|
|
get_configured_link_state(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_xport_stats_t value;
|
|
|
|
if (ocs_xport_status(ocs->xport, OCS_XPORT_CONFIG_PORT_STATUS, &value) == 0) {
|
|
if (value.value == OCS_XPORT_PORT_ONLINE) {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "configured_link_state", "online");
|
|
} else {
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "configured_link_state", "offline");
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief HW link config enum to mgmt string value mapping.
|
|
*
|
|
* This structure provides a mapping from the ocs_hw_linkcfg_e
|
|
* enum (enum exposed for the OCS_HW_PORT_SET_LINK_CONFIG port
|
|
* control) to the mgmt string that is passed in by the mgmt application
|
|
* (elxsdkutil).
|
|
*/
|
|
typedef struct ocs_mgmt_linkcfg_map_s {
|
|
ocs_hw_linkcfg_e linkcfg;
|
|
const char *mgmt_str;
|
|
} ocs_mgmt_linkcfg_map_t;
|
|
|
|
static ocs_mgmt_linkcfg_map_t mgmt_linkcfg_map[] = {
|
|
{OCS_HW_LINKCFG_4X10G, OCS_CONFIG_LINKCFG_4X10G},
|
|
{OCS_HW_LINKCFG_1X40G, OCS_CONFIG_LINKCFG_1X40G},
|
|
{OCS_HW_LINKCFG_2X16G, OCS_CONFIG_LINKCFG_2X16G},
|
|
{OCS_HW_LINKCFG_4X8G, OCS_CONFIG_LINKCFG_4X8G},
|
|
{OCS_HW_LINKCFG_4X1G, OCS_CONFIG_LINKCFG_4X1G},
|
|
{OCS_HW_LINKCFG_2X10G, OCS_CONFIG_LINKCFG_2X10G},
|
|
{OCS_HW_LINKCFG_2X10G_2X8G, OCS_CONFIG_LINKCFG_2X10G_2X8G}};
|
|
|
|
/**
|
|
* @brief Get the HW linkcfg enum from the mgmt config string.
|
|
*
|
|
* @param mgmt_str mgmt string value.
|
|
*
|
|
* @return Returns the HW linkcfg enum corresponding to clp_str.
|
|
*/
|
|
static ocs_hw_linkcfg_e
|
|
ocs_hw_linkcfg_from_mgmt(const char *mgmt_str)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < ARRAY_SIZE(mgmt_linkcfg_map); i++) {
|
|
if (ocs_strncmp(mgmt_linkcfg_map[i].mgmt_str,
|
|
mgmt_str, ocs_strlen(mgmt_str)) == 0) {
|
|
return mgmt_linkcfg_map[i].linkcfg;
|
|
}
|
|
}
|
|
return OCS_HW_LINKCFG_NA;
|
|
}
|
|
|
|
/**
|
|
* @brief Get the mgmt string value from the HW linkcfg enum.
|
|
*
|
|
* @param linkcfg HW linkcfg enum.
|
|
*
|
|
* @return Returns the mgmt string value corresponding to the given HW linkcfg.
|
|
*/
|
|
static const char *
|
|
ocs_mgmt_from_hw_linkcfg(ocs_hw_linkcfg_e linkcfg)
|
|
{
|
|
uint32_t i;
|
|
for (i = 0; i < ARRAY_SIZE(mgmt_linkcfg_map); i++) {
|
|
if (mgmt_linkcfg_map[i].linkcfg == linkcfg) {
|
|
return mgmt_linkcfg_map[i].mgmt_str;
|
|
}
|
|
}
|
|
return OCS_CONFIG_LINKCFG_UNKNOWN;
|
|
}
|
|
|
|
/**
|
|
* @brief Link configuration callback argument
|
|
*/
|
|
typedef struct ocs_mgmt_linkcfg_arg_s {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
ocs_hw_linkcfg_e linkcfg;
|
|
} ocs_mgmt_linkcfg_arg_t;
|
|
|
|
/**
|
|
* @brief Get linkcfg config value
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param textbuf The textbuf to which the result is written.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
get_linkcfg(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
const char *linkcfg_str = NULL;
|
|
uint32_t value;
|
|
ocs_hw_linkcfg_e linkcfg;
|
|
ocs_hw_get(&ocs->hw, OCS_HW_LINKCFG, &value);
|
|
linkcfg = (ocs_hw_linkcfg_e)value;
|
|
linkcfg_str = ocs_mgmt_from_hw_linkcfg(linkcfg);
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "linkcfg", linkcfg_str);
|
|
}
|
|
|
|
/**
|
|
* @brief Get requested WWNN config value
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param textbuf The textbuf to which the result is written.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
get_req_wwnn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_xport_t *xport = ocs->xport;
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "requested_wwnn", "0x%llx", (unsigned long long)xport->req_wwnn);
|
|
}
|
|
|
|
/**
|
|
* @brief Get requested WWPN config value
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param textbuf The textbuf to which the result is written.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
get_req_wwpn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_xport_t *xport = ocs->xport;
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "requested_wwpn", "0x%llx", (unsigned long long)xport->req_wwpn);
|
|
}
|
|
|
|
/**
|
|
* @brief Get requested nodedb_mask config value
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param textbuf The textbuf to which the result is written.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
get_nodedb_mask(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RW, "nodedb_mask", "0x%08x", ocs->nodedb_mask);
|
|
}
|
|
|
|
/**
|
|
* @brief Set requested WWNN value.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param value Value to which the linkcfg is set.
|
|
*
|
|
* @return Returns 0 on success.
|
|
*/
|
|
|
|
int
|
|
set_req_wwnn(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int rc;
|
|
uint64_t wwnn;
|
|
|
|
if (ocs_strcasecmp(value, "default") == 0) {
|
|
wwnn = 0;
|
|
}
|
|
else if (parse_wwn(value, &wwnn) != 0) {
|
|
ocs_log_test(ocs, "Invalid WWNN: %s\n", value);
|
|
return 1;
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_WWNN_SET, wwnn);
|
|
|
|
if(rc) {
|
|
ocs_log_test(ocs, "OCS_XPORT_WWNN_SET failed: %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
|
|
if (rc) {
|
|
ocs_log_test(ocs, "port offline failed : %d\n", rc);
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
|
|
if (rc) {
|
|
ocs_log_test(ocs, "port online failed : %d\n", rc);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Set requested WWNP value.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param value Value to which the linkcfg is set.
|
|
*
|
|
* @return Returns 0 on success.
|
|
*/
|
|
|
|
int
|
|
set_req_wwpn(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int rc;
|
|
uint64_t wwpn;
|
|
|
|
if (ocs_strcasecmp(value, "default") == 0) {
|
|
wwpn = 0;
|
|
}
|
|
else if (parse_wwn(value, &wwpn) != 0) {
|
|
ocs_log_test(ocs, "Invalid WWPN: %s\n", value);
|
|
return 1;
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_WWPN_SET, wwpn);
|
|
|
|
if(rc) {
|
|
ocs_log_test(ocs, "OCS_XPORT_WWPN_SET failed: %d\n", rc);
|
|
return rc;
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
|
|
if (rc) {
|
|
ocs_log_test(ocs, "port offline failed : %d\n", rc);
|
|
}
|
|
|
|
rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
|
|
if (rc) {
|
|
ocs_log_test(ocs, "port online failed : %d\n", rc);
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
/**
|
|
* @brief Set node debug mask value
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param value Value to which the nodedb_mask is set.
|
|
*
|
|
* @return Returns 0 on success.
|
|
*/
|
|
static int
|
|
set_nodedb_mask(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->nodedb_mask = ocs_strtoul(value, 0, 0);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Set linkcfg config value.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Not used.
|
|
* @param value Value to which the linkcfg is set.
|
|
*
|
|
* @return Returns 0 on success.
|
|
*/
|
|
static int
|
|
set_linkcfg(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs_hw_linkcfg_e linkcfg;
|
|
ocs_mgmt_linkcfg_arg_t cb_arg;
|
|
ocs_hw_rtn_e status;
|
|
|
|
ocs_sem_init(&cb_arg.semaphore, 0, "mgmt_linkcfg");
|
|
|
|
/* translate mgmt linkcfg string to HW linkcfg enum */
|
|
linkcfg = ocs_hw_linkcfg_from_mgmt(value);
|
|
|
|
/* set HW linkcfg */
|
|
status = ocs_hw_port_control(&ocs->hw, OCS_HW_PORT_SET_LINK_CONFIG,
|
|
(uintptr_t)linkcfg, ocs_mgmt_linkcfg_cb, &cb_arg);
|
|
if (status) {
|
|
ocs_log_test(ocs, "ocs_hw_set_linkcfg failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (ocs_sem_p(&cb_arg.semaphore, OCS_SEM_FOREVER)) {
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
return -1;
|
|
}
|
|
|
|
if (cb_arg.status) {
|
|
ocs_log_test(ocs, "failed to set linkcfg from HW status=%d\n",
|
|
cb_arg.status);
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief Linkcfg callback
|
|
*
|
|
* @param status Result of the linkcfg get/set operation.
|
|
* @param value Resulting linkcfg value.
|
|
* @param arg Callback argument.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
ocs_mgmt_linkcfg_cb(int32_t status, uintptr_t value, void *arg)
|
|
{
|
|
ocs_mgmt_linkcfg_arg_t *cb_arg = (ocs_mgmt_linkcfg_arg_t *)arg;
|
|
cb_arg->status = status;
|
|
cb_arg->linkcfg = (ocs_hw_linkcfg_e)value;
|
|
ocs_sem_v(&cb_arg->semaphore);
|
|
}
|
|
|
|
static int
|
|
set_debug_mq_dump(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result;
|
|
|
|
if (ocs_strcasecmp(value, "false") == 0) {
|
|
ocs_debug_disable(OCS_DEBUG_ENABLE_MQ_DUMP);
|
|
result = 0;
|
|
} else if (ocs_strcasecmp(value, "true") == 0) {
|
|
ocs_debug_enable(OCS_DEBUG_ENABLE_MQ_DUMP);
|
|
result = 0;
|
|
} else {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
set_debug_cq_dump(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result;
|
|
|
|
if (ocs_strcasecmp(value, "false") == 0) {
|
|
ocs_debug_disable(OCS_DEBUG_ENABLE_CQ_DUMP);
|
|
result = 0;
|
|
} else if (ocs_strcasecmp(value, "true") == 0) {
|
|
ocs_debug_enable(OCS_DEBUG_ENABLE_CQ_DUMP);
|
|
result = 0;
|
|
} else {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
set_debug_wq_dump(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result;
|
|
|
|
if (ocs_strcasecmp(value, "false") == 0) {
|
|
ocs_debug_disable(OCS_DEBUG_ENABLE_WQ_DUMP);
|
|
result = 0;
|
|
} else if (ocs_strcasecmp(value, "true") == 0) {
|
|
ocs_debug_enable(OCS_DEBUG_ENABLE_WQ_DUMP);
|
|
result = 0;
|
|
} else {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
set_debug_eq_dump(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result;
|
|
|
|
if (ocs_strcasecmp(value, "false") == 0) {
|
|
ocs_debug_disable(OCS_DEBUG_ENABLE_EQ_DUMP);
|
|
result = 0;
|
|
} else if (ocs_strcasecmp(value, "true") == 0) {
|
|
ocs_debug_enable(OCS_DEBUG_ENABLE_EQ_DUMP);
|
|
result = 0;
|
|
} else {
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
set_logmask(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
|
|
ocs->logmask = ocs_strtoul(value, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_loglevel(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
|
|
loglevel = ocs_strtoul(value, NULL, 0);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int
|
|
set_configured_speed(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result = 0;
|
|
ocs_hw_rtn_e hw_rc;
|
|
int xport_rc;
|
|
uint32_t spd;
|
|
|
|
spd = ocs_strtoul(value, NULL, 0);
|
|
|
|
if ((spd != 0) && (spd != 2000) && (spd != 4000) &&
|
|
(spd != 8000) && (spd != 16000) && (spd != 32000)) {
|
|
ocs_log_test(ocs, "unsupported speed %d\n", spd);
|
|
return 1;
|
|
}
|
|
|
|
ocs_log_debug(ocs, "Taking port offline\n");
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
|
|
if (xport_rc != 0) {
|
|
ocs_log_test(ocs, "Port offline failed\n");
|
|
result = 1;
|
|
} else {
|
|
ocs_log_debug(ocs, "Setting port to speed %d\n", spd);
|
|
hw_rc = ocs_hw_set(&ocs->hw, OCS_HW_LINK_SPEED, spd);
|
|
if (hw_rc != OCS_HW_RTN_SUCCESS) {
|
|
ocs_log_test(ocs, "Speed set failed\n");
|
|
result = 1;
|
|
}
|
|
|
|
/* If we failed to set the speed we still want to try to bring
|
|
* the port back online */
|
|
|
|
ocs_log_debug(ocs, "Bringing port online\n");
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
|
|
if (xport_rc != 0) {
|
|
result = 1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
int
|
|
set_configured_topology(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result = 0;
|
|
ocs_hw_rtn_e hw_rc;
|
|
int xport_rc;
|
|
uint32_t topo;
|
|
|
|
topo = ocs_strtoul(value, NULL, 0);
|
|
if (topo >= OCS_HW_TOPOLOGY_NONE) {
|
|
return 1;
|
|
}
|
|
|
|
ocs_log_debug(ocs, "Taking port offline\n");
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
|
|
if (xport_rc != 0) {
|
|
ocs_log_test(ocs, "Port offline failed\n");
|
|
result = 1;
|
|
} else {
|
|
ocs_log_debug(ocs, "Setting port to topology %d\n", topo);
|
|
hw_rc = ocs_hw_set(&ocs->hw, OCS_HW_TOPOLOGY, topo);
|
|
if (hw_rc != OCS_HW_RTN_SUCCESS) {
|
|
ocs_log_test(ocs, "Topology set failed\n");
|
|
result = 1;
|
|
}
|
|
|
|
/* If we failed to set the topology we still want to try to bring
|
|
* the port back online */
|
|
|
|
ocs_log_debug(ocs, "Bringing port online\n");
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
|
|
if (xport_rc != 0) {
|
|
result = 1;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int
|
|
set_configured_link_state(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
int result = 0;
|
|
int xport_rc;
|
|
|
|
if (ocs_strcasecmp(value, "offline") == 0) {
|
|
ocs_log_debug(ocs, "Setting port to %s\n", value);
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_OFFLINE);
|
|
if (xport_rc != 0) {
|
|
ocs_log_test(ocs, "Setting port to offline failed\n");
|
|
result = -1;
|
|
}
|
|
} else if (ocs_strcasecmp(value, "online") == 0) {
|
|
ocs_log_debug(ocs, "Setting port to %s\n", value);
|
|
xport_rc = ocs_xport_control(ocs->xport, OCS_XPORT_PORT_ONLINE);
|
|
if (xport_rc != 0) {
|
|
ocs_log_test(ocs, "Setting port to online failed\n");
|
|
result = -1;
|
|
}
|
|
} else {
|
|
ocs_log_test(ocs, "Unsupported link state \"%s\"\n", value);
|
|
result = -1;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
typedef struct ocs_mgmt_get_port_protocol_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
ocs_hw_port_protocol_e port_protocol;
|
|
} ocs_mgmt_get_port_protocol_result_t;
|
|
|
|
|
|
static void
|
|
ocs_mgmt_get_port_protocol_cb(int32_t status,
|
|
ocs_hw_port_protocol_e port_protocol,
|
|
void *arg)
|
|
{
|
|
ocs_mgmt_get_port_protocol_result_t *result = arg;
|
|
|
|
result->status = status;
|
|
result->port_protocol = port_protocol;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
static void
|
|
get_port_protocol(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_get_port_protocol_result_t result;
|
|
uint8_t bus;
|
|
uint8_t dev;
|
|
uint8_t func;
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "get_port_protocol");
|
|
|
|
ocs_get_bus_dev_func(ocs, &bus, &dev, &func);
|
|
|
|
if(ocs_hw_get_port_protocol(&ocs->hw, func, ocs_mgmt_get_port_protocol_cb, &result) == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
}
|
|
if (result.status == 0) {
|
|
switch (result.port_protocol) {
|
|
case OCS_HW_PORT_PROTOCOL_ISCSI:
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "port_protocol", "iSCSI");
|
|
break;
|
|
case OCS_HW_PORT_PROTOCOL_FCOE:
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "port_protocol", "FCoE");
|
|
break;
|
|
case OCS_HW_PORT_PROTOCOL_FC:
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "port_protocol", "FC");
|
|
break;
|
|
case OCS_HW_PORT_PROTOCOL_OTHER:
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "port_protocol", "Other");
|
|
break;
|
|
}
|
|
} else {
|
|
ocs_log_test(ocs, "getting port profile status 0x%x\n", result.status);
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "port_protocol", "Unknown");
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct ocs_mgmt_set_port_protocol_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
} ocs_mgmt_set_port_protocol_result_t;
|
|
|
|
|
|
|
|
static void
|
|
ocs_mgmt_set_port_protocol_cb(int32_t status,
|
|
void *arg)
|
|
{
|
|
ocs_mgmt_get_port_protocol_result_t *result = arg;
|
|
|
|
result->status = status;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
/**
|
|
* @brief Set port protocol
|
|
* @par Description
|
|
* This is a management action handler to set the current
|
|
* port protocol. Input value should be one of iSCSI,
|
|
* FC, or FCoE.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param value The value to be assigned
|
|
*
|
|
* @return Returns 0 on success, non-zero on failure.
|
|
*/
|
|
static int32_t
|
|
set_port_protocol(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs_mgmt_set_port_protocol_result_t result;
|
|
int32_t rc = 0;
|
|
ocs_hw_port_protocol_e new_protocol;
|
|
uint8_t bus;
|
|
uint8_t dev;
|
|
uint8_t func;
|
|
|
|
ocs_get_bus_dev_func(ocs, &bus, &dev, &func);
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "set_port_protocol");
|
|
|
|
if (ocs_strcasecmp(value, "iscsi") == 0) {
|
|
new_protocol = OCS_HW_PORT_PROTOCOL_ISCSI;
|
|
} else if (ocs_strcasecmp(value, "fc") == 0) {
|
|
new_protocol = OCS_HW_PORT_PROTOCOL_FC;
|
|
} else if (ocs_strcasecmp(value, "fcoe") == 0) {
|
|
new_protocol = OCS_HW_PORT_PROTOCOL_FCOE;
|
|
} else {
|
|
return -1;
|
|
}
|
|
|
|
rc = ocs_hw_set_port_protocol(&ocs->hw, new_protocol, func,
|
|
ocs_mgmt_set_port_protocol_cb, &result);
|
|
if (rc == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
rc = -ENXIO;
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. */
|
|
rc = 0;
|
|
} else {
|
|
rc = -1;
|
|
ocs_log_test(ocs, "setting active profile status 0x%x\n",
|
|
result.status);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
typedef struct ocs_mgmt_get_profile_list_result_s {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
ocs_hw_profile_list_t *list;
|
|
} ocs_mgmt_get_profile_list_result_t;
|
|
|
|
static void
|
|
ocs_mgmt_get_profile_list_cb(int32_t status, ocs_hw_profile_list_t *list, void *ul_arg)
|
|
{
|
|
ocs_mgmt_get_profile_list_result_t *result = ul_arg;
|
|
|
|
result->status = status;
|
|
result->list = list;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
/**
|
|
* @brief Get list of profiles
|
|
* @par Description
|
|
* This is a management action handler to get the list of
|
|
* profiles supported by the SLI port. Although the spec says
|
|
* that all SLI platforms support this, only Skyhawk actually
|
|
* has a useful implementation.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void
|
|
get_profile_list(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
ocs_mgmt_get_profile_list_result_t result;
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "get_profile_list");
|
|
|
|
if(ocs_hw_get_profile_list(&ocs->hw, ocs_mgmt_get_profile_list_cb, &result) == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. */
|
|
#define MAX_LINE_SIZE 520
|
|
#define BUFFER_SIZE MAX_LINE_SIZE*40
|
|
char *result_buf;
|
|
char result_line[MAX_LINE_SIZE];
|
|
uint32_t bytes_left;
|
|
uint32_t i;
|
|
|
|
result_buf = ocs_malloc(ocs, BUFFER_SIZE, OCS_M_ZERO);
|
|
bytes_left = BUFFER_SIZE;
|
|
|
|
for (i=0; i<result.list->num_descriptors; i++) {
|
|
sprintf(result_line, "0x%02x:%s\n", result.list->descriptors[i].profile_id,
|
|
result.list->descriptors[i].profile_description);
|
|
if (strlen(result_line) < bytes_left) {
|
|
strcat(result_buf, result_line);
|
|
bytes_left -= strlen(result_line);
|
|
}
|
|
}
|
|
|
|
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RD, "profile_list", result_buf);
|
|
|
|
ocs_free(ocs, result_buf, BUFFER_SIZE);
|
|
ocs_free(ocs, result.list, sizeof(ocs_hw_profile_list_t));
|
|
} else {
|
|
ocs_log_test(ocs, "getting profile list status 0x%x\n", result.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct ocs_mgmt_get_active_profile_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
uint32_t active_profile_id;
|
|
} ocs_mgmt_get_active_profile_result_t;
|
|
|
|
static void
|
|
ocs_mgmt_get_active_profile_cb(int32_t status, uint32_t active_profile, void *ul_arg)
|
|
{
|
|
ocs_mgmt_get_active_profile_result_t *result = ul_arg;
|
|
|
|
result->status = status;
|
|
result->active_profile_id = active_profile;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
#define MAX_PROFILE_LENGTH 5
|
|
|
|
/**
|
|
* @brief Get active profile
|
|
* @par Description
|
|
* This is a management action handler to get the currently
|
|
* active profile for an SLI port. Although the spec says that
|
|
* all SLI platforms support this, only Skyhawk actually has a
|
|
* useful implementation.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void
|
|
get_active_profile(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
char result_string[MAX_PROFILE_LENGTH];
|
|
ocs_mgmt_get_active_profile_result_t result;
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "get_active_profile");
|
|
|
|
if(ocs_hw_get_active_profile(&ocs->hw, ocs_mgmt_get_active_profile_cb, &result) == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. */
|
|
sprintf(result_string, "0x%02x", result.active_profile_id);
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "active_profile", result_string);
|
|
} else {
|
|
ocs_log_test(ocs, "getting active profile status 0x%x\n", result.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
typedef struct ocs_mgmt_set_active_profile_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
} ocs_mgmt_set_active_profile_result_t;
|
|
|
|
|
|
static void
|
|
ocs_mgmt_set_active_profile_cb(int32_t status, void *ul_arg)
|
|
{
|
|
ocs_mgmt_get_profile_list_result_t *result = ul_arg;
|
|
|
|
result->status = status;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
/**
|
|
* @brief Set active profile
|
|
* @par Description
|
|
* This is a management action handler to set the currently
|
|
* active profile for an SLI port. Although the spec says that
|
|
* all SLI platforms support this, only Skyhawk actually has a
|
|
* useful implementation.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param value Requested new value of the property.
|
|
*
|
|
* @return Returns 0 on success, non-zero on failure.
|
|
*/
|
|
static int32_t
|
|
set_active_profile(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs_mgmt_set_active_profile_result_t result;
|
|
int32_t rc = 0;
|
|
int32_t new_profile;
|
|
|
|
new_profile = ocs_strtoul(value, NULL, 0);
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "set_active_profile");
|
|
|
|
rc = ocs_hw_set_active_profile(&ocs->hw, ocs_mgmt_set_active_profile_cb, new_profile, &result);
|
|
if (rc == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
rc = -ENXIO;
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. */
|
|
rc = 0;
|
|
} else {
|
|
rc = -1;
|
|
ocs_log_test(ocs, "setting active profile status 0x%x\n", result.status);
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
typedef struct ocs_mgmt_get_nvparms_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
uint8_t wwpn[8];
|
|
uint8_t wwnn[8];
|
|
uint8_t hard_alpa;
|
|
uint32_t preferred_d_id;
|
|
} ocs_mgmt_get_nvparms_result_t;
|
|
|
|
static void
|
|
ocs_mgmt_get_nvparms_cb(int32_t status, uint8_t *wwpn, uint8_t *wwnn, uint8_t hard_alpa,
|
|
uint32_t preferred_d_id, void *ul_arg)
|
|
{
|
|
ocs_mgmt_get_nvparms_result_t *result = ul_arg;
|
|
|
|
result->status = status;
|
|
ocs_memcpy(result->wwpn, wwpn, sizeof(result->wwpn));
|
|
ocs_memcpy(result->wwnn, wwnn, sizeof(result->wwnn));
|
|
result->hard_alpa = hard_alpa;
|
|
result->preferred_d_id = preferred_d_id;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
/**
|
|
* @brief Get wwpn
|
|
* @par Description
|
|
*
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void
|
|
get_nv_wwpn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
char result_string[24];
|
|
static ocs_mgmt_get_nvparms_result_t result;
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "get_nv_wwpn");
|
|
|
|
if(ocs_hw_get_nvparms(&ocs->hw, ocs_mgmt_get_nvparms_cb, &result) == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
return;
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. Copy wwpn from result struct to result string */
|
|
sprintf(result_string, "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
result.wwpn[0], result.wwpn[1], result.wwpn[2],
|
|
result.wwpn[3], result.wwpn[4], result.wwpn[5],
|
|
result.wwpn[6], result.wwpn[7]);
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "nv_wwpn", result_string);
|
|
} else {
|
|
ocs_log_test(ocs, "getting wwpn status 0x%x\n", result.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get wwnn
|
|
* @par Description
|
|
*
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return none
|
|
*/
|
|
static void
|
|
get_nv_wwnn(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
char result_string[24];
|
|
static ocs_mgmt_get_nvparms_result_t result;
|
|
|
|
ocs_sem_init(&(result.semaphore), 0, "get_nv_wwnn");
|
|
|
|
if(ocs_hw_get_nvparms(&ocs->hw, ocs_mgmt_get_nvparms_cb, &result) == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
return;
|
|
}
|
|
if (result.status == 0) {
|
|
/* Success. Copy wwnn from result struct to result string */
|
|
ocs_snprintf(result_string, sizeof(result_string), "%02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x",
|
|
result.wwnn[0], result.wwnn[1], result.wwnn[2],
|
|
result.wwnn[3], result.wwnn[4], result.wwnn[5],
|
|
result.wwnn[6], result.wwnn[7]);
|
|
ocs_mgmt_emit_string(textbuf, MGMT_MODE_RW, "nv_wwnn", result_string);
|
|
} else {
|
|
ocs_log_test(ocs, "getting wwnn status 0x%x\n", result.status);
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @brief Get accumulated node abort counts
|
|
* @par Description Get the sum of all nodes abort count.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param textbuf Pointer to an ocs_textbuf, which is used to return the results.
|
|
*
|
|
* @return None.
|
|
*/
|
|
static void
|
|
get_node_abort_cnt(ocs_t *ocs, char *name, ocs_textbuf_t *textbuf)
|
|
{
|
|
uint32_t abort_counts = 0;
|
|
ocs_domain_t *domain;
|
|
ocs_sport_t *sport;
|
|
ocs_node_t *node;
|
|
|
|
if (ocs_device_lock_try(ocs) != TRUE) {
|
|
/* Didn't get the lock */
|
|
return;
|
|
}
|
|
|
|
/* Here the Device lock is held */
|
|
ocs_list_foreach(&ocs->domain_list, domain) {
|
|
if (ocs_domain_lock_try(domain) != TRUE) {
|
|
/* Didn't get the lock */
|
|
ocs_device_unlock(ocs);
|
|
return;
|
|
}
|
|
|
|
/* Here the Domain lock is held */
|
|
ocs_list_foreach(&domain->sport_list, sport) {
|
|
if (ocs_sport_lock_try(sport) != TRUE) {
|
|
/* Didn't get the lock */
|
|
ocs_domain_unlock(domain);
|
|
ocs_device_unlock(ocs);
|
|
return;
|
|
}
|
|
|
|
/* Here the sport lock is held */
|
|
ocs_list_foreach(&sport->node_list, node) {
|
|
abort_counts += node->abort_cnt;
|
|
}
|
|
|
|
ocs_sport_unlock(sport);
|
|
}
|
|
|
|
ocs_domain_unlock(domain);
|
|
}
|
|
|
|
ocs_device_unlock(ocs);
|
|
|
|
ocs_mgmt_emit_int(textbuf, MGMT_MODE_RD, "node_abort_cnt", "%d" , abort_counts);
|
|
}
|
|
|
|
typedef struct ocs_mgmt_set_nvparms_result {
|
|
ocs_sem_t semaphore;
|
|
int32_t status;
|
|
} ocs_mgmt_set_nvparms_result_t;
|
|
|
|
|
|
static void
|
|
ocs_mgmt_set_nvparms_cb(int32_t status, void *ul_arg)
|
|
{
|
|
ocs_mgmt_get_profile_list_result_t *result = ul_arg;
|
|
|
|
result->status = status;
|
|
|
|
ocs_sem_v(&(result->semaphore));
|
|
}
|
|
|
|
/**
|
|
* @brief Set wwn
|
|
* @par Description Sets the Non-volatile worldwide names,
|
|
* if provided.
|
|
*
|
|
* @param ocs Pointer to the ocs structure.
|
|
* @param name Name of the action being performed.
|
|
* @param wwn_p Requested new WWN values.
|
|
*
|
|
* @return Returns 0 on success, non-zero on failure.
|
|
*/
|
|
static int32_t
|
|
set_nv_wwn(ocs_t *ocs, char *name, char *wwn_p)
|
|
{
|
|
ocs_mgmt_get_nvparms_result_t result;
|
|
uint8_t new_wwpn[8];
|
|
uint8_t new_wwnn[8];
|
|
char *wwpn_p = NULL;
|
|
char *wwnn_p = NULL;
|
|
int32_t rc = -1;
|
|
int wwpn;
|
|
int wwnn;
|
|
int i;
|
|
|
|
/* This is a read-modify-write operation, so first we have to read
|
|
* the current values
|
|
*/
|
|
ocs_sem_init(&(result.semaphore), 0, "set_nv_wwn1");
|
|
|
|
rc = ocs_hw_get_nvparms(&ocs->hw, ocs_mgmt_get_nvparms_cb, &result);
|
|
|
|
if (rc == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
return -ENXIO;
|
|
}
|
|
if (result.status != 0) {
|
|
ocs_log_test(ocs, "getting nvparms status 0x%x\n", result.status);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* wwn_p contains wwpn_p@wwnn_p values */
|
|
if (wwn_p != NULL) {
|
|
wwpn_p = ocs_strsep(&wwn_p, "@");
|
|
wwnn_p = wwn_p;
|
|
}
|
|
|
|
wwpn = ocs_strcmp(wwpn_p, "NA");
|
|
wwnn = ocs_strcmp(wwnn_p, "NA");
|
|
|
|
/* Parse the new WWPN */
|
|
if ((wwpn_p != NULL) && (wwpn != 0)) {
|
|
if (ocs_sscanf(wwpn_p, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
|
|
&(new_wwpn[0]), &(new_wwpn[1]), &(new_wwpn[2]),
|
|
&(new_wwpn[3]), &(new_wwpn[4]), &(new_wwpn[5]),
|
|
&(new_wwpn[6]), &(new_wwpn[7])) != 8) {
|
|
ocs_log_test(ocs, "can't parse WWPN %s\n", wwpn_p);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Parse the new WWNN */
|
|
if ((wwnn_p != NULL) && (wwnn != 0 )) {
|
|
if (ocs_sscanf(wwnn_p, "%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx:%2hhx",
|
|
&(new_wwnn[0]), &(new_wwnn[1]), &(new_wwnn[2]),
|
|
&(new_wwnn[3]), &(new_wwnn[4]), &(new_wwnn[5]),
|
|
&(new_wwnn[6]), &(new_wwnn[7])) != 8) {
|
|
ocs_log_test(ocs, "can't parse WWNN %s\n", wwnn_p);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
/* Use active wwpn, if new one is not provided */
|
|
if (wwpn == 0) {
|
|
new_wwpn[i] = result.wwpn[i];
|
|
}
|
|
|
|
/* Use active wwnn, if new one is not provided */
|
|
if (wwnn == 0) {
|
|
new_wwnn[i] = result.wwnn[i];
|
|
}
|
|
}
|
|
|
|
/* Modify the nv_wwnn and nv_wwpn, then write it back */
|
|
ocs_sem_init(&(result.semaphore), 0, "set_nv_wwn2");
|
|
|
|
rc = ocs_hw_set_nvparms(&ocs->hw, ocs_mgmt_set_nvparms_cb, new_wwpn,
|
|
new_wwnn, result.hard_alpa, result.preferred_d_id,
|
|
&result);
|
|
if (rc == OCS_HW_RTN_SUCCESS) {
|
|
if (ocs_sem_p(&(result.semaphore), OCS_SEM_FOREVER) != 0) {
|
|
/* Undefined failure */
|
|
ocs_log_err(ocs, "ocs_sem_p failed\n");
|
|
return -ENXIO;
|
|
}
|
|
if (result.status != 0) {
|
|
ocs_log_test(ocs, "setting wwn status 0x%x\n", result.status);
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int
|
|
set_tgt_rscn_delay(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->tgt_rscn_delay_msec = ocs_strtoul(value, NULL, 0) * 1000;
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_tgt_rscn_period(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->tgt_rscn_period_msec = ocs_strtoul(value, NULL, 0) * 1000;
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_inject_drop_cmd(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->err_injection = (ocs_strtoul(value, NULL, 0) == 0 ? NO_ERR_INJECT : INJECT_DROP_CMD);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_inject_free_drop_cmd(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->err_injection = (ocs_strtoul(value, NULL, 0) == 0 ? NO_ERR_INJECT : INJECT_FREE_DROPPED);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_inject_drop_data(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->err_injection = (ocs_strtoul(value, NULL, 0) == 0 ? NO_ERR_INJECT : INJECT_DROP_DATA);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_inject_drop_resp(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->err_injection = (ocs_strtoul(value, NULL, 0) == 0 ? NO_ERR_INJECT : INJECT_DROP_RESP);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_cmd_err_inject(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->cmd_err_inject = ocs_strtoul(value, NULL, 0);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
static int
|
|
set_cmd_delay_value(ocs_t *ocs, char *name, char *value)
|
|
{
|
|
ocs->delay_value_msec = ocs_strtoul(value, NULL, 0);
|
|
ocs->err_injection = (ocs->delay_value_msec == 0 ? NO_ERR_INJECT : INJECT_DELAY_CMD);
|
|
ocs_log_debug(ocs, "mgmt set: %s %s\n", name, value);
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* @brief parse a WWN from a string into a 64-bit value
|
|
*
|
|
* Given a pointer to a string, parse the string into a 64-bit
|
|
* WWN value. The format of the string must be xx:xx:xx:xx:xx:xx:xx:xx
|
|
*
|
|
* @param wwn_in pointer to the string to be parsed
|
|
* @param wwn_out pointer to uint64_t in which to put the parsed result
|
|
*
|
|
* @return 0 if successful, non-zero if the WWN is malformed and couldn't be parsed
|
|
*/
|
|
int
|
|
parse_wwn(char *wwn_in, uint64_t *wwn_out)
|
|
{
|
|
uint8_t byte0;
|
|
uint8_t byte1;
|
|
uint8_t byte2;
|
|
uint8_t byte3;
|
|
uint8_t byte4;
|
|
uint8_t byte5;
|
|
uint8_t byte6;
|
|
uint8_t byte7;
|
|
int rc;
|
|
|
|
rc = ocs_sscanf(wwn_in, "0x%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx%2hhx",
|
|
&byte0, &byte1, &byte2, &byte3,
|
|
&byte4, &byte5, &byte6, &byte7);
|
|
|
|
if (rc == 8) {
|
|
*wwn_out = ((uint64_t)byte0 << 56) |
|
|
((uint64_t)byte1 << 48) |
|
|
((uint64_t)byte2 << 40) |
|
|
((uint64_t)byte3 << 32) |
|
|
((uint64_t)byte4 << 24) |
|
|
((uint64_t)byte5 << 16) |
|
|
((uint64_t)byte6 << 8) |
|
|
((uint64_t)byte7);
|
|
return 0;
|
|
|
|
} else {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
static char *mode_string(int mode);
|
|
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate the beginning of a numbered section in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function begins a section. The XML information is appended to
|
|
* the textbuf. This form of the function is used for sections that might have
|
|
* multiple instances, such as a node or a SLI Port (sport). The index number
|
|
* is appended to the name.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param name Name of the section.
|
|
* @param index Index number of this instance of the section.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
extern void ocs_mgmt_start_section(ocs_textbuf_t *textbuf, const char *name, int index)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<%s instance=\"%d\">\n", name, index);
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate the beginning of an unnumbered section in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function begins a section. The XML information is appended to
|
|
* the textbuf. This form of the function is used for sections that have
|
|
* a single instance only. Therefore, no index number is needed.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param name Name of the section.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
extern void ocs_mgmt_start_unnumbered_section(ocs_textbuf_t *textbuf, const char *name)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<%s>\n", name);
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate the end of a section in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function ends a section. The XML information is appended to
|
|
* the textbuf.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param name Name of the section.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void ocs_mgmt_end_unnumbered_section(ocs_textbuf_t *textbuf, const char *name)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "</%s>\n", name);
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate the indexed end of a section in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function ends a section. The XML information is appended to
|
|
* the textbuf.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param name Name of the section.
|
|
* @param index Index number of this instance of the section.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void ocs_mgmt_end_section(ocs_textbuf_t *textbuf, const char *name, int index)
|
|
{
|
|
|
|
ocs_textbuf_printf(textbuf, "</%s>\n", name);
|
|
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate a property, with no value, in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function generates a property name. The XML information is appended to
|
|
* the textbuf. This form of the function is used by the list functions
|
|
* when the property name only (and not the current value) is given.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param mode Defines whether the property is read(r)/write(w)/executable(x).
|
|
* @param name Name of the property.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void ocs_mgmt_emit_property_name(ocs_textbuf_t *textbuf, int mode, const char *name)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<%s mode=\"%s\"/>\n", name, mode_string(mode));
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate a property with a string value in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function generates a property name and a string value.
|
|
* The XML information is appended to the textbuf.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param mode Defines whether the property is read(r)/write(w)/executable(x).
|
|
* @param name Name of the property.
|
|
* @param value Value of the property.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void ocs_mgmt_emit_string(ocs_textbuf_t *textbuf, int mode, const char *name, const char *value)
|
|
{
|
|
ocs_textbuf_printf(textbuf, "<%s mode=\"%s\">%s</%s>\n", name, mode_string(mode), value, name);
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate a property with an integer value in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function generates a property name and an integer value.
|
|
* The XML information is appended to the textbuf.
|
|
*
|
|
* @param textbuf Pointer to driver dump text buffer.
|
|
* @param mode Defines whether the property is read(r)/write(w)/executable(x).
|
|
* @param name Name of the property.
|
|
* @param fmt A printf format for formatting the integer value.
|
|
*
|
|
* @return none
|
|
*/
|
|
|
|
void ocs_mgmt_emit_int(ocs_textbuf_t *textbuf, int mode, const char *name, const char *fmt, ...)
|
|
{
|
|
va_list ap;
|
|
char valuebuf[64];
|
|
|
|
va_start(ap, fmt);
|
|
ocs_vsnprintf(valuebuf, sizeof(valuebuf), fmt, ap);
|
|
va_end(ap);
|
|
|
|
ocs_textbuf_printf(textbuf, "<%s mode=\"%s\">%s</%s>\n", name, mode_string(mode), valuebuf, name);
|
|
}
|
|
|
|
/**
|
|
* @ingroup mgmt
|
|
* @brief Generate a property with a boolean value in a management XML document.
|
|
*
|
|
* @par Description
|
|
* This function generates a property name and a boolean value.
|
|
* The XML information is appended to the textbuf.
|
|
*
|
|
* @param textbuf Pointer to the driver dump text buffer.
|
|
* @param mode Defines whether the property is read(r)/write(w)/executable(x).
|
|
* @param name Name of the property.
|
|
* @param value Boolean value to be added to the textbuf.
|
|
*
|
|
* @return None.
|
|
*/
|
|
|
|
void ocs_mgmt_emit_boolean(ocs_textbuf_t *textbuf, int mode, const char *name, int value)
|
|
{
|
|
char *valuebuf = value ? "true" : "false";
|
|
|
|
ocs_textbuf_printf(textbuf, "<%s mode=\"%s\">%s</%s>\n", name, mode_string(mode), valuebuf, name);
|
|
}
|
|
|
|
static char *mode_string(int mode)
|
|
{
|
|
static char mode_str[4];
|
|
|
|
mode_str[0] = '\0';
|
|
if (mode & MGMT_MODE_RD) {
|
|
strcat(mode_str, "r");
|
|
}
|
|
if (mode & MGMT_MODE_WR) {
|
|
strcat(mode_str, "w");
|
|
}
|
|
if (mode & MGMT_MODE_EX) {
|
|
strcat(mode_str, "x");
|
|
}
|
|
|
|
return mode_str;
|
|
|
|
}
|