/*
* Copyright (c) 2008, Intel Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU Lesser General Public License,
* version 2.1, as published by the Free Software Foundation.
*
* This program is distributed in the hope it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
* for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include "utils.h"
#include "api_lib.h"
#include "adapt_impl.h"
static struct sa_table adapter_table;
static const u_int32_t adapter_handle_offset = 0x100;
#define HBA_SHORT_NAME_LIMIT 64
/*
* Support for adapter information.
*/
HBA_UINT32
adapter_get_count(void)
{
return adapter_table.st_limit;
}
/*
* Get adapter name.
*/
HBA_STATUS
adapter_get_name(HBA_UINT32 index, char *buf)
{
HBA_STATUS status;
struct adapter_info *ap;
status = HBA_STATUS_ERROR_ILLEGAL_INDEX;
ap = sa_table_lookup(&adapter_table, index);
if (ap != NULL) {
snprintf(buf, HBA_SHORT_NAME_LIMIT,
"%s-%u", ap->ad_name, index);
status = HBA_STATUS_OK;
}
return status;
}
/*
* Add an adapter to the table.
*/
HBA_STATUS
adapter_create(struct adapter_info *ap)
{
int index;
index = sa_table_append(&adapter_table, ap);
if (index < 0)
return HBA_STATUS_ERROR;
ap->ad_index = index;
return HBA_STATUS_OK;
}
void
adapter_destroy(struct adapter_info *ap)
{
sa_table_destroy_all(&ap->ad_ports);
free(ap);
}
void
adapter_destroy_all(void)
{
struct adapter_info *ap;
int i;
for (i = 0; i < adapter_table.st_limit; i++) {
ap = adapter_table.st_table[i];
if (ap) {
adapter_table.st_table[i] = NULL;
adapter_destroy(ap);
}
}
sa_table_destroy(&adapter_table);
}
struct adapter_info *
adapter_open_handle(HBA_HANDLE handle)
{
return sa_table_lookup(&adapter_table, handle -
adapter_handle_offset);
}
struct port_info *
adapter_get_port(HBA_HANDLE handle, HBA_UINT32 port)
{
struct adapter_info *ap;
struct port_info *pp = NULL;
ap = adapter_open_handle(handle);
if (ap)
pp = sa_table_lookup(&ap->ad_ports, port);
return pp;
}
struct port_info *
adapter_get_rport(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 rport)
{
struct port_info *pp;
struct port_info *rp = NULL;
pp = adapter_get_port(handle, port);
if (pp) {
get_rport_info(pp);
rp = sa_table_lookup(&pp->ap_rports, rport);
}
return rp;
}
/*
* Get the Nth discovered port information.
*/
struct port_info *
adapter_get_rport_n(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 n)
{
struct port_info *pp;
struct port_info *rp = NULL;
pp = adapter_get_port(handle, port);
if (pp) {
get_rport_info(pp);
rp = sa_table_lookup_n(&pp->ap_rports, n);
}
return rp;
}
static void *
adapter_target_match(void *rp_arg, void *target_arg)
{
struct port_info *rp = rp_arg;
if (rp->ap_scsi_target != *(u_int32_t *)target_arg)
rp_arg = NULL;
return rp_arg;
}
/*
* Get the rport by scsi_target number.
*/
struct port_info *
adapter_get_rport_target(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 n)
{
struct port_info *pp;
struct port_info *rp = NULL;
pp = adapter_get_port(handle, port);
if (pp) {
get_rport_info(pp);
rp = sa_table_search(&pp->ap_rports,
adapter_target_match, &n);
}
return rp;
}
static void *
adapter_wwpn_match(void *rp_arg, void *wwpn_arg)
{
struct port_info *rp = rp_arg;
if (memcmp(&rp->ap_attr.PortWWN, wwpn_arg, sizeof(HBA_WWN)) != 0)
rp_arg = NULL;
return rp_arg;
}
struct port_info *
adapter_get_rport_by_wwn(struct port_info *pp, HBA_WWN wwpn)
{
struct port_info *rp;
get_rport_info(pp);
rp = sa_table_search(&pp->ap_rports, adapter_wwpn_match, &wwpn);
return rp;
}
static void *
adapter_fcid_match(void *rp_arg, void *fcid_arg)
{
struct port_info *rp = rp_arg;
if (rp->ap_attr.PortFcId != *(fc_fid_t *)fcid_arg)
rp_arg = NULL;
return rp_arg;
}
struct port_info *
adapter_get_rport_by_fcid(struct port_info *pp, fc_fid_t fcid)
{
struct port_info *rp;
get_rport_info(pp);
rp = sa_table_search(&pp->ap_rports, adapter_fcid_match, &fcid);
return rp;
}
/*
* Open adapter by name.
*/
HBA_HANDLE
adapter_open(char *name)
{
char buf[256];
HBA_HANDLE i;
HBA_STATUS status;
for (i = 0; i < adapter_table.st_limit; i++) {
status = adapter_get_name(i, buf);
if (status != HBA_STATUS_OK)
return 0;
if (!strcmp(buf, name))
return adapter_handle_offset + i;
}
return 0;
}
/*
* Get port by WWPN.
* Returns NULL if WWN not unique.
* If countp is non-NULL, the int it points to will be set to the
* number found so that the caller can tell if the WWN was ambiguous.
*/
struct port_info *
adapter_get_port_by_wwn(HBA_HANDLE handle, HBA_WWN wwn, int *countp)
{
struct adapter_info *ap;
struct port_info *pp_found = NULL;
struct port_info *pp;
int count = 0;
int p;
ap = adapter_open_handle(handle);
if (ap != NULL) {
for (p = 0; p < ap->ad_ports.st_limit; p++) {
pp = ap->ad_ports.st_table[p];
if (pp &&
!memcmp(&pp->ap_attr.PortWWN, &wwn, sizeof(wwn))) {
count++;
pp_found = pp;
}
}
}
if (count > 1)
pp_found = NULL;
if (countp != NULL)
*countp = count;
return pp_found;
}
/*
* Open adapter by WWN.
*/
HBA_STATUS
adapter_open_by_wwn(HBA_HANDLE *phandle, HBA_WWN wwn)
{
struct adapter_info *ap;
struct port_info *pp;
HBA_HANDLE found_handle = 0;
int count = 0;
HBA_STATUS status;
int i;
int p;
for (i = 0; i < adapter_table.st_limit; i++) {
ap = adapter_table.st_table[i];
if (!ap)
continue;
if (memcmp(&ap->ad_attr.NodeWWN, &wwn, sizeof(wwn)) == 0) {
count++;
found_handle = ap->ad_index + adapter_handle_offset;
} else {
for (p = 0; p < ap->ad_ports.st_limit; p++) {
pp = ap->ad_ports.st_table[p];
if (!pp)
continue;
if (memcmp(&pp->ap_attr.PortWWN,
&wwn, sizeof(wwn)) == 0) {
count++;
found_handle = ap->ad_index +
adapter_handle_offset;
}
}
}
}
*phandle = HBA_HANDLE_INVALID;
if (count == 1) {
status = HBA_STATUS_OK;
*phandle = found_handle;
} else if (count > 1) {
status = HBA_STATUS_ERROR_AMBIGUOUS_WWN;
} else {
status = HBA_STATUS_ERROR_ILLEGAL_WWN;
}
return status;
}
/*
* Close adapter.
*/
void
adapter_close(HBA_HANDLE handle)
{
}
/*
* Get adapter attributes.
*/
HBA_STATUS
adapter_get_attr(HBA_HANDLE handle, HBA_ADAPTERATTRIBUTES *pattr)
{
struct adapter_info *ap;
ap = adapter_open_handle(handle);
if (ap) {
*pattr = ap->ad_attr; /* struct copy */
return HBA_STATUS_OK;
}
return HBA_STATUS_ERROR;
}
/*
* Get adapter port attributes.
*/
HBA_STATUS
adapter_get_port_attr(HBA_HANDLE handle, HBA_UINT32 port,
HBA_PORTATTRIBUTES *pattr)
{
struct port_info *pp;
pp = adapter_get_port(handle, port);
if (pp) {
*pattr = pp->ap_attr; /* struct copy */
return HBA_STATUS_OK;
}
return HBA_STATUS_ERROR;
}
/*
* Get discovered (remote) port attributes.
*/
HBA_STATUS
adapter_get_rport_attr(HBA_HANDLE handle, HBA_UINT32 port, HBA_UINT32 rport,
HBA_PORTATTRIBUTES *pattr)
{
struct port_info *rp;
rp = adapter_get_rport_n(handle, port, rport);
if (rp) {
*pattr = rp->ap_attr; /* struct copy */
return HBA_STATUS_OK;
}
return HBA_STATUS_ERROR;
}
/*
* Get adapter port attributes.
*/
HBA_STATUS
adapter_get_port_attr_by_wwn(HBA_HANDLE handle, HBA_WWN wwn,
HBA_PORTATTRIBUTES *pattr)
{
struct adapter_info *ap;
struct port_info *pp;
struct port_info *pp_found = NULL;
u_int32_t p;
int count = 0;
HBA_STATUS status;
ap = adapter_open_handle(handle);
if (ap != NULL) {
for (p = 0; p < ap->ad_ports.st_limit; p++) {
pp = ap->ad_ports.st_table[p];
if (pp == NULL)
continue;
if (!memcmp(&pp->ap_attr.PortWWN, &wwn, sizeof(wwn))) {
count++;
pp_found = pp;
}
pp = sa_table_search(&pp->ap_rports,
adapter_wwpn_match,
&wwn);
if (pp) {
count++;
pp_found = pp;
}
}
}
pp = pp_found;
if (pp != NULL) {
if (count > 1) {
status = HBA_STATUS_ERROR_AMBIGUOUS_WWN;
} else {
*pattr = pp->ap_attr; /* struct copy */
status = HBA_STATUS_OK;
}
} else {
status = HBA_STATUS_ERROR_ILLEGAL_WWN;
}
return status;
}