94cba8034b
libifconfig_sfp.h provides an API in libifconfig for querying SFP module properties, operational status, and vendor strings, as well as descriptions of the various fields, string conversions, and other useful helpers for implementing user interfaces. SFP module status is obtained by reading registers via an I2C interface. Descriptions of these registers and the values therein have been collected in a Lua table which is used to generate all the boilerplace C headers and source files for accessing these values, their names, and descriptions. The generated code is fully commented and readable. This is the first use of libifconfig in ifconfig itself. For now, the scope remains very limited. Over time, more of ifconfig will be replaced with libifconfig. Some minor changes to the formatting of ifconfig output have been made: - Module memory hex dumps are indented one extra space as a result of using hexdump(3) instead of a bespoke hex dump function. - Media descriptions have an added two-character short-name in parenthesis. - QSFP modules were incorrectly displaying TX bias current as power. Now TX channels display bias current, and this change has been made for both SFP and QSFP modules for consistency. A Lua binding for libifconfig including this functionality is implemented but has not been included in this commit. The plan is for it to be committed after dynamic module loading has been enabled in flua. Reviewed by: kp, melifaro Relnotes: yes Differential Revision: https://reviews.freebsd.org/D25494
593 lines
14 KiB
C
593 lines
14 KiB
C
/*-
|
||
* Copyright (c) 2014, Alexander V. Chernikov
|
||
* Copyright (c) 2020, Ryan Moeller <freqlabs@FreeBSD.org>
|
||
*
|
||
* 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.
|
||
*
|
||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 AUTHOR 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$
|
||
*/
|
||
|
||
#include <sys/types.h>
|
||
#include <sys/param.h>
|
||
#include <sys/ioctl.h>
|
||
#include <sys/socket.h>
|
||
|
||
#include <net/if.h>
|
||
#include <net/sff8436.h>
|
||
#include <net/sff8472.h>
|
||
|
||
#include <math.h>
|
||
#include <err.h>
|
||
#include <errno.h>
|
||
#include <fcntl.h>
|
||
#include <stdbool.h>
|
||
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <unistd.h>
|
||
|
||
#include <libifconfig.h>
|
||
#include <libifconfig_internal.h>
|
||
#include <libifconfig_sfp.h>
|
||
#include <libifconfig_sfp_tables_internal.h>
|
||
|
||
#define SFF_8636_EXT_COMPLIANCE 0x80
|
||
|
||
struct i2c_info {
|
||
struct ifreq ifr;
|
||
ifconfig_handle_t *h;
|
||
int error; /* Store first error */
|
||
enum sfp_id id; /* Module type */
|
||
};
|
||
|
||
static uint8_t
|
||
find_zero_bit(const struct sfp_enum_metadata *table, int value, int sz)
|
||
{
|
||
int v, m;
|
||
|
||
for (v = 1, m = 1 << (8 * sz); v < m; v <<= 1) {
|
||
if ((value & v) == 0)
|
||
continue;
|
||
if (find_metadata(table, value & v) != NULL) {
|
||
return (value & v);
|
||
}
|
||
}
|
||
return (0);
|
||
}
|
||
|
||
/*
|
||
* Reads i2c data from opened kernel socket.
|
||
*/
|
||
static int
|
||
read_i2c(struct i2c_info *ii, uint8_t addr, uint8_t off, uint8_t len,
|
||
uint8_t *buf)
|
||
{
|
||
struct ifi2creq req;
|
||
int i, l;
|
||
|
||
if (ii->error != 0)
|
||
return (ii->error);
|
||
|
||
ii->ifr.ifr_data = (caddr_t)&req;
|
||
|
||
i = 0;
|
||
l = 0;
|
||
memset(&req, 0, sizeof(req));
|
||
req.dev_addr = addr;
|
||
req.offset = off;
|
||
req.len = len;
|
||
|
||
while (len > 0) {
|
||
l = MIN(sizeof(req.data), len);
|
||
req.len = l;
|
||
if (ifconfig_ioctlwrap(ii->h, AF_LOCAL, SIOCGI2C,
|
||
&ii->ifr) != 0) {
|
||
ii->error = errno;
|
||
return (errno);
|
||
}
|
||
|
||
memcpy(&buf[i], req.data, l);
|
||
len -= l;
|
||
i += l;
|
||
req.offset += l;
|
||
}
|
||
|
||
return (0);
|
||
}
|
||
|
||
static int
|
||
i2c_info_init(struct i2c_info *ii, ifconfig_handle_t *h, const char *name)
|
||
{
|
||
uint8_t id_byte;
|
||
|
||
memset(ii, 0, sizeof(*ii));
|
||
strlcpy(ii->ifr.ifr_name, name, sizeof(ii->ifr.ifr_name));
|
||
ii->h = h;
|
||
|
||
/*
|
||
* Try to read byte 0 from i2c:
|
||
* Both SFF-8472 and SFF-8436 use it as
|
||
* 'identification byte'.
|
||
* Stop reading status on zero as value -
|
||
* this might happen in case of empty transceiver slot.
|
||
*/
|
||
id_byte = 0;
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &id_byte);
|
||
if (ii->error != 0)
|
||
return (-1);
|
||
if (id_byte == 0) {
|
||
h->error.errtype = OTHER;
|
||
h->error.errcode = ENOENT;
|
||
return (-1);
|
||
}
|
||
ii->id = id_byte;
|
||
return (0);
|
||
}
|
||
|
||
static int
|
||
get_sfp_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp)
|
||
{
|
||
uint8_t code;
|
||
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_ID, 1, &sfp->sfp_id);
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_CONNECTOR, 1, &sfp->sfp_conn);
|
||
|
||
/* Use extended compliance code if it's valid */
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS, 1, &sfp->sfp_eth_ext);
|
||
if (sfp->sfp_eth_ext == 0) {
|
||
/* Next, check 10G Ethernet/IB CCs */
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START, 1, &code);
|
||
sfp->sfp_eth_10g = find_zero_bit(sfp_eth_10g_table, code, 1);
|
||
if (sfp->sfp_eth_10g == 0) {
|
||
/* No match. Try Ethernet 1G */
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_TRANS_START + 3,
|
||
1, &code);
|
||
sfp->sfp_eth = find_zero_bit(sfp_eth_table, code, 1);
|
||
}
|
||
}
|
||
|
||
return (ii->error);
|
||
}
|
||
|
||
static int
|
||
get_qsfp_info(struct i2c_info *ii, struct ifconfig_sfp_info *sfp)
|
||
{
|
||
uint8_t code;
|
||
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_ID, 1, &sfp->sfp_id);
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_CONNECTOR, 1, &sfp->sfp_conn);
|
||
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_STATUS, 1, &sfp->sfp_rev);
|
||
|
||
/* Check for extended specification compliance */
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_CODE_E1040100G, 1, &code);
|
||
if (code & SFF_8636_EXT_COMPLIANCE) {
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_OPTIONS_START, 1,
|
||
&sfp->sfp_eth_ext);
|
||
} else {
|
||
/* Check 10/40G Ethernet class only */
|
||
sfp->sfp_eth_1040g =
|
||
find_zero_bit(sfp_eth_1040g_table, code, 1);
|
||
}
|
||
|
||
return (ii->error);
|
||
}
|
||
|
||
int
|
||
ifconfig_sfp_get_sfp_info(ifconfig_handle_t *h,
|
||
const char *name, struct ifconfig_sfp_info *sfp)
|
||
{
|
||
struct i2c_info ii;
|
||
char buf[8];
|
||
|
||
memset(sfp, 0, sizeof(*sfp));
|
||
|
||
if (i2c_info_init(&ii, h, name) != 0)
|
||
return (-1);
|
||
|
||
/* Read bytes 3-10 at once */
|
||
read_i2c(&ii, SFF_8472_BASE, SFF_8472_TRANS_START, 8, buf);
|
||
if (ii.error != 0)
|
||
return (ii.error);
|
||
|
||
/* Check 10G ethernet first */
|
||
sfp->sfp_eth_10g = find_zero_bit(sfp_eth_10g_table, buf[0], 1);
|
||
if (sfp->sfp_eth_10g == 0) {
|
||
/* No match. Try 1G */
|
||
sfp->sfp_eth = find_zero_bit(sfp_eth_table, buf[3], 1);
|
||
}
|
||
sfp->sfp_fc_len = find_zero_bit(sfp_fc_len_table, buf[4], 1);
|
||
sfp->sfp_fc_media = find_zero_bit(sfp_fc_media_table, buf[6], 1);
|
||
sfp->sfp_fc_speed = find_zero_bit(sfp_fc_speed_table, buf[7], 1);
|
||
sfp->sfp_cab_tech =
|
||
find_zero_bit(sfp_cab_tech_table, (buf[4] << 8) | buf[5], 2);
|
||
|
||
if (ifconfig_sfp_id_is_qsfp(ii.id))
|
||
return (get_qsfp_info(&ii, sfp));
|
||
return (get_sfp_info(&ii, sfp));
|
||
}
|
||
|
||
static size_t
|
||
channel_count(enum sfp_id id)
|
||
{
|
||
/* TODO: other ids */
|
||
switch (id) {
|
||
case SFP_ID_UNKNOWN:
|
||
return (0);
|
||
case SFP_ID_QSFP:
|
||
case SFP_ID_QSFPPLUS:
|
||
case SFP_ID_QSFP28:
|
||
return (4);
|
||
default:
|
||
return (1);
|
||
}
|
||
}
|
||
|
||
size_t
|
||
ifconfig_sfp_channel_count(const struct ifconfig_sfp_info *sfp)
|
||
{
|
||
return (channel_count(sfp->sfp_id));
|
||
}
|
||
|
||
/*
|
||
* Print SFF-8472/SFF-8436 string to supplied buffer.
|
||
* All (vendor-specific) strings are padded right with '0x20'.
|
||
*/
|
||
static void
|
||
get_sff_string(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst)
|
||
{
|
||
read_i2c(ii, addr, off, SFF_VENDOR_STRING_SIZE, dst);
|
||
dst += SFF_VENDOR_STRING_SIZE;
|
||
do { *dst-- = '\0'; } while (*dst == 0x20);
|
||
}
|
||
|
||
static void
|
||
get_sff_date(struct i2c_info *ii, uint8_t addr, uint8_t off, char *dst)
|
||
{
|
||
char buf[SFF_VENDOR_DATE_SIZE];
|
||
|
||
read_i2c(ii, addr, off, SFF_VENDOR_DATE_SIZE, buf);
|
||
sprintf(dst, "20%c%c-%c%c-%c%c", buf[0], buf[1], buf[2], buf[3],
|
||
buf[4], buf[5]);
|
||
}
|
||
|
||
static int
|
||
get_sfp_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi)
|
||
{
|
||
get_sff_string(ii, SFF_8472_BASE, SFF_8472_VENDOR_START, vi->name);
|
||
get_sff_string(ii, SFF_8472_BASE, SFF_8472_PN_START, vi->pn);
|
||
get_sff_string(ii, SFF_8472_BASE, SFF_8472_SN_START, vi->sn);
|
||
get_sff_date(ii, SFF_8472_BASE, SFF_8472_DATE_START, vi->date);
|
||
return (ii->error);
|
||
}
|
||
|
||
static int
|
||
get_qsfp_vendor_info(struct i2c_info *ii, struct ifconfig_sfp_vendor_info *vi)
|
||
{
|
||
get_sff_string(ii, SFF_8436_BASE, SFF_8436_VENDOR_START, vi->name);
|
||
get_sff_string(ii, SFF_8436_BASE, SFF_8436_PN_START, vi->pn);
|
||
get_sff_string(ii, SFF_8436_BASE, SFF_8436_SN_START, vi->sn);
|
||
get_sff_date(ii, SFF_8436_BASE, SFF_8436_DATE_START, vi->date);
|
||
return (ii->error);
|
||
}
|
||
|
||
int
|
||
ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h,
|
||
const char *name, struct ifconfig_sfp_vendor_info *vi)
|
||
{
|
||
struct i2c_info ii;
|
||
|
||
memset(vi, 0, sizeof(*vi));
|
||
|
||
if (i2c_info_init(&ii, h, name) != 0)
|
||
return (-1);
|
||
|
||
if (ifconfig_sfp_id_is_qsfp(ii.id))
|
||
return (get_qsfp_vendor_info(&ii, vi));
|
||
return (get_sfp_vendor_info(&ii, vi));
|
||
}
|
||
|
||
/*
|
||
* Converts internal temperature (SFF-8472, SFF-8436)
|
||
* 16-bit unsigned value to human-readable representation:
|
||
*
|
||
* Internally measured Module temperature are represented
|
||
* as a 16-bit signed twos complement value in increments of
|
||
* 1/256 degrees Celsius, yielding a total range of –128C to +128C
|
||
* that is considered valid between –40 and +125C.
|
||
*/
|
||
static double
|
||
get_sff_temp(struct i2c_info *ii, uint8_t addr, uint8_t off)
|
||
{
|
||
double d;
|
||
uint8_t buf[2];
|
||
|
||
read_i2c(ii, addr, off, 2, buf);
|
||
d = (double)buf[0];
|
||
d += (double)buf[1] / 256;
|
||
return (d);
|
||
}
|
||
|
||
/*
|
||
* Retrieves supplied voltage (SFF-8472, SFF-8436).
|
||
* 16-bit usigned value, treated as range 0..+6.55 Volts
|
||
*/
|
||
static double
|
||
get_sff_voltage(struct i2c_info *ii, uint8_t addr, uint8_t off)
|
||
{
|
||
double d;
|
||
uint8_t buf[2];
|
||
|
||
read_i2c(ii, addr, off, 2, buf);
|
||
d = (double)((buf[0] << 8) | buf[1]);
|
||
return (d / 10000);
|
||
}
|
||
|
||
/*
|
||
* The following conversions assume internally-calibrated data.
|
||
* This is always true for SFF-8346, and explicitly checked for SFF-8472.
|
||
*/
|
||
|
||
double
|
||
power_mW(uint16_t power)
|
||
{
|
||
/* Power is specified in units of 0.1 uW. */
|
||
return (1.0 * power / 10000);
|
||
}
|
||
|
||
double
|
||
power_dBm(uint16_t power)
|
||
{
|
||
return (10.0 * log10(power_mW(power)));
|
||
}
|
||
|
||
double
|
||
bias_mA(uint16_t bias)
|
||
{
|
||
/* Bias current is specified in units of 2 uA. */
|
||
return (1.0 * bias / 500);
|
||
}
|
||
|
||
static uint16_t
|
||
get_sff_channel(struct i2c_info *ii, uint8_t addr, uint8_t off)
|
||
{
|
||
uint8_t buf[2];
|
||
|
||
read_i2c(ii, addr, off, 2, buf);
|
||
if (ii->error != 0)
|
||
return (0);
|
||
|
||
return ((buf[0] << 8) + buf[1]);
|
||
}
|
||
|
||
static int
|
||
get_sfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss)
|
||
{
|
||
uint8_t diag_type, flags;
|
||
|
||
/* Read diagnostic monitoring type */
|
||
read_i2c(ii, SFF_8472_BASE, SFF_8472_DIAG_TYPE, 1, (caddr_t)&diag_type);
|
||
if (ii->error != 0)
|
||
return (-1);
|
||
|
||
/*
|
||
* Read monitoring data IFF it is supplied AND is
|
||
* internally calibrated
|
||
*/
|
||
flags = SFF_8472_DDM_DONE | SFF_8472_DDM_INTERNAL;
|
||
if ((diag_type & flags) != flags) {
|
||
ii->h->error.errtype = OTHER;
|
||
ii->h->error.errcode = ENXIO;
|
||
return (-1);
|
||
}
|
||
|
||
ss->temp = get_sff_temp(ii, SFF_8472_DIAG, SFF_8472_TEMP);
|
||
ss->voltage = get_sff_voltage(ii, SFF_8472_DIAG, SFF_8472_VCC);
|
||
ss->channel = calloc(channel_count(ii->id), sizeof(*ss->channel));
|
||
if (ss->channel == NULL) {
|
||
ii->h->error.errtype = OTHER;
|
||
ii->h->error.errcode = ENOMEM;
|
||
return (-1);
|
||
}
|
||
ss->channel[0].rx = get_sff_channel(ii, SFF_8472_DIAG, SFF_8472_RX_POWER);
|
||
ss->channel[0].tx = get_sff_channel(ii, SFF_8472_DIAG, SFF_8472_TX_BIAS);
|
||
return (ii->error);
|
||
}
|
||
|
||
static uint32_t
|
||
get_qsfp_bitrate(struct i2c_info *ii)
|
||
{
|
||
uint8_t code;
|
||
uint32_t rate;
|
||
|
||
code = 0;
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8436_BITRATE, 1, &code);
|
||
rate = code * 100;
|
||
if (code == 0xFF) {
|
||
read_i2c(ii, SFF_8436_BASE, SFF_8636_BITRATE, 1, &code);
|
||
rate = code * 250;
|
||
}
|
||
|
||
return (rate);
|
||
}
|
||
|
||
static int
|
||
get_qsfp_status(struct i2c_info *ii, struct ifconfig_sfp_status *ss)
|
||
{
|
||
size_t channels;
|
||
|
||
ss->temp = get_sff_temp(ii, SFF_8436_BASE, SFF_8436_TEMP);
|
||
ss->voltage = get_sff_voltage(ii, SFF_8436_BASE, SFF_8436_VCC);
|
||
channels = channel_count(ii->id);
|
||
ss->channel = calloc(channels, sizeof(*ss->channel));
|
||
if (ss->channel == NULL) {
|
||
ii->h->error.errtype = OTHER;
|
||
ii->h->error.errcode = ENOMEM;
|
||
return (-1);
|
||
}
|
||
for (size_t chan = 0; chan < channels; ++chan) {
|
||
uint8_t rxoffs = SFF_8436_RX_CH1_MSB + chan * sizeof(uint16_t);
|
||
uint8_t txoffs = SFF_8436_TX_CH1_MSB + chan * sizeof(uint16_t);
|
||
ss->channel[chan].rx =
|
||
get_sff_channel(ii, SFF_8436_BASE, rxoffs);
|
||
ss->channel[chan].tx =
|
||
get_sff_channel(ii, SFF_8436_BASE, txoffs);
|
||
}
|
||
ss->bitrate = get_qsfp_bitrate(ii);
|
||
return (ii->error);
|
||
}
|
||
|
||
int
|
||
ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name,
|
||
struct ifconfig_sfp_status *ss)
|
||
{
|
||
struct i2c_info ii;
|
||
|
||
memset(ss, 0, sizeof(*ss));
|
||
|
||
if (i2c_info_init(&ii, h, name) != 0)
|
||
return (-1);
|
||
|
||
if (ifconfig_sfp_id_is_qsfp(ii.id))
|
||
return (get_qsfp_status(&ii, ss));
|
||
return (get_sfp_status(&ii, ss));
|
||
}
|
||
|
||
void
|
||
ifconfig_sfp_free_sfp_status(struct ifconfig_sfp_status *ss)
|
||
{
|
||
if (ss != NULL)
|
||
free(ss->channel);
|
||
}
|
||
|
||
static const char *
|
||
sfp_id_string_alt(uint8_t value)
|
||
{
|
||
const char *id;
|
||
|
||
if (value <= SFF_8024_ID_LAST)
|
||
id = sff_8024_id[value];
|
||
else if (value > 0x80)
|
||
id = "Vendor specific";
|
||
else
|
||
id = "Reserved";
|
||
|
||
return (id);
|
||
}
|
||
|
||
static const char *
|
||
sfp_conn_string_alt(uint8_t value)
|
||
{
|
||
const char *conn;
|
||
|
||
if (value >= 0x0D && value <= 0x1F)
|
||
conn = "Unallocated";
|
||
else if (value >= 0x24 && value <= 0x7F)
|
||
conn = "Unallocated";
|
||
else
|
||
conn = "Vendor specific";
|
||
|
||
return (conn);
|
||
}
|
||
|
||
void
|
||
ifconfig_sfp_get_sfp_info_strings(const struct ifconfig_sfp_info *sfp,
|
||
struct ifconfig_sfp_info_strings *strings)
|
||
{
|
||
get_sfp_info_strings(sfp, strings);
|
||
if (strings->sfp_id == NULL)
|
||
strings->sfp_id = sfp_id_string_alt(sfp->sfp_id);
|
||
if (strings->sfp_conn == NULL)
|
||
strings->sfp_conn = sfp_conn_string_alt(sfp->sfp_conn);
|
||
if (strings->sfp_rev == NULL)
|
||
strings->sfp_rev = "Unallocated";
|
||
}
|
||
|
||
const char *
|
||
ifconfig_sfp_physical_spec(const struct ifconfig_sfp_info *sfp,
|
||
const struct ifconfig_sfp_info_strings *strings)
|
||
{
|
||
switch (sfp->sfp_id) {
|
||
case SFP_ID_UNKNOWN:
|
||
break;
|
||
case SFP_ID_QSFP:
|
||
case SFP_ID_QSFPPLUS:
|
||
case SFP_ID_QSFP28:
|
||
if (sfp->sfp_eth_1040g & SFP_ETH_1040G_EXTENDED)
|
||
return (strings->sfp_eth_ext);
|
||
else if (sfp->sfp_eth_1040g)
|
||
return (strings->sfp_eth_1040g);
|
||
break;
|
||
default:
|
||
if (sfp->sfp_eth_ext)
|
||
return (strings->sfp_eth_ext);
|
||
else if (sfp->sfp_eth_10g)
|
||
return (strings->sfp_eth_10g);
|
||
else if (sfp->sfp_eth)
|
||
return (strings->sfp_eth);
|
||
break;
|
||
}
|
||
return ("Unknown");
|
||
}
|
||
|
||
int
|
||
ifconfig_sfp_get_sfp_dump(ifconfig_handle_t *h, const char *name,
|
||
struct ifconfig_sfp_dump *dump)
|
||
{
|
||
struct i2c_info ii;
|
||
uint8_t *buf = dump->data;
|
||
|
||
memset(dump->data, 0, sizeof(dump->data));
|
||
|
||
if (i2c_info_init(&ii, h, name) != 0)
|
||
return (-1);
|
||
|
||
if (ifconfig_sfp_id_is_qsfp(ii.id)) {
|
||
read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP0_START, QSFP_DUMP0_SIZE,
|
||
buf + QSFP_DUMP0_START);
|
||
read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP1_START, QSFP_DUMP1_SIZE,
|
||
buf + QSFP_DUMP1_START);
|
||
} else {
|
||
read_i2c(&ii, SFF_8472_BASE, SFP_DUMP_START, SFP_DUMP_SIZE,
|
||
buf + SFP_DUMP_START);
|
||
}
|
||
|
||
return (ii.error != 0 ? -1 : 0);
|
||
}
|
||
|
||
size_t
|
||
ifconfig_sfp_dump_region_count(const struct ifconfig_sfp_dump *dp)
|
||
{
|
||
uint8_t id_byte = dp->data[0];
|
||
|
||
switch ((enum sfp_id)id_byte) {
|
||
case SFP_ID_UNKNOWN:
|
||
return (0);
|
||
case SFP_ID_QSFP:
|
||
case SFP_ID_QSFPPLUS:
|
||
case SFP_ID_QSFP28:
|
||
return (2);
|
||
default:
|
||
return (1);
|
||
}
|
||
}
|