root/usr/src/lib/sun_fc/common/HBAList.cc
/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License (the "License").
 * You may not use this file except in compliance with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
 * Use is subject to license terms.
 */



#include "HBAList.h"
#include "Exceptions.h"
#include "Trace.h"
#include "sun_fc_version.h"
#include <string>
#include <sstream>
#include "FCHBA.h"
#include "TgtFCHBA.h"
#include <cstring>
#include <climits>
#include <cstdlib>

using namespace std;

/**
 * @memo            Private constructor (used to create singleton instance)
 * @see             HBAList::instance
 */
HBAList::HBAList() { }

/**
 * Internal singleton instance
 */
HBAList* HBAList::_instance = 0;

/**
 * Max number of adapters that this class supports.
 */
const int32_t HBAList::HBA_MAX_PER_LIST = INT_MAX;

/**
 * @memo            Free up resources held by this HBA list
 * @postcondition   All memory used by this list will be freed
 * @return          HBA_STATUS_OK on success
 * 
 */
HBA_STATUS HBAList::unload() {
        Trace log("HBAList::unload");
        lock();
        _instance = NULL;
        unlock();
        return (HBA_STATUS_OK);
}

/**
 * @memo            Fetch the singleton instance
 * @return          The singleton instance
 * 
 * @doc             Only one instance of HBAList must be present
 *                  per address space at a time.  The singleton design pattern
 *                  is used to enforce this behavior.
 */
HBAList* HBAList::instance() {
        Trace log("HBAList::instance");
        if (_instance == 0) {
            _instance = new HBAList();
        }
        return (_instance);
}

/**
 * @memo            Fetch an HBA based on name.
 *                  Always returns  non-null or throw an Exception.
 * @precondition    HBAs must be loaded in the list
 * @postcondition   A handle will be opened.  The caller must close the handle
 *                  at some later time to prevent leakage.
 * @exception       BadArgumentException if the name is not properly formatted
 * @exception       IllegalIndexException if the name does not match any
 *                  present HBAs within this list.
 * @return          A valid handle for future API calls
 * @param           name The name of the HBA to open
 * 
 * @doc             This routine will always return a handle (ie, non null)
 *                  or will throw an exception.
 */
Handle* HBAList::openHBA(string name) {
        Trace log("HBAList::openHBA(name)");
        int index = -1;
        try {
            string::size_type offset = name.find_last_of("-");
            if (offset >= 0) {
                string indexString = name.substr(offset+1);
                index = atoi(indexString.c_str());
            }
        } catch (...) {
            throw BadArgumentException();
        }
        lock();
        if (index < 0 || index > hbas.size()) {
            unlock();
            throw IllegalIndexException();
        } else {
            HBA *tmp = hbas[index];
            unlock();
            tmp->validatePresent();
            return (new Handle(tmp));
        }
}

/**
 * @memo            Fetch an target mode FC HBA based on name.
 *                  Always returns  non-null or throw an Exception.
 * @precondition    Target mode HBAs must be loaded in the list
 * @postcondition   A handle will be opened.  The caller must close the handle
 *                  at some later time to prevent leakage.
 * @exception       BadArgumentException if the name is not properly formatted
 * @exception       IllegalIndexException if the name does not match any
 *                  present HBAs within this list.
 * @return          A valid handle for future API calls
 * @param           name The name of the target mode HBA to open
 * 
 * @doc             This routine will always return a handle (ie, non null)
 *                  or will throw an exception.
 */
Handle* HBAList::openTgtHBA(string name) {
        Trace log("HBAList::openHBA(name)");
        int index = -1;
        try {
            string::size_type offset = name.find_last_of("-");
            if (offset >= 0) {
                string indexString = name.substr(offset+1);
                index = atoi(indexString.c_str());
            }
        } catch (...) {
            throw BadArgumentException();
        }
        lock();
        if (index < 0 || index > tgthbas.size()) {
            unlock();
            throw IllegalIndexException();
        } else {
            HBA *tmp = tgthbas[index];
            unlock();
            tmp->validatePresent();
            return (new Handle(tmp));
        }
}

/**
 * @memo            Get the name of an HBA at the given index
 * @precondition    HBAs must be loaded in the list
 * @exception       IllegalIndexException Thrown if the index doesn't match any
 *                  HBA in the list
 * @return          The name of the specified HBA
 * @param           index The zero based index of the desired HBA
 * 
 */
string HBAList::getHBAName(int index) {
        Trace log("HBAList::getHBAName");
        lock();
        if (index < 0 || index > hbas.size()) {
            unlock();
            throw IllegalIndexException();
        } else {
            HBA *tmp = hbas[index];
            unlock();
            tmp->validatePresent();
            char buf[128];
            snprintf(buf, 128, "%s-%d", tmp->getName().c_str(), index);
            string name = buf;
            return (name);
        }
}

/**
 * @memo            Get the name of an target mode HBA at the given index
 * @precondition    Target mode HBAs must be loaded in the list
 * @exception       IllegalIndexException Thrown if the index doesn't match any
 *                  HBA in the list
 * @return          The name of the specified target mode HBA
 * @param           index The zero based index of the desired target mode HBA
 * 
 */
string HBAList::getTgtHBAName(int index) {
        Trace log("HBAList::getTgtHBAName");
        lock();
        if (index < 0 || index > tgthbas.size()) {
            unlock();
            throw IllegalIndexException();
        } else {
            HBA *tmp = tgthbas[index];
            unlock();
            tmp->validatePresent();
            char buf[128];
            snprintf(buf, 128, "%s-%d", tmp->getName().c_str(), index);
            string name = buf;
            return (name);
        }
}

/**
 * @memo            Open an HBA based on a WWN
 * @precondition    HBAs must be loaded in the list
 * @postcondition   A handle will be opened.  The caller must close the handle
 *                  at some later time to prevent leakage.
 * @exception       IllegalWWNException Thrown if the wwn doesn't match any
 *                  HBA in the list
 * @return          A valid Handle for later use by API calls
 * @param           wwn The node or any port WWN of HBA to open
 * @see             HBA::containsWWN
 * 
 * @doc             This routine will accept both Node and Port WWNs based
 *                  on the HBA routine containsWWN
 */
Handle* HBAList::openHBA(uint64_t wwn) {

        Trace log("HBAList::openHBA(wwn)");
        lock();
        HBA *tmp;
        for (int i = 0; i < hbas.size(); i++) {
            if (hbas[i]->containsWWN(wwn)) {
                tmp = hbas[i];
                unlock();
                tmp->validatePresent();
                return (new Handle(tmp));
            }
        }
        unlock();
        throw IllegalWWNException();
}

/**
 * @memo            Open an target mode HBA based on a WWN
 * @precondition    Targee mode HBAs must be loaded in the list
 * @postcondition   A handle will be opened.  The caller must close the handle
 *                  at some later time to prevent leakage.
 * @exception       IllegalWWNException Thrown if the wwn doesn't match any
 *                  target mode HBA in the list
 * @return          A valid Handle for later use by API calls
 * @param           The node WWN or any port WWN of target mode HBA to open
 * @see             HBA::containsWWN
 * 
 * @doc             This routine will accept both Node and Port WWNs based
 *                  on the HBA routine containsWWN
 */
Handle* HBAList::openTgtHBA(uint64_t wwn) {

        Trace log("HBAList::openTgtHBA(wwn)");
        lock();
        HBA *tmp;
        for (int i = 0; i < tgthbas.size(); i++) {
            if (tgthbas[i]->containsWWN(wwn)) {
                tmp = tgthbas[i];
                unlock();
                tmp->validatePresent();
                return (new Handle(tmp));
            }
        }
        unlock();
        throw IllegalWWNException();
}

/**
 * @memo            Get the number of adapters present in the list
 * @postcondition   List of HBAs will be loaded
 * @exception       ... Underlying exceptions will be thrown
 * @return          The number of adapters in the list
 * 
 * @doc             This routine will triger discovery of HBAs on the system.
 *                  It will also handle addition/removal of HBAs in the list
 *                  based on dynamic reconfiguration operations.  The max 
 *                  number of HBAs that HBA API supports is up to the
 *                  uint32_t size.  VSL supports up to int32_t size thus
 *                  it gives enough room for the HBA API library
 *                  to handle up to max uint32_t number if adapters. 
 */
int HBAList::getNumberofAdapters() {
        Trace log("HBAList::getNumberofAdapters");
        lock();

        try {
        if (hbas.size() == 0) {
            // First pass, just store them all blindly
            FCHBA::loadAdapters(hbas);
        } else {
            // Second pass, do the update operation
            vector<HBA*> tmp;
            FCHBA::loadAdapters(tmp);
            bool matched;
            for (int i = 0; i < tmp.size(); i++) {
                matched = false;
                for (int j = 0; j < hbas.size(); j++) {
                    if (*tmp[i] == *hbas[j]) {
                        matched = true;
                        break;
                    }
                }
                if (matched) {
                    delete (tmp[i]);
                } else {
                    hbas.insert(hbas.end(), tmp[i]);
                }
            }
        }
        } catch (...) {
            unlock();
            throw;
        }

        unlock();

        // When there is more than HBA_MAX_PER_LIST(= int32_max)
        // VSL returns an error so it is safe to cast it here.
        return ((uint32_t)hbas.size());
}

/**
 * @memo            Get the number of target mode adapters present in the list
 * @postcondition   List of TgtHBAs will be loaded
 * @exception       ... Underlying exceptions will be thrown
 * @return          The number of target mode adapters in the list
 * 
 * @doc             This routine will triger discovery of Target mode HBAs on
 *                  the system. It will also handle addition/removal of Target
 *                  mode HBAs in the list based on dynamic reconfiguration 
 *                  operations. The max number of target mode HBAs that
 *                  HBA API supports is up to the
 *                  uint32_t size.  VSL supports up to int32_t size thus
 *                  it gives enough room for the HBA API library
 *                  to handle up to max uint32_t number of adapters. 
 */
int HBAList::getNumberofTgtAdapters() {
        Trace log("HBAList::getNumberofTgtAdapters");
        lock();

        try {
            if (tgthbas.size() == 0) {
                // First pass, just store them all blindly
                TgtFCHBA::loadAdapters(tgthbas);
            } else {
                // Second pass, do the update operation
                vector<HBA*> tmp;
                TgtFCHBA::loadAdapters(tmp);
                bool matched;
                for (int i = 0; i < tmp.size(); i++) {
                    matched = false;
                    for (int j = 0; j < tgthbas.size(); j++) {
                        if (*tmp[i] == *tgthbas[j]) {
                            matched = true;
                            break;
                        }
                    }
                    if (matched) {
                        delete (tmp[i]);
                    } else {
                        tgthbas.insert(tgthbas.end(), tmp[i]);
                    }
                }
            }
        } catch (...) {
            unlock();
            throw;
        }

        unlock();

        // When there is more than HBA_MAX_PER_LIST(= int32_max)
        // VSL returns an error so it is safe to cast it here.
        return ((uint32_t)tgthbas.size());
}

/**
 * @memo            Load the list
 * @return          HBA_STATUS_OK
 * 
 * @doc             Currently this routine is a no-op and may be a cantidate
 *                  for removal in the future.
 */
HBA_STATUS HBAList::load() {
        Trace log("HBAList::load");

        // No lock is required since no VSL specific action requried.
        return (HBA_STATUS_OK);
}

/**
 * @memo            Free up resources
 */
HBAList::~HBAList() {
        Trace log("HBAList::~HBAList");
        for (int i = 0; i < hbas.size(); i++) {
            delete (hbas[i]);
        }
        for (int i = 0; i < tgthbas.size(); i++) {
            delete (tgthbas[i]);
        }
}

HBA_LIBRARYATTRIBUTES HBAList::getVSLAttributes() {
        HBA_LIBRARYATTRIBUTES attrs;
        char    build_time[] = BUILD_TIME;
        attrs.final = 0;
        memset(&attrs, 0, sizeof(attrs));
        strlcpy(attrs.VName, VSL_NAME, sizeof (attrs.VName));
        strlcpy(attrs.VVersion, VSL_STRING_VERSION, sizeof (attrs.VVersion));
        strptime(build_time, "%c", &attrs.build_date);

        return (attrs);
}