Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F158943963
D25494.id73947.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
66 KB
Referenced Files
None
Subscribers
None
D25494.id73947.diff
View Options
Index: lib/libifconfig/Makefile
===================================================================
--- lib/libifconfig/Makefile
+++ lib/libifconfig/Makefile
@@ -13,15 +13,24 @@
libifconfig_inet6.c \
libifconfig_internal.c \
libifconfig_lagg.c \
- libifconfig_media.c
+ libifconfig_media.c \
+ libifconfig_sfp.c \
+ libifconfig_sfp_enums.c
+
+GENERATED= libifconfig_sfp_enums.h \
+ libifconfig_sfp_enums.c \
+ libifconfig_sfp_enums_internal.h
+
+$(GENERATED): sfp.lua
+ ${.CURDIR}/sfp.lua
# If libifconfig become public uncomment those two lines
#INCSDIR= ${INCLUDEDIR}
-#INCS= libifconfig.h
+#INCS= libifconfig.h libifconfig_sfp.h libifconfig_sfp_enums.h
#MAN= libifconfig.3
-CFLAGS+= -I${.CURDIR}
+CFLAGS+= -I${.CURDIR} -I${.OBJDIR}
NO_WCAST_ALIGN= yes
.include <bsd.lib.mk>
Index: lib/libifconfig/libifconfig.h
===================================================================
--- lib/libifconfig/libifconfig.h
+++ lib/libifconfig/libifconfig.h
@@ -28,6 +28,10 @@
#pragma once
+#include <sys/types.h>
+
+#include <net/if.h>
+
#include <netinet/in.h>
#include <netinet6/in6_var.h>
Index: lib/libifconfig/libifconfig_sfp.h
===================================================================
--- /dev/null
+++ lib/libifconfig/libifconfig_sfp.h
@@ -0,0 +1,160 @@
+/*-
+ * Copyright (c) 2014 Alexander V. Chernikov. All rights reserved.
+ * 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$
+ */
+
+#pragma once
+
+#include <stdbool.h>
+#include <stdint.h>
+
+#include <libifconfig.h>
+#include <libifconfig_sfp_enums.h>
+
+/** SFP module information in raw numeric form
+ * These are static properties of the hardware.
+ * @see SFP_ENUMS_MAP for fields and their descriptions.
+ */
+struct ifconfig_sfp_info;
+
+/** SFP module information formatted as strings
+ * These are static strings that do not need to be freed.
+ * @see SFP_ENUM for fields and their descriptions.
+ */
+struct ifconfig_sfp_info_strings;
+
+#define SFF_VENDOR_STRING_SIZE 16 /**< max chars in a vendor string */
+#define SFF_VENDOR_DATE_SIZE 6 /**< chars in a vendor date code */
+
+/** SFP module vendor info strings */
+struct ifconfig_sfp_vendor_info {
+ char name[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor name */
+ char pn[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor part number */
+ char sn[SFF_VENDOR_STRING_SIZE + 1]; /**< vendor serial number */
+ char date[SFF_VENDOR_DATE_SIZE + 5]; /**< formatted vendor date */
+};
+
+/** SFP module status
+ * These are dynamic properties of the hardware.
+ */
+struct ifconfig_sfp_status {
+ double temp; /**< module temperature in degrees C,
+ valid range -40.0 to 125.0 */
+ double voltage; /**< module voltage in volts */
+ struct {
+ uint16_t rx; /**< channel receive power in milliwatts */
+ uint16_t tx; /**< channel transmit power in milliwatts */
+ } power[4]; /**< channel rx/tx power in milliwatts,
+ four channels for QSFP modules,
+ one channel for SFP modules,
+ unused channels are zeroed */
+ uint32_t bitrate; /**< link bitrate,
+ only present for QSFP modules,
+ zero for SFP modules */
+};
+
+#define SFF_DUMP_SIZE 256 /**< size of the memory dump buffer */
+
+#define SFP_DUMP_START 0 /**< start address of an SFP module dump */
+#define SFP_DUMP_SIZE 128 /**< bytes in an SFP module dump */
+
+#define QSFP_DUMP0_START 0 /**< start address of the first region
+ in a QSFP module dump */
+#define QSFP_DUMP0_SIZE 82 /**< bytes in the first region
+ in a QSFP module dump */
+#define QSFP_DUMP1_START 128 /**< start address of the second region
+ in a QSFP module dump */
+#define QSFP_DUMP1_SIZE 128 /**< bytes in the second region
+ in a QSFP module dump */
+
+/** SFP module I2C memory dump
+ * SFP modules have one region, QSFP modules have two regions.
+ */
+struct ifconfig_sfp_dump {
+ uint8_t data[SFF_DUMP_SIZE]; /**< memory dump data */
+ bool qsfp; /**< true if a QSFP module was dumped,
+ otherwise false */
+};
+
+/** Get information about the static properties of an SFP/QSFP module
+ * The information is returned in numeric form.
+ * @see ifconfig_sfp_get_sfp_info_strings to get corresponding strings.
+ * @param h An open ifconfig state handle
+ * @param name The name of an interface
+ * @param sfp Pointer to an object to fill, will be zeroed by this function
+ * @return 0 if successful, -1 with error info set in the handle otherwise
+ */
+int ifconfig_sfp_get_sfp_info(ifconfig_handle_t *h, const char *name,
+ struct ifconfig_sfp_info *sfp);
+
+/** Get string descriptions of the given SFP/WSFP module info
+ * The strings are static and do not need to be freed.
+ * @see ifconfig_sfp_get_sfp_info to obtain the input info.
+ * @param sfp Pointer to a filled SFP module info object
+ * @param strings Pointer to an object to be filled with pointers to
+ * static strings describing the given info
+ */
+void ifconfig_sfp_get_sfp_info_strings(struct ifconfig_sfp_info *sfp,
+ struct ifconfig_sfp_info_strings *strings);
+
+/** Get the vendor info strings from an SFP/QSFP module
+ * @param h An open ifconfig state handle
+ * @param name The name of an interface
+ * @param vi Pointer to an object to be filled with the vendor info strings,
+ * will be zeroed by this function
+ * @return 0 if successful, -1 with error info set in the handle otherwise
+ */
+int ifconfig_sfp_get_sfp_vendor_info(ifconfig_handle_t *h, const char *name,
+ struct ifconfig_sfp_vendor_info *vi);
+
+/** Get the status of an SFP/QSFP module's dynamic properties
+ * @param h An open ifconfig state handle
+ * @param name The name of an interface
+ * @param ss Pointer to an object to be filled with the module's status
+ * @return 0 if successful, -1 with error info set in the handle otherwise
+ * where the errcode `ENXIO` indicates an SFP module that is not
+ * calibrated or does not provide diagnostic status measurements
+ */
+int ifconfig_sfp_get_sfp_status(ifconfig_handle_t *h, const char *name,
+ struct ifconfig_sfp_status *ss);
+
+/** Convert milliwats power to decibel-milliwats power level
+ * This is provided as a convenience for displaying channel power levels.
+ * @see (struct ifconfig_sfp_status).power
+ * @param mW Power in milliwatts (mW)
+ * @return Power level in decibel-milliwatts (dBm)
+ */
+double mW_to_dBm(uint16_t mW);
+
+/** Dump the I2C memory of an SFP/QSFP module
+ * SFP modules have one memory region dumped, QSFP modules have two.
+ * @param h An open ifconfig state handle
+ * @param name The name of an interface
+ * @param buf Pointer to a dump data buffer object
+ * @return 0 if successful, -1 with error info set in the handle otherwise
+ */
+int ifconfig_sfp_get_sfp_dump(ifconfig_handle_t *h, const char *name,
+ struct ifconfig_sfp_dump *buf);
Index: lib/libifconfig/libifconfig_sfp.c
===================================================================
--- /dev/null
+++ lib/libifconfig/libifconfig_sfp.c
@@ -0,0 +1,494 @@
+/*-
+ * Copyright (c) 2014 Alexander V. Chernikov. All rights reserved.
+ * 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_enums_internal.h>
+
+#define SFF_8636_EXT_COMPLIANCE 0x80
+
+struct i2c_info {
+ struct ifreq ifr;
+ ifconfig_handle_t *h;
+ int error; /* Store first error */
+ bool qsfp; /* True if transceiver is QSFP */
+};
+
+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);
+ switch (id_byte) {
+ case 0:
+ h->error.errtype = OTHER;
+ h->error.errcode = ENOENT;
+ return (-1);
+ case SFF_8024_ID_QSFP:
+ case SFF_8024_ID_QSFPPLUS:
+ case SFF_8024_ID_QSFP28:
+ ii->qsfp = true;
+ default:
+ 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 (ii.qsfp)
+ return (get_qsfp_info(&ii, sfp));
+ return (get_sfp_info(&ii, sfp));
+}
+
+/*
+ * 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 (ii.qsfp)
+ 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);
+}
+
+double
+mW_to_dBm(uint16_t mW)
+{
+ /*
+ * Assume internally-calibrated data.
+ * This is always true for SFF-8346, and explicitly
+ * checked for SFF-8472.
+ */
+ return (10.0 * log10(1.0 * mW / 10000));
+}
+
+static uint16_t
+get_sff_power(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->power[0].rx = get_sff_power(ii, SFF_8472_DIAG, SFF_8472_RX_POWER);
+ ss->power[0].tx = get_sff_power(ii, SFF_8472_DIAG, SFF_8472_TX_POWER);
+ 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)
+{
+ ss->temp = get_sff_temp(ii, SFF_8436_BASE, SFF_8436_TEMP);
+ ss->voltage = get_sff_voltage(ii, SFF_8436_BASE, SFF_8436_VCC);
+ for (size_t chan = 0; chan < nitems(ss->power); ++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->power[chan].rx = get_sff_power(ii, SFF_8436_BASE, rxoffs);
+ ss->power[chan].tx = get_sff_power(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 (ii.qsfp)
+ return (get_qsfp_status(&ii, ss));
+ return (get_sfp_status(&ii, ss));
+}
+
+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(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";
+}
+
+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 (ii.qsfp) {
+ dump->qsfp = true;
+ read_i2c(&ii, SFF_8436_BASE, QSFP_DUMP0_START, QSFP_DUMP0_SIZE,
+ buf);
+ 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);
+ }
+
+ return (ii.error != 0 ? -1 : 0);
+}
Index: lib/libifconfig/sfp.lua
===================================================================
--- /dev/null
+++ lib/libifconfig/sfp.lua
@@ -0,0 +1,620 @@
+#!/usr/libexec/flua
+local license = [[
+/*-
+ * 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$
+ */
+]]
+
+enums = {
+ "SFF-8024 Rev. 4.6 Table 4-1: Indentifier Values",
+ {
+ name = "id",
+ description = "Transceiver identifier",
+ bits = 8,
+ values = {
+ {0x00, "UNKNOWN", "Unknown", "Unknown or unspecified"},
+ {0x01, "GBIC", "GBIC", "GBIC"},
+ {0x02, "SFF", "SFF",
+ "Module soldered to motherboard (ex: SFF)"},
+ {0x03, "SFP", "SFP/SFP+/SFP28", "SFP or SFP+"},
+ {0x04, "XBI", "XBI", "300 pin XBI"},
+ {0x05, "XENPAK", "Xenpak", "Xenpak"},
+ {0x06, "XFP", "XFP", "XFP"},
+ {0x07, "XFF", "XFF", "XFF"},
+ {0x08, "XFPE", "XFP-E", "XFP-E"},
+ {0x09, "XPAK", "XPAK", "XPAK"},
+ {0x0A, "X2", "X2", "X2"},
+ {0x0B, "DWDM_SFP", "DWDM-SFP/SFP+", "DWDM-SFP/SFP+"},
+ {0x0C, "QSFP", "QSFP", "QSFP"},
+ {0x0D, "QSFPPLUS", "QSFP+", "QSFP+ or later"},
+ {0x0E, "CXP", "CXP", "CXP"},
+ {0x0F, "HD4X", "HD4X",
+ "Shielded Mini Multilane HD 4X"},
+ {0x10, "HD8X", "HD8X",
+ "Shielded Mini Multilane HD 8X"},
+ {0x11, "QSFP28", "QSFP28", "QSFP28 or later"},
+ {0x12, "CXP2", "CXP2", "CXP2 (aka CXP28)"},
+ {0x13, "CDFP", "CDFP", "CDFP (Style 1/Style 2)"},
+ {0x14, "SMM4", "SMM4",
+ "Shielded Mini Multilane HD 4X fanout"},
+ {0x15, "SMM8", "SMM8",
+ "Shielded Mini Multilane HD 8X fanout"},
+ {0x16, "CDFP3", "CDFP3", "CDFP (Style 3)"},
+ {0x17, "MICROQSFP", "microQSFP", "microQSFP"},
+ {0x18, "QSFP_DD", "QSFP-DD",
+ "QSFP-DD 8X pluggable transceiver"},
+ {0x19, "QSFP8X", "QSFP8X",
+ "QSFP 8X pluggable transceiver"},
+ {0x1A, "SFP_DD", "SFP-DD",
+ "SFP-DD 2X pluggable transceiver"},
+ {0x1B, "DSFP", "DSFP",
+ "DSFP Dual SFP pluggable transceiver"},
+ {0x1C, "X4ML", "x4MiniLink/OcuLink", "x4 MiniLink/OcuLink"},
+ {0x1D, "X8ML", "x8MiniLink", "x8 MiniLink"},
+ {0x1E, "QSFP_CMIS", "QSFP+(CMIS)",
+ "QSFP+ or later w/Common Management Interface Specification"},
+ },
+ },
+ "SFF-8024 Rev. 4.6 Table 4-3: Connector Types",
+ {
+ name = "conn",
+ description = "Connector type",
+ bits = 8,
+ values = {
+ {0x00, "UNKNOWN", "Unknown"},
+ {0x01, "SC", "SC"},
+ {0x02, "FC_1_COPPER", "Fibre Channel Style 1 copper"},
+ {0x03, "FC_2_COPPER", "Fibre Channel Style 2 copper"},
+ {0x04, "BNC_TNC", "BNC/TNC"},
+ {0x05, "FC_COAX", "Fibre Channel coaxial"},
+ {0x06, "FIBER_JACK", "Fiber Jack"},
+ {0x07, "LC", "LC"},
+ {0x08, "MT_RJ", "MT-RJ"},
+ {0x09, "MU", "MU"},
+ {0x0A, "SG", "SG"},
+ {0x0B, "OPTICAL_PIGTAIL", "Optical pigtail"},
+ {0x0C, "MPO_1X12_POPTIC", "MPO 1x12 Parallel Optic"},
+ {0x0D, "MPO_2X16_POPTIC", "MPO 2x16 Parallel Optic"},
+ {0x20, "HSSDC_II", "HSSDC II"},
+ {0x21, "COPPER_PIGTAIL", "Copper pigtail"},
+ {0x22, "RJ45", "RJ45"},
+ {0x23, "NONE", "No separable connector"},
+ {0x24, "MXC_2X16", "MXC 2x16"},
+ {0x25, "CS_OPTICAL", "CS optical connector"},
+ {0x26, "MINI_CS_OPTICAL", "Mini CS optical connector"},
+ {0x27, "MPO_2X12_POPTIC", "MPO 2x12 Parallel Optic"},
+ {0x28, "MPO_1X16_POPTIC", "MPO 1x16 Parallel Optic"},
+ },
+ },
+ "SFF-8472 Rev. 11.4 table 3.5: Transceiver codes",
+ "10G Ethernet/IB compliance codes, byte 3",
+ {
+ name = "eth_10g",
+ description = "10G Ethernet/IB compliance",
+ bits = 8,
+ values = {
+ {0x80, "10G_BASE_ER", "10G Base-ER"},
+ {0x40, "10G_BASE_LRM", "10G Base-LRM"},
+ {0x20, "10G_BASE_LR", "10G Base-LR"},
+ {0x10, "10G_BASE_SR", "10G Base-SR"},
+ {0x08, "1X_SX", "1X SX"},
+ {0x04, "1X_LX", "1X LX"},
+ {0x02, "1X_COPPER_ACTIVE", "1X Copper Active"},
+ {0x01, "1X_COPPER_PASSIVE", "1X Copper Passive"},
+ },
+ },
+ "Ethernet compliance codes, byte 6",
+ {
+ name = "eth",
+ description = "Ethernet compliance",
+ bits = 8,
+ values = {
+ {0x80, "BASE_PX", "BASE-PX"},
+ {0x40, "BASE_BX10", "BASE-BX10"},
+ {0x20, "100BASE_FX", "100BASE-FX"},
+ {0x10, "100BASE_LX_LX10", "100BASE-LX/LX10"},
+ {0x08, "1000BASE_T", "1000BASE-T"},
+ {0x04, "1000BASE_CX", "1000BASE-CX"},
+ {0x02, "1000BASE_LX", "1000BASE-LX"},
+ {0x01, "1000BASE_SX", "1000BASE-SX"},
+ },
+ },
+ "FC link length, byte 7",
+ {
+ name = "fc_len",
+ description = "Fibre Channel link length",
+ bits = 8,
+ values = {
+ {0x80, "VERY_LONG", "very long distance"},
+ {0x40, "SHORT", "short distance"},
+ {0x20, "INTERMEDIATE", "intermediate distance"},
+ {0x10, "LONG", "long distance"},
+ {0x08, "MEDIUM", "medium distance"},
+ },
+ },
+ "Channel/Cable technology, byte 7-8",
+ {
+ name = "cab_tech",
+ description = "Channel/cable technology",
+ bits = 16,
+ values = {
+ {0x0400, "SA", "Shortwave laser (SA)"},
+ {0x0200, "LC", "Longwave laser (LC)"},
+ {0x0100, "EL_INTER", "Electrical inter-enclosure (EL)"},
+ {0x0080, "EL_INTRA", "Electrical intra-enclosure (EL)"},
+ {0x0040, "SN", "Shortwave laser (SN)"},
+ {0x0020, "SL", "Shortwave laser (SL)"},
+ {0x0010, "LL", "Longwave laser (LL)"},
+ {0x0008, "ACTIVE", "Active Cable"},
+ {0x0004, "PASSIVE", "Passive Cable"},
+ },
+ },
+ "FC Transmission media, byte 9",
+ {
+ name = "fc_media",
+ description = "Fibre Channel transmission media",
+ bits = 8,
+ values = {
+ {0x80, "TW", "Twin Axial Pair (TW)"},
+ {0x40, "TP", "Twisted Pair (TP)"},
+ {0x20, "MI", "Miniature Coax (MI)"},
+ {0x10, "TV", "Video Coax (TV)"},
+ {0x08, "M6", "Miltimode 62.5um (M6)"},
+ {0x04, "M5", "Multimode 50um (M5)"},
+ {0x02, "RESERVED", "Reserved"},
+ {0x01, "SM", "Single Mode (SM)"},
+ },
+ },
+ "FC Speed, byte 10",
+ {
+ name = "fc_speed",
+ description = "Fibre Channel speed",
+ bits = 8,
+ values = {
+ {0x80, "1200", "1200 MBytes/sec"},
+ {0x40, "800", "800 MBytes/sec"},
+ {0x20, "1600", "1600 MBytes/sec"},
+ {0x10, "400", "400 MBytes/sec"},
+ {0x08, "3200", "3200 MBytes/sec"},
+ {0x04, "200", "200 MBytes/sec"},
+ {0x01, "100", "100 MBytes/sec"},
+ },
+ },
+ "SFF-8436 Rev. 4.8 table 33: Specification compliance",
+ "10/40G Ethernet compliance codes, byte 128 + 3",
+ {
+ name = "eth_1040g",
+ description = "10/40G Ethernet compliance",
+ bits = 8,
+ values = {
+ {0x80, "EXTENDED", "Extended"},
+ {0x40, "10GBASE_LRM", "10GBASE-LRM"},
+ {0x20, "10GBASE_LR", "10GBASE-LR"},
+ {0x10, "10GBASE_SR", "10GBASE-SR"},
+ {0x08, "40GBASE_CR4", "40GBASE-CR4"},
+ {0x04, "40GBASE_SR4", "40GBASE-SR4"},
+ {0x02, "40GBASE_LR4", "40GBASE-LR4"},
+ {0x01, "40G_ACTIVE", "40G Active Cable"},
+ },
+ },
+ "SFF-8024 Rev. 4.6 table 4-4: Extended Specification Compliance",
+ {
+ name = "eth_ext",
+ description = "Extended specification compliance",
+ bits = 8,
+ values = {
+ {0xFF, "RESERVED_FF", "Reserved"},
+ {0x55, "128GFC_LW", "128GFC LW"},
+ {0x54, "128GFC_SW", "128GFC SW"},
+ {0x53, "128GFC_EA", "128GFC EA"},
+ {0x52, "64GFC_LW", "64GFC LW"},
+ {0x51, "64GFC_SW", "64GFC SW"},
+ {0x50, "64GFC_EA", "64GFC EA"},
+ {0x4F, "RESERVED_4F", "Reserved"},
+ {0x4E, "RESERVED_4E", "Reserved"},
+ {0x4D, "RESERVED_4D", "Reserved"},
+ {0x4C, "RESERVED_4C", "Reserved"},
+ {0x4B, "RESERVED_4B", "Reserved"},
+ {0x4A, "RESERVED_4A", "Reserved"},
+ {0x49, "RESERVED_49", "Reserved"},
+ {0x48, "RESERVED_48", "Reserved"},
+ {0x47, "RESERVED_47", "Reserved"},
+ {0x46, "200GBASE_LR4", "200GBASE-LR4"},
+ {0x45, "50GBASE_LR", "50GBASE-LR"},
+ {0x44, "200G_1550NM_PSM4", "200G 1550nm PSM4"},
+ {0x43, "200GBASE_FR4", "200GBASE-FR4"},
+ {0x42, "50GBASE_FR_200GBASE_DR4", "50GBASE-FR or 200GBASE-DR4"},
+ {0x41, "50GBASE_SR_100GBASE_SR2_200GBASE_SR4",
+ "50GBASE-SR/100GBASE-SR2/200GBASE-SR4"},
+ {0x40, "50GBASE_CR_100GBASE_CR2_200GBASE_CR4",
+ "50GBASE-CR/100GBASE-CR2/200GBASE-CR4"},
+ {0x3F, "RESERVED_3F", "Reserved"},
+ {0x3E, "RESERVED_3E", "Reserved"},
+ {0x3D, "RESERVED_3D", "Reserved"},
+ {0x3C, "RESERVED_3C", "Reserved"},
+ {0x3B, "RESERVED_3B", "Reserved"},
+ {0x3A, "RESERVED_3A", "Reserved"},
+ {0x39, "RESERVED_39", "Reserved"},
+ {0x38, "RESERVED_38", "Reserved"},
+ {0x37, "RESERVED_37", "Reserved"},
+ {0x36, "RESERVED_36", "Reserved"},
+ {0x35, "RESERVED_35", "Reserved"},
+ {0x34, "RESERVED_34", "Reserved"},
+ {0x33, "50_100_200GAUI_AOC_HI_BER",
+ "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <2.6e-4)"},
+ {0x32, "50_100_200GAUI_ACC_HI_BER",
+ "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <2.6e-4)"},
+ {0x31, "50_100_200GAUI_AOC_LO_BER",
+ "50GAUI/100GAUI-2/200GAUI-4 AOC (BER <1e-6)"},
+ {0x30, "50_100_200GAUI_ACC_LO_BER",
+ "50GAUI/100GAUI-2/200GAUI-4 ACC (BER <1e-6)"},
+ {0x2F, "RESERVED_2F", "Reserved"},
+ {0x2E, "RESERVED_2E", "Reserved"},
+ {0x2D, "RESERVED_2D", "Reserved"},
+ {0x2C, "RESERVED_2C", "Reserved"},
+ {0x2B, "RESERVED_2B", "Reserved"},
+ {0x2A, "RESERVED_2A", "Reserved"},
+ {0x29, "RESERVED_29", "Reserved"},
+ {0x28, "RESERVED_28", "Reserved"},
+ {0x27, "100G_LR", "100G-LR"},
+ {0x26, "100G_FR", "100G-FR"},
+ {0x25, "100GBASE_DR", "100GBASE-DR"},
+ {0x24, "4WDM_40_MSA", "4WDM-40 MSA"},
+ {0x23, "4WDM_20_MSA", "4WDM-20 MSA"},
+ {0x22, "4WDM_10_MSA", "4WDM-10 MSA"},
+ {0x21, "100G_PAM4_BIDI", "100G PAM4 BiDi"},
+ {0x20, "100G_SWDM4", "100G SWDM4"},
+ {0x1F, "40G_SWDM4", "40G SWDM4"},
+ {0x1E, "2_5GBASE_T", "2.5GBASE-T"},
+ {0x1D, "5GBASE_T", "5GBASE-T"},
+ {0x1C, "10GBASE_T_SR", "10GBASE-T Short Reach"},
+ {0x1B, "100G_1550NM_WDM", "100G 1550nm WDM"},
+ {0x1A, "100GE_DWDM2", "100GE-DWDM2"},
+ {0x19, "100G_25GAUI_C2M_ACC", "100G ACC or 25GAUI C2M ACC"},
+ {0x18, "100G_25GAUI_C2M_AOC", "100G AOC or 25GAUI C2M AOC"},
+ {0x17, "100G_CLR4", "100G CLR4"},
+ {0x16, "10GBASE_T_SFI",
+ "10GBASE-T with SFI electrical interface"},
+ {0x15, "G959_1_P1L1_2D2", "G959.1 profile P1L1-2D2"},
+ {0x14, "G959_1_P1S1_2D2", "G959.1 profile P1S1-2D2"},
+ {0x13, "G959_1_P1I1_2D1", "G959.1 profile P1I1-2D1"},
+ {0x12, "40G_PSM4", "40G PSM4 Parallel SMF"},
+ {0x11, "4X_10GBASE_SR", "4 x 10GBASE-SR"},
+ {0x10, "40GBASE_ER4", "40GBASE-ER4"},
+ {0x0F, "RESERVED_0F", "Reserved"},
+ {0x0E, "RESERVED_0E", "Reserved"},
+ {0x0D, "CA_25G_N", "25GBASE-CR CA-25G-N"},
+ {0x0C, "CA_25G_S", "25GBASE-CR CA-25G-S"},
+ {0x0B, "CA_L", "100GBASE-CR4 or 25GBASE-CR CA-L"},
+ {0x0A, "RESERVED_0A", "Reserved"},
+ {0x09, "OBSOLETE", "Obsolete"},
+ {0x08, "100G_25GAUI_C2M_ACC_1",
+ "100G ACC (Active Copper Cable"},
+ {0x07, "100G_PSM4_P_SMF", "100G PSM4 Parallel SMF"},
+ {0x06, "100G_CWDM4", "100G CWDM4"},
+ {0x05, "100GBASE_SR10", "100GBASE-SR10"},
+ {0x04, "100GBASE_ER4_25GBASE_ER", "100GBASE-ER4 or 25GBASE-ER"},
+ {0x03, "100GBASE_LR4_25GBASE_LR", "100GBASE-LR4 or 25GBASE-LR"},
+ {0x02, "100GBASE_SR4_25GBASE_SR", "100GBASE-SR4 or 25GBASE-SR"},
+ {0x01, "100G_25GAUI_C2M_AOC_1",
+ "100G AOC (Active Optical Cable"},
+ {0x00, "UNSPECIFIED", "Unspecified"},
+ },
+ },
+ "SFF-8636 Rev. 2.9 table 6.3: Revision compliance",
+ {
+ name = "rev",
+ description = "Revision compliance",
+ bits = 8,
+ values = {
+ {0x1, "SFF_8436_REV_LE_4_8", "SFF-8436 rev <=4.8"},
+ {0x2, "SFF_8436_REV_LE_4_8_ALT", "SFF-8436 rev <=4.8"},
+ {0x3, "SFF_8636_REV_LE_1_3", "SFF-8636 rev <=1.3"},
+ {0x4, "SFF_8636_REV_LE_1_4", "SFF-8636 rev <=1.4"},
+ {0x5, "SFF_8636_REV_LE_1_5", "SFF-8636 rev <=1.5"},
+ {0x6, "SFF_8636_REV_LE_2_0", "SFF-8636 rev <=2.0"},
+ {0x7, "SFF_8636_REV_LE_2_7", "SFF-8636 rev <=2.7"},
+ {0x8, "SFF_8363_REV_GE_2_8", "SFF-8636 rev >=2.8"},
+ {0x0, "UNSPECIFIED", "Unspecified"},
+ },
+ },
+}
+
+-- The rest of this file formats the above table as C source code.
+
+package.path = (os.getenv("SRCTOP") or "/usr/src").."/tools/lua/?.lua"
+local template = require("template")
+
+license_header = string.sub(license, 1, #license-1)
+generated_notice = [[
+/*
+ * Autogenerated file, do not edit!
+ *
+ * See sfp.lua instead.
+ */
+]]
+
+local header = template.process([[{*license_header*}
+{*generated_notice*}
+
+#pragma once
+
+#include <stdint.h>
+
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "string" then
+%}
+/*
+ * {*ent*}
+ */
+
+{%
+ else
+ local enum = ent
+ local name = "sfp_"..enum.name
+ local num, sym, disp, desc
+%}
+/** {*enum.description*} */
+enum {{name}} {
+{%
+ for _, item in ipairs(enum.values) do
+ num, sym, disp, desc = table.unpack(item)
+ local symbol = string.upper(name).."_"..sym
+%}
+ {{symbol}} = {{num}}, /**< {*desc or disp*} */
+{%
+ end
+%}
+};
+
+/** Get the symbolic name of a given {{name}} value */
+const char *ifconfig_{{name}}_symbol(enum {{name}});
+
+/** Get a user-friendly display name for a given {{name}} value */
+const char *ifconfig_{{name}}_display(enum {{name}});
+
+{%
+ if desc then
+%}
+/** Get a longer description of a given {{name}} value */
+const char *ifconfig_{{name}}_description(enum {{name}});
+
+{%
+ end
+ end
+end
+%}
+/*
+ * Descriptions of each enum
+ */
+
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "table" then
+ local enum = ent
+ local name = "sfp_"..enum.name
+%}
+/** Get a brief description of the {{name}} enum */
+static inline const char *
+ifconfig_enum_{{name}}_description(void)
+{
+ return ("{*enum.description*}");
+}
+
+{%
+ end
+end
+%}
+/*
+ * Info struct definitions
+ */
+
+struct ifconfig_sfp_info {
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "table" then
+ local enum = ent
+ local name = "sfp_"..enum.name
+ local t = string.format("uint%d_t", enum.bits)
+%}
+ {{t}} {{name}}; /**< {*enum.description*} */
+{%
+ end
+end
+%}
+};
+
+struct ifconfig_sfp_info_strings {
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "table" then
+ local enum = ent
+ local name = "sfp_"..enum.name
+%}
+ const char *{{name}}; /**< {*enum.description*} */
+{%
+ end
+end
+%}
+};]])
+
+local source = template.process([[{*license_header*}
+{*generated_notice*}
+
+#include <libifconfig_sfp_enums.h>
+#include <libifconfig_sfp_enums_internal.h>
+
+struct sfp_enum_metadata {
+ int number; /* numeric discriminant value */
+ const char *symbol; /* symbolic name */
+ const char *display; /* display name */
+ const char *description; /* detailed description */
+};
+
+const struct sfp_enum_metadata *
+find_metadata(const struct sfp_enum_metadata *table, int number)
+{
+ while (table->number != number && table->symbol != NULL)
+ ++table;
+ return (table->symbol != NULL ? table : NULL);
+}
+
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "string" then
+%}
+/*
+ * {*ent*}
+ */
+
+{%
+ else
+ local enum = ent
+ local name = "sfp_"..enum.name
+ local num, sym, disp, desc
+%}
+static const struct sfp_enum_metadata {{name}}_table_[] = {
+{%
+ for _, item in ipairs(enum.values) do
+ _num, sym, disp, desc = table.unpack(item)
+ local symbol = string.upper(name).."_"..sym
+%}
+ {
+ .number = {{symbol}},
+ .symbol = "{{symbol}}",
+ .display = "{*disp*}",
+{%
+ if desc then
+%}
+ .description = "{*desc*}",
+{%
+ end
+%}
+ },
+{%
+ end
+%}
+ {0}
+};
+const struct sfp_enum_metadata *{{name}}_table = {{name}}_table_;
+
+const char *
+ifconfig_{{name}}_symbol(enum {{name}} v)
+{
+ const struct sfp_enum_metadata *metadata;
+
+ if ((metadata = find_metadata({{name}}_table, v)) == NULL)
+ return (NULL);
+ return (metadata->symbol);
+}
+
+const char *
+ifconfig_{{name}}_display(enum {{name}} v)
+{
+ const struct sfp_enum_metadata *metadata;
+
+ if ((metadata = find_metadata({{name}}_table, v)) == NULL)
+ return (NULL);
+ return (metadata->display);
+}
+
+{%
+ if desc then
+%}
+const char *
+ifconfig_{{name}}_description(enum {{name}} v)
+{
+ const struct sfp_enum_metadata *metadata;
+
+ if ((metadata = find_metadata({{name}}_table, v)) == NULL)
+ return (NULL);
+ return (metadata->description);
+}
+
+{%
+ end
+ end
+end
+%}]])
+
+local internal = template.process([[{*license_header*}
+{*generated_notice*}
+
+#pragma once
+
+#include <libifconfig_sfp.h>
+#include <libifconfig_sfp_enums.h>
+
+struct sfp_enum_metadata;
+const struct sfp_enum_metadata *find_metadata(const struct sfp_enum_metadata *,
+ int);
+
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "table" then
+ local enum = ent
+ local name = "sfp_"..enum.name
+%}
+extern const struct sfp_enum_metadata *{{name}}_table;
+{%
+ end
+end
+%}
+
+static inline void
+get_sfp_info_strings(struct ifconfig_sfp_info *sfp,
+ struct ifconfig_sfp_info_strings *strings)
+{
+{%
+for i, ent in ipairs(enums) do
+ if type(ent) == "table" then
+ local enum = ent
+ local name = "sfp_"..enum.name
+%}
+ strings->{{name}} = ifconfig_{{name}}_display(sfp->{{name}});
+{%
+ end
+end
+%}
+}]])
+
+local name = "libifconfig_sfp_enums"
+do
+ local f = assert(io.open(name..".h", "w"))
+ f:write(header)
+ f:close()
+end
+do
+ local f = assert(io.open(name..".c", "w"))
+ f:write(source)
+ f:close()
+end
+do
+ local f = assert(io.open(name.."_internal.h", "w"))
+ f:write(internal)
+ f:close()
+end
Index: tools/lua/template.lua
===================================================================
--- /dev/null
+++ tools/lua/template.lua
@@ -0,0 +1,651 @@
+-- From lua-resty-template (modified to remove external dependencies)
+--[[
+Copyright (c) 2014 - 2020 Aapo Talvensaari
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+* 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.
+
+* Neither the name of the {organization} 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.
+]]--
+
+local setmetatable = setmetatable
+local loadstring = loadstring
+local tostring = tostring
+local setfenv = setfenv
+local require = require
+local concat = table.concat
+local assert = assert
+local write = io.write
+local pcall = pcall
+local phase
+local open = io.open
+local load = load
+local type = type
+local dump = string.dump
+local find = string.find
+local gsub = string.gsub
+local byte = string.byte
+local null
+local sub = string.sub
+local var
+
+local _VERSION = _VERSION
+local _ENV = _ENV -- luacheck: globals _ENV
+local _G = _G
+
+local HTML_ENTITIES = {
+ ["&"] = "&",
+ ["<"] = "<",
+ [">"] = ">",
+ ['"'] = """,
+ ["'"] = "'",
+ ["/"] = "/"
+}
+
+local CODE_ENTITIES = {
+ ["{"] = "{",
+ ["}"] = "}",
+ ["&"] = "&",
+ ["<"] = "<",
+ [">"] = ">",
+ ['"'] = """,
+ ["'"] = "'",
+ ["/"] = "/"
+}
+
+local VAR_PHASES
+
+local ESC = byte("\27")
+local NUL = byte("\0")
+local HT = byte("\t")
+local VT = byte("\v")
+local LF = byte("\n")
+local SOL = byte("/")
+local BSOL = byte("\\")
+local SP = byte(" ")
+local AST = byte("*")
+local NUM = byte("#")
+local LPAR = byte("(")
+local LSQB = byte("[")
+local LCUB = byte("{")
+local MINUS = byte("-")
+local PERCNT = byte("%")
+
+local EMPTY = ""
+
+local VIEW_ENV
+if _VERSION == "Lua 5.1" then
+ VIEW_ENV = { __index = function(t, k)
+ return t.context[k] or t.template[k] or _G[k]
+ end }
+else
+ VIEW_ENV = { __index = function(t, k)
+ return t.context[k] or t.template[k] or _ENV[k]
+ end }
+end
+
+local newtab
+do
+ local ok
+ ok, newtab = pcall(require, "table.new")
+ if not ok then newtab = function() return {} end end
+end
+
+local function enabled(val)
+ if val == nil then return true end
+ return val == true or (val == "1" or val == "true" or val == "on")
+end
+
+local function trim(s)
+ return gsub(gsub(s, "^%s+", EMPTY), "%s+$", EMPTY)
+end
+
+local function rpos(view, s)
+ while s > 0 do
+ local c = byte(view, s, s)
+ if c == SP or c == HT or c == VT or c == NUL then
+ s = s - 1
+ else
+ break
+ end
+ end
+ return s
+end
+
+local function escaped(view, s)
+ if s > 1 and byte(view, s - 1, s - 1) == BSOL then
+ if s > 2 and byte(view, s - 2, s - 2) == BSOL then
+ return false, 1
+ else
+ return true, 1
+ end
+ end
+ return false, 0
+end
+
+local function read_file(path)
+ local file, err = open(path, "rb")
+ if not file then return nil, err end
+ local content
+ content, err = file:read "*a"
+ file:close()
+ return content, err
+end
+
+local function load_view(template)
+ return function(view, plain)
+ if plain == true then return view end
+ local path, root = view, template.root
+ if root and root ~= EMPTY then
+ if byte(root, -1) == SOL then root = sub(root, 1, -2) end
+ if byte(view, 1) == SOL then path = sub(view, 2) end
+ path = root .. "/" .. path
+ end
+ return plain == false and assert(read_file(path)) or read_file(path) or view
+ end
+end
+
+local function load_file(func)
+ return function(view) return func(view, false) end
+end
+
+local function load_string(func)
+ return function(view) return func(view, true) end
+end
+
+local function loader(template)
+ return function(view)
+ return assert(load(view, nil, nil, setmetatable({ template = template }, VIEW_ENV)))
+ end
+end
+
+local function visit(visitors, content, tag, name)
+ if not visitors then
+ return content
+ end
+
+ for i = 1, visitors.n do
+ content = visitors[i](content, tag, name)
+ end
+
+ return content
+end
+
+local function new(template, safe)
+ template = template or newtab(0, 26)
+
+ template._VERSION = "2.0"
+ template.cache = {}
+ template.load = load_view(template)
+ template.load_file = load_file(template.load)
+ template.load_string = load_string(template.load)
+ template.print = write
+
+ local load_chunk = loader(template)
+
+ local caching
+ if VAR_PHASES and VAR_PHASES[phase()] then
+ caching = enabled(var.template_cache)
+ else
+ caching = true
+ end
+
+ local visitors
+ function template.visit(func)
+ if not visitors then
+ visitors = { func, n = 1 }
+ return
+ end
+ visitors.n = visitors.n + 1
+ visitors[visitors.n] = func
+ end
+
+ function template.caching(enable)
+ if enable ~= nil then caching = enable == true end
+ return caching
+ end
+
+ function template.output(s)
+ if s == nil or s == null then return EMPTY end
+ if type(s) == "function" then return template.output(s()) end
+ return tostring(s)
+ end
+
+ function template.escape(s, c)
+ if type(s) == "string" then
+ if c then return gsub(s, "[}{\">/<'&]", CODE_ENTITIES) end
+ return gsub(s, "[\">/<'&]", HTML_ENTITIES)
+ end
+ return template.output(s)
+ end
+
+ function template.new(view, layout)
+ local vt = type(view)
+
+ if vt == "boolean" then return new(nil, view) end
+ if vt == "table" then return new(view, safe) end
+ if vt == "nil" then return new(nil, safe) end
+
+ local render
+ local process
+ if layout then
+ if type(layout) == "table" then
+ render = function(self, context)
+ context = context or self
+ context.blocks = context.blocks or {}
+ context.view = template.process(view, context)
+ layout.blocks = context.blocks or {}
+ layout.view = context.view or EMPTY
+ layout:render()
+ end
+ process = function(self, context)
+ context = context or self
+ context.blocks = context.blocks or {}
+ context.view = template.process(view, context)
+ layout.blocks = context.blocks or {}
+ layout.view = context.view
+ return tostring(layout)
+ end
+ else
+ render = function(self, context)
+ context = context or self
+ context.blocks = context.blocks or {}
+ context.view = template.process(view, context)
+ template.render(layout, context)
+ end
+ process = function(self, context)
+ context = context or self
+ context.blocks = context.blocks or {}
+ context.view = template.process(view, context)
+ return template.process(layout, context)
+ end
+ end
+ else
+ render = function(self, context)
+ return template.render(view, context or self)
+ end
+ process = function(self, context)
+ return template.process(view, context or self)
+ end
+ end
+
+ if safe then
+ return setmetatable({
+ render = function(...)
+ local ok, err = pcall(render, ...)
+ if not ok then
+ return nil, err
+ end
+ end,
+ process = function(...)
+ local ok, output = pcall(process, ...)
+ if not ok then
+ return nil, output
+ end
+ return output
+ end,
+ }, {
+ __tostring = function(...)
+ local ok, output = pcall(process, ...)
+ if not ok then
+ return ""
+ end
+ return output
+ end })
+ end
+
+ return setmetatable({
+ render = render,
+ process = process
+ }, {
+ __tostring = process
+ })
+ end
+
+ function template.precompile(view, path, strip, plain)
+ local chunk = dump(template.compile(view, nil, plain), strip ~= false)
+ if path then
+ local file = open(path, "wb")
+ file:write(chunk)
+ file:close()
+ end
+ return chunk
+ end
+
+ function template.precompile_string(view, path, strip)
+ return template.precompile(view, path, strip, true)
+ end
+
+ function template.precompile_file(view, path, strip)
+ return template.precompile(view, path, strip, false)
+ end
+
+ function template.compile(view, cache_key, plain)
+ assert(view, "view was not provided for template.compile(view, cache_key, plain)")
+ if cache_key == "no-cache" then
+ return load_chunk(template.parse(view, plain)), false
+ end
+ cache_key = cache_key or view
+ local cache = template.cache
+ if cache[cache_key] then return cache[cache_key], true end
+ local func = load_chunk(template.parse(view, plain))
+ if caching then cache[cache_key] = func end
+ return func, false
+ end
+
+ function template.compile_file(view, cache_key)
+ return template.compile(view, cache_key, false)
+ end
+
+ function template.compile_string(view, cache_key)
+ return template.compile(view, cache_key, true)
+ end
+
+ function template.parse(view, plain)
+ assert(view, "view was not provided for template.parse(view, plain)")
+ if plain ~= true then
+ view = template.load(view, plain)
+ if byte(view, 1, 1) == ESC then return view end
+ end
+ local j = 2
+ local c = {[[
+context=... or {}
+local ___,blocks,layout={},blocks or {}
+local function include(v, c) return template.process(v, c or context) end
+local function echo(...) for i=1,select("#", ...) do ___[#___+1] = tostring(select(i, ...)) end end
+]] }
+ local i, s = 1, find(view, "{", 1, true)
+ while s do
+ local t, p = byte(view, s + 1, s + 1), s + 2
+ if t == LCUB then
+ local e = find(view, "}}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ if z then
+ i = s
+ else
+ c[j] = "___[#___+1]=template.escape("
+ c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "{")
+ c[j+2] = ")\n"
+ j=j+3
+ s, i = e + 1, e + 2
+ end
+ end
+ elseif t == AST then
+ local e = find(view, "*}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ if z then
+ i = s
+ else
+ c[j] = "___[#___+1]=template.output("
+ c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "*")
+ c[j+2] = ")\n"
+ j=j+3
+ s, i = e + 1, e + 2
+ end
+ end
+ elseif t == PERCNT then
+ local e = find(view, "%}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if z then
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ i = s
+ else
+ local n = e + 2
+ if byte(view, n, n) == LF then
+ n = n + 1
+ end
+ local r = rpos(view, s - 1)
+ if i <= r then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, r))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ c[j] = visit(visitors, trim(sub(view, p, e - 1)), "%")
+ c[j+1] = "\n"
+ j=j+2
+ s, i = n - 1, n
+ end
+ end
+ elseif t == LPAR then
+ local e = find(view, ")}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ if z then
+ i = s
+ else
+ local f = visit(visitors, sub(view, p, e - 1), "(")
+ local x = find(f, ",", 2, true)
+ if x then
+ c[j] = "___[#___+1]=include([=["
+ c[j+1] = trim(sub(f, 1, x - 1))
+ c[j+2] = "]=],"
+ c[j+3] = trim(sub(f, x + 1))
+ c[j+4] = ")\n"
+ j=j+5
+ else
+ c[j] = "___[#___+1]=include([=["
+ c[j+1] = trim(f)
+ c[j+2] = "]=])\n"
+ j=j+3
+ end
+ s, i = e + 1, e + 2
+ end
+ end
+ elseif t == LSQB then
+ local e = find(view, "]}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ if z then
+ i = s
+ else
+ c[j] = "___[#___+1]=include("
+ c[j+1] = visit(visitors, trim(sub(view, p, e - 1)), "[")
+ c[j+2] = ")\n"
+ j=j+3
+ s, i = e + 1, e + 2
+ end
+ end
+ elseif t == MINUS then
+ local e = find(view, "-}", p, true)
+ if e then
+ local x, y = find(view, sub(view, s, e + 1), e + 2, true)
+ if x then
+ local z, w = escaped(view, s)
+ if z then
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ i = s
+ else
+ y = y + 1
+ x = x - 1
+ if byte(view, y, y) == LF then
+ y = y + 1
+ end
+ local b = trim(sub(view, p, e - 1))
+ if b == "verbatim" or b == "raw" then
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ c[j] = "___[#___+1]=[=["
+ c[j+1] = visit(visitors, sub(view, e + 2, x))
+ c[j+2] = "]=]\n"
+ j=j+3
+ else
+ if byte(view, x, x) == LF then
+ x = x - 1
+ end
+ local r = rpos(view, s - 1)
+ if i <= r then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, r))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ c[j] = 'blocks["'
+ c[j+1] = b
+ c[j+2] = '"]=include[=['
+ c[j+3] = visit(visitors, sub(view, e + 2, x), "-", b)
+ c[j+4] = "]=]\n"
+ j=j+5
+ end
+ s, i = y - 1, y
+ end
+ end
+ end
+ elseif t == NUM then
+ local e = find(view, "#}", p, true)
+ if e then
+ local z, w = escaped(view, s)
+ if i < s - w then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, sub(view, i, s - 1 - w))
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ if z then
+ i = s
+ else
+ e = e + 2
+ if byte(view, e, e) == LF then
+ e = e + 1
+ end
+ s, i = e - 1, e
+ end
+ end
+ end
+ s = find(view, "{", s + 1, true)
+ end
+ s = sub(view, i)
+ if s and s ~= EMPTY then
+ c[j] = "___[#___+1]=[=[\n"
+ c[j+1] = visit(visitors, s)
+ c[j+2] = "]=]\n"
+ j=j+3
+ end
+ c[j] = "return layout and include(layout,setmetatable({view=table.concat(___),blocks=blocks},{__index=context})) or table.concat(___)" -- luacheck: ignore
+ return concat(c)
+ end
+
+ function template.parse_file(view)
+ return template.parse(view, false)
+ end
+
+ function template.parse_string(view)
+ return template.parse(view, true)
+ end
+
+ function template.process(view, context, cache_key, plain)
+ assert(view, "view was not provided for template.process(view, context, cache_key, plain)")
+ return template.compile(view, cache_key, plain)(context)
+ end
+
+ function template.process_file(view, context, cache_key)
+ assert(view, "view was not provided for template.process_file(view, context, cache_key)")
+ return template.compile(view, cache_key, false)(context)
+ end
+
+ function template.process_string(view, context, cache_key)
+ assert(view, "view was not provided for template.process_string(view, context, cache_key)")
+ return template.compile(view, cache_key, true)(context)
+ end
+
+ function template.render(view, context, cache_key, plain)
+ assert(view, "view was not provided for template.render(view, context, cache_key, plain)")
+ template.print(template.process(view, context, cache_key, plain))
+ end
+
+ function template.render_file(view, context, cache_key)
+ assert(view, "view was not provided for template.render_file(view, context, cache_key)")
+ template.render(view, context, cache_key, false)
+ end
+
+ function template.render_string(view, context, cache_key)
+ assert(view, "view was not provided for template.render_string(view, context, cache_key)")
+ template.render(view, context, cache_key, true)
+ end
+
+ if safe then
+ return setmetatable({}, {
+ __index = function(_, k)
+ if type(template[k]) == "function" then
+ return function(...)
+ local ok, a, b = pcall(template[k], ...)
+ if not ok then
+ return nil, a
+ end
+ return a, b
+ end
+ end
+ return template[k]
+ end,
+ __new_index = function(_, k, v)
+ template[k] = v
+ end,
+ })
+ end
+
+ return template
+end
+
+return new()
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jun 9, 2:09 AM (8 h, 34 s)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
33823211
Default Alt Text
D25494.id73947.diff (66 KB)
Attached To
Mode
D25494: libifconfig: Add functionality for querying SFP modules
Attached
Detach File
Event Timeline
Log In to Comment