summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/fcoe/fcoe.c
diff options
context:
space:
mode:
authorZhong Wang <Zhong.Wang@Sun.COM>2009-03-19 03:03:46 +0800
committerZhong Wang <Zhong.Wang@Sun.COM>2009-03-19 03:03:46 +0800
commit2a8164df8a5f42c8a00f10c67d7bc84f80ae9c41 (patch)
tree5ef9c65f09194743324b20261093e076d4eeea6b /usr/src/uts/common/io/fcoe/fcoe.c
parentbfe6f8f50e1ad7cfc72f4665989dc9e25e82e872 (diff)
downloadillumos-gate-2a8164df8a5f42c8a00f10c67d7bc84f80ae9c41.tar.gz
PSARC/2008/310 FCoE (Fibre Channel over Ethernet) Target
6701027 Need FCoE target for Solaris 6812750 Change in stmf/fct/stmf_sbd needed for support FCoE
Diffstat (limited to 'usr/src/uts/common/io/fcoe/fcoe.c')
-rw-r--r--usr/src/uts/common/io/fcoe/fcoe.c1286
1 files changed, 1286 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/fcoe/fcoe.c b/usr/src/uts/common/io/fcoe/fcoe.c
new file mode 100644
index 0000000000..e2dbe02921
--- /dev/null
+++ b/usr/src/uts/common/io/fcoe/fcoe.c
@@ -0,0 +1,1286 @@
+/*
+ * 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 2009 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+/*
+ * The following notice accompanied the original version of this file:
+ *
+ * BSD LICENSE
+ *
+ * Copyright(c) 2007 Intel Corporation. All rights reserved.
+ * 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 Intel Corporation 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
+ * OWNER 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.
+ */
+
+/*
+ * Common FCoE interface interacts with MAC and FCoE clients, managing
+ * FCoE ports, doing MAC address discovery/managment, and FC frame
+ * encapsulation/decapsulation
+ */
+
+#include <sys/stat.h>
+#include <sys/conf.h>
+#include <sys/file.h>
+#include <sys/cred.h>
+
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/sunndi.h>
+#include <sys/byteorder.h>
+#include <sys/atomic.h>
+#include <sys/sysmacros.h>
+#include <sys/cmn_err.h>
+#include <sys/crc32.h>
+#include <sys/strsubr.h>
+
+#include <sys/mac_client.h>
+
+/*
+ * FCoE header files
+ */
+#include <sys/fcoe/fcoeio.h>
+#include <sys/fcoe/fcoe_common.h>
+
+/*
+ * Driver's own header files
+ */
+#include <fcoe.h>
+#include <fcoe_fc.h>
+#include <fcoe_eth.h>
+
+/*
+ * Function forward declaration
+ */
+static int fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd);
+static int fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd);
+static int fcoe_bus_ctl(dev_info_t *fca_dip, dev_info_t *rip,
+ ddi_ctl_enum_t op, void *arg, void *result);
+static int fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp);
+static int fcoe_close(dev_t dev, int flag, int otype, cred_t *credp);
+static int fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
+ cred_t *credp, int *rval);
+static int fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
+ void **ibuf, void **abuf, void **obuf);
+static int fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio,
+ void *obuf);
+static int fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode);
+static int fcoe_attach_init(fcoe_soft_state_t *this_ss);
+static int fcoe_detach_uninit(fcoe_soft_state_t *this_ss);
+static int fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
+static int fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip);
+static void fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac,
+ int is_pwwn, uint8_t idx);
+static fcoe_mac_t *fcoe_create_mac_by_name(uint8_t *name);
+static int fcoe_cmp_wwn(fcoe_mac_t *checkedmac);
+static void fcoe_watchdog(void *arg);
+static void fcoe_worker_init();
+static int fcoe_worker_fini();
+static void fcoe_worker_frame();
+static int fcoe_get_port_list(fcoe_port_instance_t *ports, int count);
+
+/*
+ * Driver identificaton stuff
+ */
+static struct cb_ops fcoe_cb_ops = {
+ fcoe_open,
+ fcoe_close,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ nodev,
+ fcoe_ioctl,
+ nodev,
+ nodev,
+ nodev,
+ nochpoll,
+ ddi_prop_op,
+ 0,
+ D_MP | D_NEW | D_HOTPLUG,
+ CB_REV,
+ nodev,
+ nodev
+};
+
+static struct bus_ops fcoe_busops = {
+ BUSO_REV,
+ nullbusmap, /* bus_map */
+ NULL, /* bus_get_intrspec */
+ NULL, /* bus_add_intrspec */
+ NULL, /* bus_remove_intrspec */
+ i_ddi_map_fault, /* bus_map_fault */
+ ddi_dma_map, /* bus_dma_map */
+ ddi_dma_allochdl, /* bus_dma_allochdl */
+ ddi_dma_freehdl, /* bus_dma_freehdl */
+ ddi_dma_bindhdl, /* bus_dma_bindhdl */
+ ddi_dma_unbindhdl, /* bus_unbindhdl */
+ ddi_dma_flush, /* bus_dma_flush */
+ ddi_dma_win, /* bus_dma_win */
+ ddi_dma_mctl, /* bus_dma_ctl */
+ fcoe_bus_ctl, /* bus_ctl */
+ ddi_bus_prop_op, /* bus_prop_op */
+ NULL, /* bus_get_eventcookie */
+ NULL, /* bus_add_eventcall */
+ NULL, /* bus_remove_event */
+ NULL, /* bus_post_event */
+ NULL, /* bus_intr_ctl */
+ NULL, /* bus_config */
+ NULL, /* bus_unconfig */
+ NULL, /* bus_fm_init */
+ NULL, /* bus_fm_fini */
+ NULL, /* bus_fm_access_enter */
+ NULL, /* bus_fm_access_exit */
+ NULL, /* bus_power */
+ NULL
+};
+
+static struct dev_ops fcoe_ops = {
+ DEVO_REV,
+ 0,
+ nodev,
+ nulldev,
+ nulldev,
+ fcoe_attach,
+ fcoe_detach,
+ nodev,
+ &fcoe_cb_ops,
+ &fcoe_busops,
+ ddi_power
+};
+
+#define FCOE_VERSION "20090311-1.00"
+#define FCOE_NAME "FCoE Transport v" FCOE_VERSION
+#define TASKQ_NAME_LEN 32
+
+static struct modldrv modldrv = {
+ &mod_driverops,
+ FCOE_NAME,
+ &fcoe_ops,
+};
+
+static struct modlinkage modlinkage = {
+ MODREV_1, &modldrv, NULL
+};
+
+/*
+ * TRACE for all FCoE related modules
+ */
+static kmutex_t fcoe_trace_buf_lock;
+static int fcoe_trace_buf_curndx = 0;
+static int fcoe_trace_on = 1;
+static caddr_t fcoe_trace_buf = NULL;
+static clock_t fcoe_trace_start = 0;
+static caddr_t ftb = NULL;
+static int fcoe_trace_buf_size = (1 * 1024 * 1024);
+
+/*
+ * Driver's global variables
+ */
+static void *fcoe_state = NULL;
+fcoe_soft_state_t *fcoe_global_ss = NULL;
+int fcoe_use_ext_log = 1;
+
+static ddi_taskq_t *fcoe_worker_taskq;
+static fcoe_worker_t *fcoe_workers;
+static uint32_t fcoe_nworkers_running;
+
+const char *fcoe_workers_num = "workers-number";
+volatile int fcoe_nworkers;
+
+/*
+ * Common loadable module entry points _init, _fini, _info
+ */
+
+int
+_init(void)
+{
+ int ret;
+
+ ret = ddi_soft_state_init(&fcoe_state, sizeof (fcoe_soft_state_t), 0);
+ if (ret == 0) {
+ ret = mod_install(&modlinkage);
+ if (ret != 0) {
+ ddi_soft_state_fini(&fcoe_state);
+ } else {
+ fcoe_trace_start = ddi_get_lbolt();
+ ftb = kmem_zalloc(fcoe_trace_buf_size,
+ KM_SLEEP);
+ fcoe_trace_buf = ftb;
+ mutex_init(&fcoe_trace_buf_lock, NULL, MUTEX_DRIVER, 0);
+ }
+ }
+
+ FCOE_LOG("fcoe", "exit _init with %x", ret);
+
+ return (ret);
+}
+
+int
+_fini(void)
+{
+ int ret;
+
+ ret = mod_remove(&modlinkage);
+ if (ret == 0) {
+ ddi_soft_state_fini(&fcoe_state);
+ }
+
+ FCOE_LOG("fcoe", "exit _fini with %x", ret);
+ if (ret == 0) {
+ kmem_free(fcoe_trace_buf, fcoe_trace_buf_size);
+ mutex_destroy(&fcoe_trace_buf_lock);
+ }
+
+ return (ret);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*
+ * Autoconfiguration entry points: attach, detach, getinfo
+ */
+
+static int
+fcoe_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ int ret = DDI_FAILURE;
+ int fcoe_ret;
+ int instance;
+ fcoe_soft_state_t *ss;
+
+ instance = ddi_get_instance(dip);
+ switch (cmd) {
+ case DDI_ATTACH:
+ ret = ddi_soft_state_zalloc(fcoe_state, instance);
+ if (ret == DDI_FAILURE) {
+ FCOE_LOG(0, "soft_state_zalloc-%x/%x", ret, instance);
+ return (ret);
+ }
+
+ ss = ddi_get_soft_state(fcoe_state, instance);
+ ss->ss_dip = dip;
+
+ ASSERT(fcoe_global_ss == NULL);
+ fcoe_global_ss = ss;
+ fcoe_ret = fcoe_attach_init(ss);
+ if (fcoe_ret == FCOE_SUCCESS) {
+ ret = DDI_SUCCESS;
+ }
+
+ FCOE_LOG("fcoe", "fcoe_attach_init end with-%x", fcoe_ret);
+ break;
+
+ case DDI_RESUME:
+ ret = DDI_SUCCESS;
+ break;
+
+ default:
+ FCOE_LOG("fcoe", "unsupported attach cmd-%x", cmd);
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+fcoe_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ int ret = DDI_FAILURE;
+ int fcoe_ret;
+ int instance;
+ fcoe_soft_state_t *ss;
+
+ instance = ddi_get_instance(dip);
+ ss = ddi_get_soft_state(fcoe_state, instance);
+ if (ss == NULL) {
+ return (ret);
+ }
+
+ ASSERT(fcoe_global_ss != NULL);
+ ASSERT(dip == fcoe_global_ss->ss_dip);
+ switch (cmd) {
+ case DDI_DETACH:
+ fcoe_ret = fcoe_detach_uninit(ss);
+ if (fcoe_ret == FCOE_SUCCESS) {
+ ret = DDI_SUCCESS;
+ fcoe_global_ss = NULL;
+ }
+
+ break;
+
+ case DDI_SUSPEND:
+ ret = DDI_SUCCESS;
+ break;
+
+ default:
+ FCOE_LOG(0, "unsupported detach cmd-%x", cmd);
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * FCA driver's intercepted bus control operations.
+ */
+static int
+fcoe_bus_ctl(dev_info_t *fcoe_dip, dev_info_t *rip,
+ ddi_ctl_enum_t op, void *clientarg, void *result)
+{
+ int ret;
+ switch (op) {
+ case DDI_CTLOPS_REPORTDEV:
+ case DDI_CTLOPS_IOMIN:
+ ret = DDI_SUCCESS;
+ break;
+
+ case DDI_CTLOPS_INITCHILD:
+ ret = fcoe_initchild(fcoe_dip, (dev_info_t *)clientarg);
+ break;
+
+ case DDI_CTLOPS_UNINITCHILD:
+ ret = fcoe_uninitchild(fcoe_dip, (dev_info_t *)clientarg);
+ break;
+
+ default:
+ ret = ddi_ctlops(fcoe_dip, rip, op, clientarg, result);
+ break;
+ }
+
+ return (ret);
+}
+
+/*
+ * We need specify the dev address for client driver's instance, or we
+ * can't online client driver's instance.
+ */
+/* ARGSUSED */
+static int
+fcoe_initchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
+{
+ char name[32];
+ static int inicounter = 0;
+ static int tgtcounter = 0;
+ int *counter;
+
+ if (strcmp(ddi_driver_name(client_dip), FCOET_DRIVER_NAME) == 0) {
+ counter = &tgtcounter;
+ tgtcounter++;
+ } else {
+ counter = &inicounter;
+ inicounter++;
+ }
+
+ bzero(name, 32);
+ (void) sprintf((char *)name, "%x,0", *counter);
+ ddi_set_name_addr(client_dip, name);
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+fcoe_uninitchild(dev_info_t *fcoe_dip, dev_info_t *client_dip)
+{
+ ddi_set_name_addr(client_dip, NULL);
+ return (DDI_SUCCESS);
+}
+
+/*
+ * Device access entry points
+ */
+static int
+fcoe_open(dev_t *devp, int flag, int otype, cred_t *credp)
+{
+ int instance;
+ fcoe_soft_state_t *ss;
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ /*
+ * Since this is for debugging only, only allow root to issue ioctl now
+ */
+ if (drv_priv(credp) != 0) {
+ return (EPERM);
+ }
+
+ instance = (int)getminor(*devp);
+ ss = ddi_get_soft_state(fcoe_state, instance);
+ if (ss == NULL) {
+ return (ENXIO);
+ }
+
+ mutex_enter(&ss->ss_ioctl_mutex);
+ if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
+ /*
+ * It is already open for exclusive access.
+ * So shut the door on this caller.
+ */
+ mutex_exit(&ss->ss_ioctl_mutex);
+ return (EBUSY);
+ }
+
+ if (flag & FEXCL) {
+ if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) {
+ /*
+ * Exclusive operation not possible
+ * as it is already opened
+ */
+ mutex_exit(&ss->ss_ioctl_mutex);
+ return (EBUSY);
+ }
+ ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL;
+ }
+
+ ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_OPEN;
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+fcoe_close(dev_t dev, int flag, int otype, cred_t *credp)
+{
+ int instance;
+ fcoe_soft_state_t *ss;
+
+ if (otype != OTYP_CHR) {
+ return (EINVAL);
+ }
+
+ instance = (int)getminor(dev);
+ ss = ddi_get_soft_state(fcoe_state, instance);
+ if (ss == NULL) {
+ return (ENXIO);
+ }
+
+ mutex_enter(&ss->ss_ioctl_mutex);
+ if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
+ mutex_exit(&ss->ss_ioctl_mutex);
+ return (ENODEV);
+ }
+
+ ss->ss_ioctl_flags &= ~FCOE_IOCTL_FLAG_MASK;
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ return (0);
+}
+
+/* ARGSUSED */
+static int
+fcoe_ioctl(dev_t dev, int cmd, intptr_t data, int mode,
+ cred_t *credp, int *rval)
+{
+ fcoe_soft_state_t *ss;
+ int ret = 0;
+
+ if (drv_priv(credp) != 0) {
+ return (EPERM);
+ }
+
+ ss = ddi_get_soft_state(fcoe_state, (int32_t)getminor(dev));
+ if (ss == NULL) {
+ return (ENXIO);
+ }
+
+ mutex_enter(&ss->ss_ioctl_mutex);
+ if ((ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_OPEN) == 0) {
+ mutex_exit(&ss->ss_ioctl_mutex);
+ return (ENXIO);
+ }
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ switch (cmd) {
+ case FCOEIO_CMD:
+ ret = fcoe_iocmd(ss, data, mode);
+ break;
+ default:
+ FCOE_LOG(0, "fcoe_ioctl: ioctl-0x%02X", cmd);
+ ret = ENOTTY;
+ break;
+ }
+
+ return (ret);
+}
+
+static int
+fcoe_copyin_iocdata(intptr_t data, int mode, fcoeio_t **fcoeio,
+ void **ibuf, void **abuf, void **obuf)
+{
+ int ret = 0;
+
+ *ibuf = NULL;
+ *abuf = NULL;
+ *obuf = NULL;
+ *fcoeio = kmem_zalloc(sizeof (fcoeio_t), KM_SLEEP);
+ if (ddi_copyin((void *)data, *fcoeio, sizeof (fcoeio_t), mode) != 0) {
+ ret = EFAULT;
+ goto copyin_iocdata_fail;
+ }
+
+ if ((*fcoeio)->fcoeio_ilen > FCOEIO_MAX_BUF_LEN ||
+ (*fcoeio)->fcoeio_alen > FCOEIO_MAX_BUF_LEN ||
+ (*fcoeio)->fcoeio_olen > FCOEIO_MAX_BUF_LEN) {
+ ret = EFAULT;
+ goto copyin_iocdata_fail;
+ }
+
+ if ((*fcoeio)->fcoeio_ilen) {
+ *ibuf = kmem_zalloc((*fcoeio)->fcoeio_ilen, KM_SLEEP);
+ if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_ibuf,
+ *ibuf, (*fcoeio)->fcoeio_ilen, mode) != 0) {
+ ret = EFAULT;
+ goto copyin_iocdata_fail;
+ }
+ }
+
+ if ((*fcoeio)->fcoeio_alen) {
+ *abuf = kmem_zalloc((*fcoeio)->fcoeio_alen, KM_SLEEP);
+ if (ddi_copyin((void *)(unsigned long)(*fcoeio)->fcoeio_abuf,
+ *abuf, (*fcoeio)->fcoeio_alen, mode) != 0) {
+ ret = EFAULT;
+ goto copyin_iocdata_fail;
+ }
+ }
+
+ if ((*fcoeio)->fcoeio_olen) {
+ *obuf = kmem_zalloc((*fcoeio)->fcoeio_olen, KM_SLEEP);
+ }
+ return (ret);
+
+copyin_iocdata_fail:
+ if (*abuf) {
+ kmem_free(*abuf, (*fcoeio)->fcoeio_alen);
+ *abuf = NULL;
+ }
+
+ if (*ibuf) {
+ kmem_free(*ibuf, (*fcoeio)->fcoeio_ilen);
+ *ibuf = NULL;
+ }
+
+ kmem_free(*fcoeio, sizeof (fcoeio_t));
+ return (ret);
+}
+
+static int
+fcoe_copyout_iocdata(intptr_t data, int mode, fcoeio_t *fcoeio, void *obuf)
+{
+ if (fcoeio->fcoeio_olen) {
+ if (ddi_copyout(obuf,
+ (void *)(unsigned long)fcoeio->fcoeio_obuf,
+ fcoeio->fcoeio_olen, mode) != 0) {
+ return (EFAULT);
+ }
+ }
+
+ if (ddi_copyout(fcoeio, (void *)data, sizeof (fcoeio_t), mode) != 0) {
+ return (EFAULT);
+ }
+ return (0);
+}
+
+static int
+fcoe_iocmd(fcoe_soft_state_t *ss, intptr_t data, int mode)
+{
+ int ret;
+ fcoe_mac_t *fcoe_mac;
+ void *ibuf = NULL;
+ void *obuf = NULL;
+ void *abuf = NULL;
+ fcoeio_t *fcoeio;
+
+ ret = fcoe_copyin_iocdata(data, mode, &fcoeio, &ibuf, &abuf, &obuf);
+ if (ret != 0) {
+ goto fcoeiocmd_release_buf;
+ }
+
+ /*
+ * If an exclusive open was demanded during open, ensure that
+ * only one thread can execute an ioctl at a time
+ */
+ mutex_enter(&ss->ss_ioctl_mutex);
+ if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL) {
+ if (ss->ss_ioctl_flags & FCOE_IOCTL_FLAG_EXCL_BUSY) {
+ mutex_exit(&ss->ss_ioctl_mutex);
+ fcoeio->fcoeio_status = FCOEIOE_BUSY;
+ ret = EBUSY;
+ goto fcoeiocmd_release_buf;
+ }
+ ss->ss_ioctl_flags |= FCOE_IOCTL_FLAG_EXCL_BUSY;
+ }
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ fcoeio->fcoeio_status = 0;
+
+ switch (fcoeio->fcoeio_cmd) {
+ case FCOEIO_CREATE_FCOE_PORT: {
+ fcoeio_create_port_param_t *param =
+ (fcoeio_create_port_param_t *)ibuf;
+ int cmpwwn = 0;
+ fcoe_port_t *eport;
+
+ if (fcoeio->fcoeio_ilen !=
+ sizeof (fcoeio_create_port_param_t) ||
+ fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) {
+ fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
+ ret = EINVAL;
+ break;
+ }
+
+ mutex_enter(&ss->ss_ioctl_mutex);
+ fcoe_mac = fcoe_create_mac_by_name(param->fcp_mac_name);
+ if (fcoe_mac == NULL) {
+ mutex_exit(&ss->ss_ioctl_mutex);
+ fcoeio->fcoeio_status = FCOEIOE_CREATE_MAC;
+ ret = EIO;
+ break;
+ }
+
+ if (fcoe_mac->fm_flags & FCOE_MAC_FLAG_ENABLED) {
+ mutex_exit(&ss->ss_ioctl_mutex);
+ fcoeio->fcoeio_status = FCOEIOE_ALREADY;
+ ret = EALREADY;
+ break;
+ } else {
+ ret = fcoe_open_mac(fcoe_mac, param->fcp_force_promisc,
+ &fcoeio->fcoeio_status);
+ if (ret != 0) {
+ fcoe_destroy_mac(fcoe_mac);
+ mutex_exit(&ss->ss_ioctl_mutex);
+ if (fcoeio->fcoeio_status == 0) {
+ fcoeio->fcoeio_status =
+ FCOEIOE_OPEN_MAC;
+ }
+ ret = EIO;
+ break;
+ } else {
+ fcoe_mac->fm_flags |= FCOE_MAC_FLAG_ENABLED;
+ }
+ }
+
+ /*
+ * Provide PWWN and NWWN based on mac address
+ */
+ eport = &fcoe_mac->fm_eport;
+ if (!param->fcp_pwwn_provided) {
+ fcoe_init_wwn_from_mac(eport->eport_portwwn,
+ fcoe_mac->fm_current_addr, 1, 0);
+ } else {
+ (void) memcpy(eport->eport_portwwn, param->fcp_pwwn, 8);
+ }
+
+ if (!param->fcp_nwwn_provided) {
+ fcoe_init_wwn_from_mac(eport->eport_nodewwn,
+ fcoe_mac->fm_current_addr, 0, 0);
+ } else {
+ (void) memcpy(eport->eport_nodewwn, param->fcp_nwwn, 8);
+ }
+
+ cmpwwn = fcoe_cmp_wwn(fcoe_mac);
+
+ if (cmpwwn != 0) {
+ if (cmpwwn == 1) {
+ fcoeio->fcoeio_status = FCOEIOE_PWWN_CONFLICTED;
+ } else if (cmpwwn == -1) {
+ fcoeio->fcoeio_status = FCOEIOE_NWWN_CONFLICTED;
+ }
+ (void) fcoe_close_mac(fcoe_mac);
+ fcoe_destroy_mac(fcoe_mac);
+ mutex_exit(&ss->ss_ioctl_mutex);
+ ret = ENOTUNIQ;
+ break;
+ }
+
+ if (ret == 0) {
+ ret = fcoe_create_port(ss->ss_dip,
+ fcoe_mac,
+ (param->fcp_port_type == FCOE_CLIENT_TARGET));
+ if (ret != 0) {
+ (void) fcoe_close_mac(fcoe_mac);
+ fcoe_destroy_mac(fcoe_mac);
+ fcoeio->fcoeio_status = FCOEIOE_CREATE_PORT;
+ ret = EIO;
+ }
+ }
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ break;
+ }
+
+ case FCOEIO_DELETE_FCOE_PORT: {
+ uint8_t *mac_name = (uint8_t *)ibuf;
+
+ if (fcoeio->fcoeio_ilen > FCOE_MAX_MAC_NAME_LEN ||
+ fcoeio->fcoeio_xfer != FCOEIO_XFER_WRITE) {
+ fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
+ ret = EINVAL;
+ break;
+ }
+
+ mutex_enter(&ss->ss_ioctl_mutex);
+ ret = fcoe_delete_port(ss->ss_dip, fcoeio, mac_name);
+ if (ret != 0) {
+ FCOE_LOG("fcoe",
+ "fcoe_delete_port failed: %d", ret);
+ }
+ mutex_exit(&ss->ss_ioctl_mutex);
+ break;
+ }
+
+ case FCOEIO_GET_FCOE_PORT_LIST: {
+ fcoe_port_list_t *list = (fcoe_port_list_t *)obuf;
+ int count;
+
+ if (fcoeio->fcoeio_xfer != FCOEIO_XFER_READ ||
+ fcoeio->fcoeio_olen < sizeof (fcoe_port_list_t)) {
+ fcoeio->fcoeio_status = FCOEIOE_INVAL_ARG;
+ ret = EINVAL;
+ break;
+ }
+ mutex_enter(&ss->ss_ioctl_mutex);
+
+ list->numPorts = 1 + (fcoeio->fcoeio_olen -
+ sizeof (fcoe_port_list_t))/sizeof (fcoe_port_instance_t);
+
+ count = fcoe_get_port_list(list->ports, list->numPorts);
+
+ if (count > list->numPorts) {
+ fcoeio->fcoeio_status = FCOEIOE_MORE_DATA;
+ ret = ENOSPC;
+ }
+ list->numPorts = count;
+ mutex_exit(&ss->ss_ioctl_mutex);
+
+ break;
+
+ }
+
+ default:
+ return (ENOTTY);
+ }
+
+ FCOE_LOG("fcoe", "fcoe_ioctl returned %d, fcoeio_status = %d",
+ ret, fcoeio->fcoeio_status);
+
+fcoeiocmd_release_buf:
+ if (ret == 0) {
+ ret = fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
+ } else if (fcoeio->fcoeio_status) {
+ (void) fcoe_copyout_iocdata(data, mode, fcoeio, obuf);
+ }
+
+ if (obuf != NULL) {
+ kmem_free(obuf, fcoeio->fcoeio_olen);
+ obuf = NULL;
+ }
+ if (abuf != NULL) {
+ kmem_free(abuf, fcoeio->fcoeio_alen);
+ abuf = NULL;
+ }
+
+ if (ibuf != NULL) {
+ kmem_free(ibuf, fcoeio->fcoeio_ilen);
+ ibuf = NULL;
+ }
+ kmem_free(fcoeio, sizeof (fcoeio_t));
+
+ return (ret);
+}
+
+/*
+ * Finish final initialization
+ */
+static int
+fcoe_attach_init(fcoe_soft_state_t *ss)
+{
+ char taskq_name[TASKQ_NAME_LEN];
+
+ if (ddi_create_minor_node(ss->ss_dip, "admin", S_IFCHR,
+ ddi_get_instance(ss->ss_dip), DDI_PSEUDO, 0) != DDI_SUCCESS) {
+ FCOE_LOG("FCOE", "ddi_create_minor_node failed");
+ return (FCOE_FAILURE);
+ }
+
+ /*
+ * watchdog responsible for release frame and dispatch events
+ */
+ (void) snprintf(taskq_name, sizeof (taskq_name), "fcoe_mac");
+ taskq_name[TASKQ_NAME_LEN - 1] = 0;
+ if ((ss->ss_watchdog_taskq = ddi_taskq_create(NULL,
+ taskq_name, 2, TASKQ_DEFAULTPRI, 0)) == NULL) {
+ return (FCOE_FAILURE);
+ }
+
+ ss->ss_ioctl_flags = 0;
+ mutex_init(&ss->ss_ioctl_mutex, NULL, MUTEX_DRIVER, NULL);
+ list_create(&ss->ss_mac_list, sizeof (fcoe_mac_t),
+ offsetof(fcoe_mac_t, fm_ss_node));
+ list_create(&ss->ss_pfrm_list, sizeof (fcoe_i_frame_t),
+ offsetof(fcoe_i_frame_t, fmi_pending_node));
+
+ mutex_init(&ss->ss_watch_mutex, 0, MUTEX_DRIVER, 0);
+ cv_init(&ss->ss_watch_cv, NULL, CV_DRIVER, NULL);
+ ss->ss_flags &= ~SS_FLAG_TERMINATE_WATCHDOG;
+ (void) ddi_taskq_dispatch(ss->ss_watchdog_taskq,
+ fcoe_watchdog, ss, DDI_SLEEP);
+ while ((ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) == 0) {
+ delay(10);
+ }
+ fcoe_nworkers = ddi_prop_get_int(DDI_DEV_T_ANY, ss->ss_dip,
+ DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, (char *)fcoe_workers_num, 4);
+ if (fcoe_nworkers < 1) {
+ fcoe_nworkers = 4;
+ }
+ fcoe_worker_init();
+
+ ddi_report_dev(ss->ss_dip);
+ return (FCOE_SUCCESS);
+}
+
+/*
+ * Finish final uninitialization
+ */
+static int
+fcoe_detach_uninit(fcoe_soft_state_t *ss)
+{
+ int ret;
+ if (!list_is_empty(&ss->ss_mac_list)) {
+ FCOE_LOG("fcoe", "ss_mac_list is not empty when detach");
+ return (FCOE_FAILURE);
+ }
+
+ if ((ret = fcoe_worker_fini()) != FCOE_SUCCESS) {
+ return (ret);
+ }
+
+ /*
+ * Stop watchdog
+ */
+ if (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
+ mutex_enter(&ss->ss_watch_mutex);
+ ss->ss_flags |= SS_FLAG_TERMINATE_WATCHDOG;
+ cv_broadcast(&ss->ss_watch_cv);
+ mutex_exit(&ss->ss_watch_mutex);
+ while (ss->ss_flags & SS_FLAG_WATCHDOG_RUNNING) {
+ delay(10);
+ }
+ }
+
+ ddi_taskq_destroy(ss->ss_watchdog_taskq);
+ mutex_destroy(&ss->ss_watch_mutex);
+ cv_destroy(&ss->ss_watch_cv);
+
+ ddi_remove_minor_node(ss->ss_dip, NULL);
+ mutex_destroy(&ss->ss_ioctl_mutex);
+ list_destroy(&ss->ss_mac_list);
+
+ return (FCOE_SUCCESS);
+}
+
+/*
+ * Return mac instance if it exist, or else return NULL.
+ */
+fcoe_mac_t *
+fcoe_lookup_mac_by_name(uint8_t *name)
+{
+ fcoe_mac_t *mac = NULL;
+
+ ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
+ for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
+ mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
+ if (strcmp((char *)name, mac->fm_link_name)) {
+ continue;
+ }
+ return (mac);
+ }
+ return (NULL);
+}
+
+/*
+ * port wwn will start with 20:..., node wwn will start with 10:...
+ */
+static void
+fcoe_init_wwn_from_mac(uint8_t *wwn, uint8_t *mac, int is_pwwn, uint8_t idx)
+{
+ ASSERT(wwn != NULL);
+ ASSERT(mac != NULL);
+ wwn[0] = (is_pwwn + 1) << 4;
+ wwn[1] = idx;
+ bcopy(mac, wwn + 2, ETHERADDRL);
+}
+
+/*
+ * Return fcoe_mac if it exists, otherwise create a new one
+ */
+static fcoe_mac_t *
+fcoe_create_mac_by_name(uint8_t *name)
+{
+ fcoe_mac_t *mac = NULL;
+ ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
+
+ mac = fcoe_lookup_mac_by_name(name);
+ if (mac != NULL) {
+ FCOE_LOG("fcoe", "fcoe_create_mac_by_name found one mac %s",
+ name);
+ return (mac);
+ }
+
+ mac = kmem_zalloc(sizeof (fcoe_mac_t), KM_SLEEP);
+ bcopy(name, mac->fm_link_name, strlen((char *)name) + 1);
+ mac->fm_flags = 0;
+ mac->fm_ss = fcoe_global_ss;
+ list_insert_tail(&mac->fm_ss->ss_mac_list, mac);
+ FCOE_LOG("fcoe", "fcoe_create_mac_by_name created one mac %s", name);
+ return (mac);
+}
+
+void
+fcoe_destroy_mac(fcoe_mac_t *mac)
+{
+ ASSERT(mac != NULL);
+ list_remove(&mac->fm_ss->ss_mac_list, mac);
+ kmem_free(mac, sizeof (fcoe_mac_t));
+}
+
+/*
+ * raw frame layout:
+ * ethernet header + vlan header (optional) + FCoE header +
+ * FC frame + FCoE tailer
+ */
+/* ARGSUSED */
+mblk_t *
+fcoe_get_mblk(fcoe_mac_t *mac, uint32_t raw_frame_size)
+{
+ mblk_t *mp;
+ int err;
+
+ /*
+ * FCFH_SIZE + PADDING_SIZE
+ */
+ ASSERT(raw_frame_size >= 60);
+ while ((mp = allocb((size_t)raw_frame_size, 0)) == NULL) {
+ if ((err = strwaitbuf((size_t)raw_frame_size, BPRI_LO)) != 0) {
+ FCOE_LOG("fcoe_get_mblk", "strwaitbuf return %d", err);
+ return (NULL);
+ }
+ }
+ mp->b_wptr = mp->b_rptr + raw_frame_size;
+
+ /*
+ * We should always zero FC frame header
+ */
+ bzero(mp->b_rptr + PADDING_HEADER_SIZE,
+ sizeof (fcoe_fc_frame_header_t));
+ return (mp);
+}
+
+static void
+fcoe_watchdog(void *arg)
+{
+ fcoe_soft_state_t *ss = (fcoe_soft_state_t *)arg;
+ fcoe_i_frame_t *fmi;
+ fcoe_mac_t *mac = NULL;
+
+ FCOE_LOG("fcoe", "fcoe_soft_state is %p", ss);
+
+ mutex_enter(&ss->ss_watch_mutex);
+ ss->ss_flags |= SS_FLAG_WATCHDOG_RUNNING;
+ while ((ss->ss_flags & SS_FLAG_TERMINATE_WATCHDOG) == 0) {
+ while (fmi = (fcoe_i_frame_t *)list_head(&ss->ss_pfrm_list)) {
+ list_remove(&ss->ss_pfrm_list, fmi);
+ mutex_exit(&ss->ss_watch_mutex);
+
+ mac = EPORT2MAC(fmi->fmi_frame->frm_eport);
+ mac->fm_client.ect_release_sol_frame(fmi->fmi_frame);
+
+ mutex_enter(&ss->ss_watch_mutex);
+ mac->fm_frm_cnt--;
+ }
+
+ ss->ss_flags |= SS_FLAG_DOG_WAITING;
+ (void) cv_wait(&ss->ss_watch_cv, &ss->ss_watch_mutex);
+ ss->ss_flags &= ~SS_FLAG_DOG_WAITING;
+ }
+
+ ss->ss_flags &= ~SS_FLAG_WATCHDOG_RUNNING;
+ mutex_exit(&ss->ss_watch_mutex);
+}
+
+static void
+fcoe_worker_init()
+{
+ uint32_t i;
+
+ fcoe_nworkers_running = 0;
+ fcoe_worker_taskq = ddi_taskq_create(0, "FCOE_WORKER_TASKQ",
+ fcoe_nworkers, TASKQ_DEFAULTPRI, 0);
+ fcoe_workers = (fcoe_worker_t *)kmem_zalloc(sizeof (fcoe_worker_t) *
+ fcoe_nworkers, KM_SLEEP);
+ for (i = 0; i < fcoe_nworkers; i++) {
+ fcoe_worker_t *w = &fcoe_workers[i];
+ mutex_init(&w->worker_lock, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&w->worker_cv, NULL, CV_DRIVER, NULL);
+ w->worker_flags &= ~FCOE_WORKER_TERMINATE;
+ list_create(&w->worker_frm_list, sizeof (fcoe_i_frame_t),
+ offsetof(fcoe_i_frame_t, fmi_pending_node));
+ (void) ddi_taskq_dispatch(fcoe_worker_taskq, fcoe_worker_frame,
+ w, DDI_SLEEP);
+ }
+ while (fcoe_nworkers_running != fcoe_nworkers) {
+ delay(10);
+ }
+}
+
+static int
+fcoe_worker_fini()
+{
+ uint32_t i;
+
+ for (i = 0; i < fcoe_nworkers; i++) {
+ fcoe_worker_t *w = &fcoe_workers[i];
+ mutex_enter(&w->worker_lock);
+ if (w->worker_flags & FCOE_WORKER_STARTED) {
+ w->worker_flags |= FCOE_WORKER_TERMINATE;
+ cv_signal(&w->worker_cv);
+ }
+ mutex_exit(&w->worker_lock);
+ }
+
+ while (fcoe_nworkers_running != 0) {
+ delay(drv_usectohz(10000));
+ }
+
+ ddi_taskq_destroy(fcoe_worker_taskq);
+ kmem_free(fcoe_workers, sizeof (fcoe_worker_t) * fcoe_nworkers);
+ fcoe_workers = NULL;
+ return (FCOE_SUCCESS);
+}
+
+static int
+fcoe_crc_verify(fcoe_frame_t *frm)
+{
+ uint32_t crc;
+ uint8_t *crc_array = FRM2FMI(frm)->fmi_fft->fft_crc;
+ uint32_t crc_from_frame = ~(crc_array[0] | (crc_array[1] << 8) |
+ (crc_array[2] << 16) | (crc_array[3] << 24));
+ CRC32(crc, frm->frm_fc_frame, frm->frm_fc_frame_size, -1U, crc32_table);
+ return (crc == crc_from_frame ? FCOE_SUCCESS : FCOE_FAILURE);
+}
+
+static void
+fcoe_worker_frame(void *arg)
+{
+ fcoe_worker_t *w = (fcoe_worker_t *)arg;
+ fcoe_i_frame_t *fmi;
+ int ret;
+
+ atomic_add_32(&fcoe_nworkers_running, 1);
+ mutex_enter(&w->worker_lock);
+ w->worker_flags |= FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE;
+ while ((w->worker_flags & FCOE_WORKER_TERMINATE) == 0) {
+ /*
+ * loop through the frames
+ */
+ while (fmi = list_head(&w->worker_frm_list)) {
+ list_remove(&w->worker_frm_list, fmi);
+ mutex_exit(&w->worker_lock);
+ /*
+ * do the checksum
+ */
+ ret = fcoe_crc_verify(fmi->fmi_frame);
+ if (ret == FCOE_SUCCESS) {
+ fmi->fmi_mac->fm_client.ect_rx_frame(
+ fmi->fmi_frame);
+ } else {
+ fcoe_release_frame(fmi->fmi_frame);
+ }
+ mutex_enter(&w->worker_lock);
+ w->worker_ntasks--;
+ }
+ w->worker_flags &= ~FCOE_WORKER_ACTIVE;
+ cv_wait(&w->worker_cv, &w->worker_lock);
+ w->worker_flags |= FCOE_WORKER_ACTIVE;
+ }
+ w->worker_flags &= ~(FCOE_WORKER_STARTED | FCOE_WORKER_ACTIVE);
+ mutex_exit(&w->worker_lock);
+ atomic_add_32(&fcoe_nworkers_running, -1);
+ list_destroy(&w->worker_frm_list);
+}
+
+void
+fcoe_post_frame(fcoe_frame_t *frm)
+{
+ fcoe_worker_t *w;
+ uint16_t oxid = FRM_OXID(frm);
+
+ w = &fcoe_workers[oxid % fcoe_nworkers_running];
+ mutex_enter(&w->worker_lock);
+ list_insert_tail(&w->worker_frm_list, frm->frm_fcoe_private);
+ w->worker_ntasks++;
+ if ((w->worker_flags & FCOE_WORKER_ACTIVE) == 0) {
+ cv_signal(&w->worker_cv);
+ }
+ mutex_exit(&w->worker_lock);
+}
+
+/*
+ * The max length of every LOG is 158
+ */
+void
+fcoe_trace(caddr_t ident, const char *fmt, ...)
+{
+ va_list args;
+ char tbuf[160];
+ int len;
+ clock_t curclock;
+ clock_t usec;
+
+ if (fcoe_trace_on == 0) {
+ return;
+ }
+
+ curclock = ddi_get_lbolt();
+ usec = (curclock - fcoe_trace_start) * usec_per_tick;
+ len = snprintf(tbuf, 158, "%lu.%03lus 0t%lu %s ", (usec /
+ (1000 * 1000)), ((usec % (1000 * 1000)) / 1000),
+ curclock, (ident ? ident : "unknown"));
+ va_start(args, fmt);
+ len += vsnprintf(tbuf + len, 158 - len, fmt, args);
+ va_end(args);
+
+ if (len > 158) {
+ len = 158;
+ }
+ tbuf[len++] = '\n';
+ tbuf[len] = 0;
+
+ mutex_enter(&fcoe_trace_buf_lock);
+ bcopy(tbuf, &fcoe_trace_buf[fcoe_trace_buf_curndx], len+1);
+ fcoe_trace_buf_curndx += len;
+ if (fcoe_trace_buf_curndx > (fcoe_trace_buf_size - 320)) {
+ fcoe_trace_buf_curndx = 0;
+ }
+ mutex_exit(&fcoe_trace_buf_lock);
+}
+
+/*
+ * Check whether the pwwn or nwwn already exist or not
+ * Return value:
+ * 1: PWWN conflicted
+ * -1: NWWN conflicted
+ * 0: No conflict
+ */
+static int
+fcoe_cmp_wwn(fcoe_mac_t *checkedmac)
+{
+ fcoe_mac_t *mac;
+ uint8_t *nwwn, *pwwn, *cnwwn, *cpwwn;
+
+ cnwwn = checkedmac->fm_eport.eport_nodewwn;
+ cpwwn = checkedmac->fm_eport.eport_portwwn;
+ ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
+
+ for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
+ mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
+ if (mac == checkedmac) {
+ continue;
+ }
+ nwwn = mac->fm_eport.eport_nodewwn;
+ pwwn = mac->fm_eport.eport_portwwn;
+
+ if (memcmp(nwwn, cnwwn, 8) == 0) {
+ return (-1);
+ }
+
+ if (memcmp(pwwn, cpwwn, 8) == 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+static int
+fcoe_get_port_list(fcoe_port_instance_t *ports, int count)
+{
+ fcoe_mac_t *mac = NULL;
+ int i = 0;
+
+ ASSERT(ports != NULL);
+ ASSERT(mutex_owned(&fcoe_global_ss->ss_ioctl_mutex));
+
+ for (mac = list_head(&fcoe_global_ss->ss_mac_list); mac;
+ mac = list_next(&fcoe_global_ss->ss_mac_list, mac)) {
+ if (i < count) {
+ bcopy(mac->fm_eport.eport_portwwn,
+ ports[i].fpi_pwwn, 8);
+ bcopy(mac->fm_link_name,
+ ports[i].fpi_mac_link_name, MAXLINKNAMELEN);
+ bcopy(mac->fm_current_addr,
+ ports[i].fpi_mac_current_addr, ETHERADDRL);
+ bcopy(mac->fm_primary_addr,
+ ports[i].fpi_mac_factory_addr, ETHERADDRL);
+ ports[i].fpi_port_type =
+ EPORT_CLT_TYPE(&mac->fm_eport);
+ ports[i].fpi_mtu_size =
+ mac->fm_eport.eport_mtu;
+ ports[i].fpi_mac_promisc =
+ mac->fm_promisc_handle != NULL ? 1 : 0;
+ }
+ i++;
+ }
+ return (i);
+}