summaryrefslogtreecommitdiff
path: root/usr/src/uts/common/io/softmac
diff options
context:
space:
mode:
authoryz147064 <none@none>2008-01-23 18:09:15 -0800
committeryz147064 <none@none>2008-01-23 18:09:15 -0800
commitd62bc4badc1c1f1549c961cfb8b420e650e1272b (patch)
tree9f466859e9cfb73da13b64261432aba4683f19ad /usr/src/uts/common/io/softmac
parentd38257c4392a9dd690c2f7f2383236c1fc80e509 (diff)
downloadillumos-gate-d62bc4badc1c1f1549c961cfb8b420e650e1272b.tar.gz
PSARC/2006/499 Clearview Nemo unification and vanity naming
PSARC/2007/527 Addendum for Clearview Vanity Naming and Nemo Unification PSARC/2008/002 Clearview UV Updates 6310766 vlan statistics get reset at unplumb time 6320515 dladm commands with "-R" option should not take effect immediately 6433732 Simplify the GLDv3 control path by making its processing asynchronous 6445912 dladm show-link fails to show a specific link in the debug version 6452413 dladm show-link doesn't show VLAN links for GLDv2 drivers 6504433 libwladm's use of wladm_wlresult2status() needs an overhaul 6504507 dladm set-linkprop failure message is unclear 6534289 DR should work with aggregations 6535719 dladm_aggr_port_attr_db_t`lp_devname should be MAXNAMELEN, not MAXNAMELEN + 1 6539634 GLDv3 should DL_ERROR_ACK a DL_UDQOS_REQ with DL_OUTSTATE when the stream is DL_UNATTACHED 6540246 libdladm should not guess zoneid from DLDIOCZIDGET ioctl errno 6544195 dladm show-dev assumes GLDv3 stats.. incompatible with GLDv2 6563295 dladm show-linkprop -P does not work properly for unavailable links 6577618 integrate network vanity naming and nemo unification 6600446 links assigned to a local zone are still aggregatable by global zone 6607572 "boot net - install" can trigger assertion failure in dld_str_attach() 6613956 "svccfg import -" does not work as bfu expects 6637596 invalid assertion in ip_soft_ring_assignment() 6642350 kernel DLPI processing routines are long overdue 6643338 GLDv3 PPA hack VLAN ID checks don't always work 6647203 bfu: smf_delete_manifest() does not work for non-global zones 6649885 DL_IB GLDv3 mactype plugin must fill in its mtr_nativetype 6650395 libuuid should be lint-clean and linted nightly --HG-- rename : usr/src/cmd/dladm/aggregation.conf => deleted_files/usr/src/cmd/dladm/aggregation.conf rename : usr/src/cmd/dladm/linkprop.conf => deleted_files/usr/src/cmd/dladm/linkprop.conf rename : usr/src/lib/libinetcfg/common/inetcfg_nic.c => deleted_files/usr/src/lib/libinetcfg/common/inetcfg_nic.c rename : usr/src/lib/libinetcfg/common/inetcfg_nic.h => deleted_files/usr/src/lib/libinetcfg/common/inetcfg_nic.h
Diffstat (limited to 'usr/src/uts/common/io/softmac')
-rw-r--r--usr/src/uts/common/io/softmac/softmac.conf27
-rw-r--r--usr/src/uts/common/io/softmac/softmac_capab.c756
-rw-r--r--usr/src/uts/common/io/softmac/softmac_ctl.c389
-rw-r--r--usr/src/uts/common/io/softmac/softmac_dev.c417
-rw-r--r--usr/src/uts/common/io/softmac/softmac_main.c1192
-rw-r--r--usr/src/uts/common/io/softmac/softmac_pkt.c320
-rw-r--r--usr/src/uts/common/io/softmac/softmac_stat.c270
7 files changed, 3371 insertions, 0 deletions
diff --git a/usr/src/uts/common/io/softmac/softmac.conf b/usr/src/uts/common/io/softmac/softmac.conf
new file mode 100644
index 0000000000..72163c27e6
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac.conf
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+name="softmac" parent="pseudo" instance=0;
diff --git a/usr/src/uts/common/io/softmac/softmac_capab.c b/usr/src/uts/common/io/softmac/softmac_capab.c
new file mode 100644
index 0000000000..d1178d19aa
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_capab.c
@@ -0,0 +1,756 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/mac.h>
+#include <sys/softmac_impl.h>
+
+typedef struct softmac_capab_ops {
+ int (*sc_hcksum_ack)(void *, t_uscalar_t);
+ int (*sc_zcopy_ack)(void *, t_uscalar_t);
+ int (*sc_mdt_ack)(void *, dl_capab_mdt_t *);
+} softmac_capab_ops_t;
+
+static int dl_capab(ldi_handle_t, mblk_t **);
+static int softmac_fill_hcksum_ack(void *, t_uscalar_t);
+static int softmac_fill_zcopy_ack(void *, t_uscalar_t);
+static int softmac_fill_mdt_ack(void *, dl_capab_mdt_t *);
+static int softmac_adv_hcksum_ack(void *, t_uscalar_t);
+static int softmac_adv_zcopy_ack(void *, t_uscalar_t);
+static int softmac_adv_mdt_ack(void *, dl_capab_mdt_t *);
+static int softmac_enable_hcksum_ack(void *, t_uscalar_t);
+static int softmac_enable_mdt_ack(void *, dl_capab_mdt_t *);
+static int softmac_capab_send(softmac_lower_t *, boolean_t);
+static int i_capab_ack(mblk_t *, queue_t *, softmac_capab_ops_t *, void *);
+static int i_capab_id_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
+ softmac_capab_ops_t *, void *);
+static int i_capab_sub_ack(mblk_t *, dl_capability_sub_t *, queue_t *,
+ softmac_capab_ops_t *, void *);
+static int i_capab_hcksum_ack(dl_capab_hcksum_t *, queue_t *,
+ softmac_capab_ops_t *, void *);
+static int i_capab_zcopy_ack(dl_capab_zerocopy_t *, queue_t *,
+ softmac_capab_ops_t *, void *);
+static int i_capab_mdt_ack(dl_capab_mdt_t *, queue_t *,
+ softmac_capab_ops_t *, void *);
+static int i_capab_hcksum_verify(dl_capab_hcksum_t *, queue_t *);
+static int i_capab_zcopy_verify(dl_capab_zerocopy_t *, queue_t *);
+static int i_capab_mdt_verify(dl_capab_mdt_t *, queue_t *);
+
+static softmac_capab_ops_t softmac_fill_capab_ops =
+{
+ softmac_fill_hcksum_ack,
+ softmac_fill_zcopy_ack,
+ softmac_fill_mdt_ack,
+};
+
+static softmac_capab_ops_t softmac_adv_capab_ops =
+{
+ softmac_adv_hcksum_ack,
+ softmac_adv_zcopy_ack,
+ softmac_adv_mdt_ack
+};
+
+static softmac_capab_ops_t softmac_enable_capab_ops =
+{
+ softmac_enable_hcksum_ack,
+ NULL,
+ softmac_enable_mdt_ack
+};
+
+int
+softmac_fill_capab(ldi_handle_t lh, softmac_t *softmac)
+{
+ mblk_t *mp = NULL;
+ union DL_primitives *prim;
+ int err = 0;
+
+ if ((err = dl_capab(lh, &mp)) != 0)
+ goto exit;
+
+ prim = (union DL_primitives *)mp->b_rptr;
+ if (prim->dl_primitive == DL_ERROR_ACK) {
+ err = -1;
+ goto exit;
+ }
+
+ err = i_capab_ack(mp, NULL, &softmac_fill_capab_ops, softmac);
+
+exit:
+ freemsg(mp);
+ return (err);
+}
+
+static int
+dl_capab(ldi_handle_t lh, mblk_t **mpp)
+{
+ dl_capability_req_t *capb;
+ union DL_primitives *dl_prim;
+ mblk_t *mp;
+ int err;
+
+ if ((mp = allocb(sizeof (dl_capability_req_t), BPRI_MED)) == NULL)
+ return (ENOMEM);
+ mp->b_datap->db_type = M_PROTO;
+
+ capb = (dl_capability_req_t *)mp->b_wptr;
+ mp->b_wptr += sizeof (dl_capability_req_t);
+ bzero(mp->b_rptr, sizeof (dl_capability_req_t));
+ capb->dl_primitive = DL_CAPABILITY_REQ;
+
+ (void) ldi_putmsg(lh, mp);
+ if ((err = ldi_getmsg(lh, &mp, (timestruc_t *)NULL)) != 0)
+ return (err);
+
+ dl_prim = (union DL_primitives *)mp->b_rptr;
+ switch (dl_prim->dl_primitive) {
+ case DL_CAPABILITY_ACK:
+ if (MBLKL(mp) < DL_CAPABILITY_ACK_SIZE) {
+ printf("dl_capability: DL_CAPABILITY_ACK "
+ "protocol err\n");
+ break;
+ }
+ *mpp = mp;
+ return (0);
+
+ case DL_ERROR_ACK:
+ if (MBLKL(mp) < DL_ERROR_ACK_SIZE) {
+ printf("dl_capability: DL_ERROR_ACK protocol err\n");
+ break;
+ }
+ if (((dl_error_ack_t *)dl_prim)->dl_error_primitive !=
+ DL_CAPABILITY_REQ) {
+ printf("dl_capability: DL_ERROR_ACK rtnd prim %u\n",
+ ((dl_error_ack_t *)dl_prim)->dl_error_primitive);
+ break;
+ }
+
+ *mpp = mp;
+ return (0);
+
+ default:
+ printf("dl_capability: bad ACK header %u\n",
+ dl_prim->dl_primitive);
+ break;
+ }
+
+ freemsg(mp);
+ return (-1);
+}
+
+static int
+softmac_fill_hcksum_ack(void *arg, t_uscalar_t flags)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * There are two types of acks we process here:
+ * 1. acks in reply to a (first form) generic capability req
+ * (no ENABLE flag set)
+ * 2. acks in reply to a ENABLE capability req.
+ * (ENABLE flag set)
+ * Only the first type should be expected here.
+ */
+
+ if (flags & HCKSUM_ENABLE) {
+ cmn_err(CE_WARN, "softmac_fill_hcksum_ack: unexpected "
+ "HCKSUM_ENABLE flag in hardware checksum capability");
+ } else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
+ HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
+ softmac->smac_capab_flags |= MAC_CAPAB_HCKSUM;
+ softmac->smac_hcksum_txflags = flags;
+ }
+ return (0);
+}
+
+static int
+softmac_fill_zcopy_ack(void *arg, t_uscalar_t flags)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
+ softmac->smac_capab_flags &= (~MAC_CAPAB_NO_ZCOPY);
+ return (0);
+}
+
+static int
+softmac_fill_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * There are two types of acks we process here:
+ * 1. acks in reply to a (first form) generic capability req
+ * (ENABLE flag might be set by some drivers)
+ * 2. acks in reply to a ENABLE capability req.
+ * (ENABLE flag set)
+ */
+
+ ASSERT(mdt->mdt_version == MDT_VERSION_2);
+ softmac->smac_mdt = B_TRUE;
+ softmac->smac_mdt_capab.mdt_hdr_head = mdt->mdt_hdr_head;
+ softmac->smac_mdt_capab.mdt_hdr_tail = mdt->mdt_hdr_tail;
+ softmac->smac_mdt_capab.mdt_max_pld = mdt->mdt_max_pld;
+ softmac->smac_mdt_capab.mdt_span_limit = mdt->mdt_span_limit;
+ return (0);
+}
+
+int
+softmac_capab_enable(softmac_lower_t *slp)
+{
+ softmac_t *softmac = slp->sl_softmac;
+ int err;
+
+ if (softmac->smac_no_capability_req)
+ return (0);
+
+ /*
+ * Send DL_CAPABILITY_REQ to get capability advertisement.
+ */
+ if ((err = softmac_capab_send(slp, B_FALSE)) != 0)
+ return (err);
+
+ /*
+ * Send DL_CAPABILITY_REQ to enable specific capabilities.
+ */
+ if ((err = softmac_capab_send(slp, B_TRUE)) != 0)
+ return (err);
+
+ return (0);
+}
+
+static int
+softmac_capab_send(softmac_lower_t *slp, boolean_t enable)
+{
+ softmac_t *softmac;
+ dl_capability_req_t *capb;
+ dl_capability_sub_t *subcapb;
+ mblk_t *reqmp, *ackmp;
+ int err;
+ size_t size = 0;
+
+ softmac = slp->sl_softmac;
+
+ if (enable) {
+ /* No need to enable DL_CAPAB_ZEROCOPY */
+ if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)
+ size += sizeof (dl_capability_sub_t) +
+ sizeof (dl_capab_hcksum_t);
+
+ if (softmac->smac_mdt) {
+ if (!(softmac->smac_mdt_capab.mdt_flags &
+ DL_CAPAB_MDT_ENABLE)) {
+ /*
+ * The MDT capability was not enabled for the
+ * first time, enable it now.
+ */
+ size += sizeof (dl_capability_sub_t) +
+ sizeof (dl_capab_mdt_t);
+ }
+ }
+
+ if (size == 0)
+ return (0);
+ }
+
+ /*
+ * Create DL_CAPABILITY_REQ message and send it down
+ */
+ reqmp = allocb(sizeof (dl_capability_req_t) + size, BPRI_MED);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ bzero(reqmp->b_rptr, sizeof (dl_capability_req_t) + size);
+
+ DB_TYPE(reqmp) = M_PROTO;
+ reqmp->b_wptr = reqmp->b_rptr + sizeof (dl_capability_req_t) + size;
+
+ capb = (dl_capability_req_t *)reqmp->b_rptr;
+ capb->dl_primitive = DL_CAPABILITY_REQ;
+
+ if (!enable)
+ goto output;
+
+ capb->dl_sub_offset = sizeof (dl_capability_req_t);
+
+ if (softmac->smac_capab_flags & MAC_CAPAB_HCKSUM) {
+ dl_capab_hcksum_t *hck_subcapp;
+
+ size = sizeof (dl_capability_sub_t) +
+ sizeof (dl_capab_hcksum_t);
+ capb->dl_sub_length += size;
+
+ subcapb = (dl_capability_sub_t *)(capb + 1);
+ subcapb->dl_cap = DL_CAPAB_HCKSUM;
+ subcapb->dl_length = sizeof (dl_capab_hcksum_t);
+ hck_subcapp = (dl_capab_hcksum_t *)(subcapb + 1);
+ hck_subcapp->hcksum_version = HCKSUM_VERSION_1;
+ hck_subcapp->hcksum_txflags =
+ softmac->smac_hcksum_txflags | HCKSUM_ENABLE;
+ }
+
+ if (softmac->smac_mdt) {
+ if (!(softmac->smac_mdt_capab.mdt_flags &
+ DL_CAPAB_MDT_ENABLE)) {
+ dl_capab_mdt_t *mdt_subcapp;
+
+ size = sizeof (dl_capability_sub_t) +
+ sizeof (dl_capab_mdt_t);
+ capb->dl_sub_length += size;
+
+ subcapb = (dl_capability_sub_t *)
+ ((uint8_t *)(subcapb + 1) + subcapb->dl_length);
+
+ subcapb->dl_cap = DL_CAPAB_MDT;
+ subcapb->dl_length = sizeof (dl_capab_mdt_t);
+ mdt_subcapp = (dl_capab_mdt_t *)(subcapb + 1);
+ mdt_subcapp->mdt_version = MDT_VERSION_2;
+ mdt_subcapp->mdt_flags =
+ (softmac->smac_mdt_capab.mdt_flags |
+ DL_CAPAB_MDT_ENABLE);
+ mdt_subcapp->mdt_hdr_head =
+ softmac->smac_mdt_capab.mdt_hdr_head;
+ mdt_subcapp->mdt_hdr_tail =
+ softmac->smac_mdt_capab.mdt_hdr_tail;
+ mdt_subcapp->mdt_max_pld =
+ softmac->smac_mdt_capab.mdt_max_pld;
+ mdt_subcapp->mdt_span_limit =
+ softmac->smac_mdt_capab.mdt_span_limit;
+ }
+ }
+
+output:
+ err = softmac_proto_tx(slp, reqmp, &ackmp);
+ if (err == 0) {
+ if (enable) {
+ err = i_capab_ack(ackmp, NULL,
+ &softmac_enable_capab_ops, softmac);
+ } else {
+ err = i_capab_ack(ackmp, NULL,
+ &softmac_adv_capab_ops, softmac);
+ }
+ }
+ freemsg(ackmp);
+
+ return (err);
+}
+
+static int
+softmac_adv_hcksum_ack(void *arg, t_uscalar_t flags)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * There are two types of acks we process here:
+ * 1. acks in reply to a (first form) generic capability req
+ * (no ENABLE flag set)
+ * 2. acks in reply to a ENABLE capability req.
+ * (ENABLE flag set)
+ * Only the first type should be expected here.
+ */
+
+ if (flags & HCKSUM_ENABLE) {
+ cmn_err(CE_WARN, "softmac_adv_hcksum_ack: unexpected "
+ "HCKSUM_ENABLE flag in hardware checksum capability");
+ return (-1);
+ } else if (flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
+ HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM)) {
+ /*
+ * The acknowledgement should be the same as we got when
+ * the softmac is created.
+ */
+ if (!(softmac->smac_capab_flags & MAC_CAPAB_HCKSUM)) {
+ ASSERT(B_FALSE);
+ return (-1);
+ }
+ if (softmac->smac_hcksum_txflags != flags) {
+ ASSERT(B_FALSE);
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+softmac_adv_zcopy_ack(void *arg, t_uscalar_t flags)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * The acknowledgement should be the same as we got when
+ * the softmac is created.
+ */
+ ASSERT(flags == DL_CAPAB_VMSAFE_MEM);
+ if (softmac->smac_capab_flags & MAC_CAPAB_NO_ZCOPY) {
+ ASSERT(B_FALSE);
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+softmac_adv_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * The acknowledgement should be the same as we got when
+ * the softmac is created.
+ */
+ if (!softmac->smac_mdt) {
+ ASSERT(B_FALSE);
+ return (-1);
+ }
+
+ if ((softmac->smac_mdt_capab.mdt_hdr_head != mdt->mdt_hdr_head) ||
+ (softmac->smac_mdt_capab.mdt_hdr_tail != mdt->mdt_hdr_tail) ||
+ (softmac->smac_mdt_capab.mdt_max_pld != mdt->mdt_max_pld) ||
+ (softmac->smac_mdt_capab.mdt_span_limit != mdt->mdt_span_limit)) {
+ ASSERT(B_FALSE);
+ return (-1);
+ }
+ /*
+ * We need the mdt_flags field to know whether an additional
+ * DL_CAPAB_MDT_ENABLE is necessary.
+ */
+ softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
+ return (0);
+}
+
+static int
+softmac_enable_hcksum_ack(void *arg, t_uscalar_t flags)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * There are two types of acks we process here:
+ * 1. acks in reply to a (first form) generic capability req
+ * (no ENABLE flag set)
+ * 2. acks in reply to a ENABLE capability req.
+ * (ENABLE flag set)
+ * Only the second type should be expected here.
+ */
+
+ if (flags & HCKSUM_ENABLE) {
+ if ((flags & ~HCKSUM_ENABLE) != softmac->smac_hcksum_txflags) {
+ cmn_err(CE_WARN, "softmac_enable_hcksum_ack: unexpected"
+ " hardware capability flag value 0x%x", flags);
+ return (-1);
+ }
+ } else {
+ cmn_err(CE_WARN, "softmac_enable_hcksum_ack: "
+ "hardware checksum flag HCKSUM_ENABLE is not set");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+softmac_enable_mdt_ack(void *arg, dl_capab_mdt_t *mdt)
+{
+ softmac_t *softmac = (softmac_t *)arg;
+
+ /*
+ * There are two types of acks we process here:
+ * 1. acks in reply to a (first form) generic capability req
+ * (no ENABLE flag set)
+ * 2. acks in reply to a ENABLE capability req.
+ * (ENABLE flag set)
+ * Only the second type should be expected here.
+ */
+
+ if (mdt->mdt_flags & DL_CAPAB_MDT_ENABLE) {
+ if ((softmac->smac_mdt_capab.mdt_hdr_head !=
+ mdt->mdt_hdr_head) ||
+ (softmac->smac_mdt_capab.mdt_hdr_tail !=
+ mdt->mdt_hdr_tail) ||
+ (softmac->smac_mdt_capab.mdt_max_pld !=
+ mdt->mdt_max_pld) ||
+ (softmac->smac_mdt_capab.mdt_span_limit !=
+ mdt->mdt_span_limit)) {
+ cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
+ "unexpected MDT capability value");
+ return (-1);
+ }
+ softmac->smac_mdt_capab.mdt_flags = mdt->mdt_flags;
+ } else {
+ cmn_err(CE_WARN, "softmac_enable_mdt_ack: "
+ "MDT flag DL_CAPAB_MDT_ENABLE is not set");
+ return (-1);
+ }
+
+ return (0);
+}
+
+static int
+i_capab_ack(mblk_t *mp, queue_t *q, softmac_capab_ops_t *op, void *arg)
+{
+ union DL_primitives *prim;
+ dl_capability_ack_t *cap;
+ dl_capability_sub_t *sub, *end;
+ int err = 0;
+
+ prim = (union DL_primitives *)mp->b_rptr;
+ ASSERT(prim->dl_primitive == DL_CAPABILITY_ACK);
+
+ cap = (dl_capability_ack_t *)prim;
+ if (cap->dl_sub_length == 0)
+ goto exit;
+
+ /* Is dl_sub_length correct? */
+ if ((sizeof (*cap) + cap->dl_sub_length) > MBLKL(mp)) {
+ err = EINVAL;
+ goto exit;
+ }
+
+ sub = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_offset);
+ end = (dl_capability_sub_t *)((caddr_t)cap + cap->dl_sub_length
+ - sizeof (*sub));
+ for (; (sub <= end) && (err == 0); ) {
+ switch (sub->dl_cap) {
+ case DL_CAPAB_ID_WRAPPER:
+ err = i_capab_id_ack(mp, sub, q, op, arg);
+ break;
+ default:
+ err = i_capab_sub_ack(mp, sub, q, op, arg);
+ break;
+ }
+ sub = (dl_capability_sub_t *)((caddr_t)sub + sizeof (*sub)
+ + sub->dl_length);
+ }
+
+exit:
+ return (err);
+}
+
+static int
+i_capab_id_ack(mblk_t *mp, dl_capability_sub_t *outers,
+ queue_t *q, softmac_capab_ops_t *op, void *arg)
+{
+ dl_capab_id_t *capab_id;
+ dl_capability_sub_t *inners;
+ caddr_t capend;
+ int err = EINVAL;
+
+ ASSERT(outers->dl_cap == DL_CAPAB_ID_WRAPPER);
+
+ capend = (caddr_t)(outers + 1) + outers->dl_length;
+ if (capend > (caddr_t)mp->b_wptr) {
+ cmn_err(CE_WARN, "i_capab_id_ack: malformed "
+ "sub-capability too long");
+ return (err);
+ }
+
+ capab_id = (dl_capab_id_t *)(outers + 1);
+
+ if (outers->dl_length < sizeof (*capab_id) ||
+ (inners = &capab_id->id_subcap,
+ inners->dl_length > (outers->dl_length - sizeof (*inners)))) {
+ cmn_err(CE_WARN, "i_capab_id_ack: malformed "
+ "encapsulated capab type %d too long",
+ inners->dl_cap);
+ return (err);
+ }
+
+ if ((q != NULL) && (!dlcapabcheckqid(&capab_id->id_mid, q))) {
+ cmn_err(CE_WARN, "i_capab_id_ack: pass-thru module(s) "
+ "detected, discarding capab type %d", inners->dl_cap);
+ return (err);
+ }
+
+ /* Process the encapsulated sub-capability */
+ return (i_capab_sub_ack(mp, inners, q, op, arg));
+}
+
+static int
+i_capab_sub_ack(mblk_t *mp, dl_capability_sub_t *sub, queue_t *q,
+ softmac_capab_ops_t *op, void *arg)
+{
+ caddr_t capend;
+ dl_capab_hcksum_t *hcksum;
+ dl_capab_zerocopy_t *zcopy;
+ dl_capab_mdt_t *mdt;
+ int err = 0;
+
+ capend = (caddr_t)(sub + 1) + sub->dl_length;
+ if (capend > (caddr_t)mp->b_wptr) {
+ cmn_err(CE_WARN, "i_capab_sub_ack: "
+ "malformed sub-capability too long");
+ return (EINVAL);
+ }
+
+ switch (sub->dl_cap) {
+ case DL_CAPAB_HCKSUM:
+ hcksum = (dl_capab_hcksum_t *)(sub + 1);
+ err = i_capab_hcksum_ack(hcksum, q, op, arg);
+ break;
+
+ case DL_CAPAB_ZEROCOPY:
+ zcopy = (dl_capab_zerocopy_t *)(sub + 1);
+ err = i_capab_zcopy_ack(zcopy, q, op, arg);
+ break;
+
+ case DL_CAPAB_MDT:
+ mdt = (dl_capab_mdt_t *)(sub + 1);
+ err = i_capab_mdt_ack(mdt, q, op, arg);
+ break;
+
+ default:
+ cmn_err(CE_WARN, "i_capab_sub_ack: unknown capab type %d",
+ sub->dl_cap);
+ err = EINVAL;
+ }
+
+ return (err);
+}
+
+static int
+i_capab_hcksum_ack(dl_capab_hcksum_t *hcksum, queue_t *q,
+ softmac_capab_ops_t *op, void *arg)
+{
+ t_uscalar_t flags;
+ int err = 0;
+
+ if ((err = i_capab_hcksum_verify(hcksum, q)) != 0)
+ return (err);
+
+ flags = hcksum->hcksum_txflags;
+
+ if (!(flags & (HCKSUM_INET_PARTIAL | HCKSUM_INET_FULL_V4 |
+ HCKSUM_INET_FULL_V6 | HCKSUM_IPHDRCKSUM | HCKSUM_ENABLE))) {
+ cmn_err(CE_WARN, "i_capab_hcksum_ack: invalid "
+ "hardware checksum capability flags 0x%x", flags);
+ return (EINVAL);
+ }
+
+ if (op->sc_hcksum_ack)
+ return (op->sc_hcksum_ack(arg, flags));
+ else {
+ cmn_err(CE_WARN, "i_capab_hcksum_ack: unexpected hardware "
+ "checksum acknowledgement");
+ return (EINVAL);
+ }
+}
+
+static int
+i_capab_zcopy_ack(dl_capab_zerocopy_t *zcopy, queue_t *q,
+ softmac_capab_ops_t *op, void *arg)
+{
+ t_uscalar_t flags;
+ int err = 0;
+
+ if ((err = i_capab_zcopy_verify(zcopy, q)) != 0)
+ return (err);
+
+ flags = zcopy->zerocopy_flags;
+ if (!(flags & DL_CAPAB_VMSAFE_MEM)) {
+ cmn_err(CE_WARN, "i_capab_zcopy_ack: invalid zcopy capability "
+ "flags 0x%x", flags);
+ return (EINVAL);
+ }
+ if (op->sc_zcopy_ack)
+ return (op->sc_zcopy_ack(arg, flags));
+ else {
+ cmn_err(CE_WARN, "i_capab_zcopy_ack: unexpected zcopy "
+ "acknowledgement");
+ return (EINVAL);
+ }
+}
+
+static int
+i_capab_mdt_ack(dl_capab_mdt_t *mdt, queue_t *q,
+ softmac_capab_ops_t *op, void *arg)
+{
+ int err;
+
+ if ((err = i_capab_mdt_verify(mdt, q)) != 0)
+ return (err);
+
+ if (op->sc_mdt_ack)
+ return (op->sc_mdt_ack(arg, mdt));
+ else {
+ cmn_err(CE_WARN, "i_capab_mdt_ack: unexpected MDT "
+ "acknowledgement");
+ return (EINVAL);
+ }
+}
+
+static int
+i_capab_hcksum_verify(dl_capab_hcksum_t *hcksum, queue_t *q)
+{
+ if (hcksum->hcksum_version != HCKSUM_VERSION_1) {
+ cmn_err(CE_WARN, "i_capab_hcksum_verify: "
+ "unsupported hardware checksum capability (version %d, "
+ "expected %d)", hcksum->hcksum_version, HCKSUM_VERSION_1);
+ return (-1);
+ }
+
+ if ((q != NULL) && !dlcapabcheckqid(&hcksum->hcksum_mid, q)) {
+ cmn_err(CE_WARN, "i_capab_hcksum_verify: unexpected pass-thru "
+ "module detected; hardware checksum capability discarded");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+i_capab_zcopy_verify(dl_capab_zerocopy_t *zcopy, queue_t *q)
+{
+ if (zcopy->zerocopy_version != ZEROCOPY_VERSION_1) {
+ cmn_err(CE_WARN, "i_capab_zcopy_verify: unsupported zcopy "
+ "capability (version %d, expected %d)",
+ zcopy->zerocopy_version, ZEROCOPY_VERSION_1);
+ return (-1);
+ }
+
+ if ((q != NULL) && !dlcapabcheckqid(&zcopy->zerocopy_mid, q)) {
+ cmn_err(CE_WARN, "i_capab_zcopy_verify: unexpected pass-thru "
+ "module detected; zcopy checksum capability discarded");
+ return (-1);
+ }
+ return (0);
+}
+
+static int
+i_capab_mdt_verify(dl_capab_mdt_t *mdt, queue_t *q)
+{
+ if (mdt->mdt_version != MDT_VERSION_2) {
+ cmn_err(CE_WARN, "i_capab_mdt_verify: unsupported MDT "
+ "capability (version %d, expected %d)",
+ mdt->mdt_version, MDT_VERSION_2);
+ return (-1);
+ }
+
+ if ((q != NULL) && !dlcapabcheckqid(&mdt->mdt_mid, q)) {
+ cmn_err(CE_WARN, "i_capab_mdt_verify: unexpected pass-thru "
+ "module detected; MDT capability discarded");
+ return (-1);
+ }
+ return (0);
+}
diff --git a/usr/src/uts/common/io/softmac/softmac_ctl.c b/usr/src/uts/common/io/softmac/softmac_ctl.c
new file mode 100644
index 0000000000..33472bd303
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_ctl.c
@@ -0,0 +1,389 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/stropts.h>
+#include <sys/softmac_impl.h>
+
+int
+softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications)
+{
+ mblk_t *reqmp;
+
+ /*
+ * create notify req message and send it down
+ */
+ reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO,
+ DL_NOTIFY_REQ);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ ((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications;
+
+ return (softmac_proto_tx(slp, reqmp, NULL));
+}
+
+int
+softmac_send_bind_req(softmac_lower_t *slp, uint_t sap)
+{
+ dl_bind_req_t *bind;
+ mblk_t *reqmp;
+
+ /*
+ * create bind req message and send it down
+ */
+ reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ bind = (dl_bind_req_t *)reqmp->b_rptr;
+ bind->dl_sap = sap;
+ bind->dl_conn_mgmt = 0;
+ bind->dl_max_conind = 0;
+ bind->dl_xidtest_flg = 0;
+ bind->dl_service_mode = DL_CLDLS;
+
+ return (softmac_proto_tx(slp, reqmp, NULL));
+}
+
+int
+softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on)
+{
+ mblk_t *reqmp;
+ size_t size;
+ t_uscalar_t dl_prim;
+
+ /*
+ * create promisc message and send it down
+ */
+ if (on) {
+ dl_prim = DL_PROMISCON_REQ;
+ size = DL_PROMISCON_REQ_SIZE;
+ } else {
+ dl_prim = DL_PROMISCOFF_REQ;
+ size = DL_PROMISCOFF_REQ_SIZE;
+ }
+
+ reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ if (on)
+ ((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level;
+ else
+ ((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level;
+
+ return (softmac_proto_tx(slp, reqmp, NULL));
+}
+
+int
+softmac_m_promisc(void *arg, boolean_t on)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp = softmac->smac_lower;
+
+ ASSERT(slp != NULL);
+ return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on));
+}
+
+int
+softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp;
+ dl_enabmulti_req_t *enabmulti;
+ dl_disabmulti_req_t *disabmulti;
+ mblk_t *reqmp;
+ t_uscalar_t dl_prim;
+ uint32_t size, addr_length;
+
+ /*
+ * create multicst message and send it down
+ */
+ addr_length = softmac->smac_addrlen;
+ if (add) {
+ size = sizeof (dl_enabmulti_req_t) + addr_length;
+ dl_prim = DL_ENABMULTI_REQ;
+ } else {
+ size = sizeof (dl_disabmulti_req_t) + addr_length;
+ dl_prim = DL_DISABMULTI_REQ;
+ }
+
+ reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ if (add) {
+ enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr;
+ enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t);
+ enabmulti->dl_addr_length = addr_length;
+ (void) memcpy(&enabmulti[1], mca, addr_length);
+ } else {
+ disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr;
+ disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t);
+ disabmulti->dl_addr_length = addr_length;
+ (void) memcpy(&disabmulti[1], mca, addr_length);
+ }
+
+ slp = softmac->smac_lower;
+ ASSERT(slp != NULL);
+ return (softmac_proto_tx(slp, reqmp, NULL));
+}
+
+int
+softmac_m_unicst(void *arg, const uint8_t *macaddr)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp;
+ dl_set_phys_addr_req_t *phyaddr;
+ mblk_t *reqmp;
+ size_t size;
+
+ /*
+ * create set_phys_addr message and send it down
+ */
+ size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen;
+ reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ);
+ if (reqmp == NULL)
+ return (ENOMEM);
+
+ phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr;
+ phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
+ phyaddr->dl_addr_length = softmac->smac_addrlen;
+ (void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen);
+
+ slp = softmac->smac_lower;
+ ASSERT(slp != NULL);
+ return (softmac_proto_tx(slp, reqmp, NULL));
+}
+
+void
+softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
+{
+ softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower;
+ mblk_t *ackmp;
+
+ ASSERT(slp != NULL);
+ softmac_ioctl_tx(slp, mp, &ackmp);
+ qreply(wq, ackmp);
+}
+
+static void
+softmac_process_notify_ind(queue_t *rq, mblk_t *mp)
+{
+ softmac_lower_t *slp = rq->q_ptr;
+ dl_notify_ind_t *dlnip = (dl_notify_ind_t *)mp->b_rptr;
+ softmac_t *softmac = slp->sl_softmac;
+ uint_t addroff, addrlen;
+
+ ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND);
+
+ switch (dlnip->dl_notification) {
+ case DL_NOTE_PHYS_ADDR:
+ if (dlnip->dl_data != DL_CURR_PHYS_ADDR)
+ break;
+
+ addroff = dlnip->dl_addr_offset;
+ addrlen = dlnip->dl_addr_length - softmac->smac_saplen;
+ if (addroff == 0 || addrlen != softmac->smac_addrlen ||
+ !MBLKIN(mp, addroff, addrlen)) {
+ cmn_err(CE_NOTE, "softmac: got malformed "
+ "DL_NOTIFY_IND; length/offset %d/%d",
+ addrlen, addroff);
+ break;
+ }
+
+ mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff);
+ break;
+
+ case DL_NOTE_LINK_UP:
+ mac_link_update(softmac->smac_mh, LINK_STATE_UP);
+ break;
+
+ case DL_NOTE_LINK_DOWN:
+ mac_link_update(softmac->smac_mh, LINK_STATE_DOWN);
+ break;
+ }
+
+ freemsg(mp);
+}
+
+static void
+softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen,
+ t_uscalar_t reqprim)
+{
+ const char *ackname;
+
+ ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive);
+
+ if (MBLKL(mp) < minlen) {
+ cmn_err(CE_WARN, "softmac: got short %s", ackname);
+ freemsg(mp);
+ return;
+ }
+
+ mutex_enter(&slp->sl_mutex);
+ if (slp->sl_pending_prim != reqprim) {
+ cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname);
+ mutex_exit(&slp->sl_mutex);
+ freemsg(mp);
+ return;
+ }
+
+ slp->sl_pending_prim = DL_PRIM_INVAL;
+ slp->sl_ack_mp = mp;
+ cv_signal(&slp->sl_cv);
+ mutex_exit(&slp->sl_mutex);
+}
+
+void
+softmac_rput_process_proto(queue_t *rq, mblk_t *mp)
+{
+ softmac_lower_t *slp = rq->q_ptr;
+ union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr;
+ ssize_t len = MBLKL(mp);
+ const char *primstr;
+
+ if (len < sizeof (t_uscalar_t)) {
+ cmn_err(CE_WARN, "softmac: got runt DLPI message");
+ goto exit;
+ }
+
+ primstr = dl_primstr(dlp->dl_primitive);
+
+ switch (dlp->dl_primitive) {
+ case DL_OK_ACK:
+ if (len < DL_OK_ACK_SIZE)
+ goto runt;
+
+ softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE,
+ dlp->ok_ack.dl_correct_primitive);
+ return;
+
+ case DL_ERROR_ACK:
+ if (len < DL_ERROR_ACK_SIZE)
+ goto runt;
+
+ cmn_err(CE_NOTE, "softmac: received DL_ERROR_ACK for "
+ "%s errno/unix_errno 0x%x/%d",
+ dl_primstr(dlp->error_ack.dl_error_primitive),
+ dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno);
+
+ softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE,
+ dlp->error_ack.dl_error_primitive);
+ return;
+
+ case DL_NOTIFY_IND:
+ if (len < DL_NOTIFY_IND_SIZE)
+ goto runt;
+
+ softmac_process_notify_ind(rq, mp);
+ return;
+
+ case DL_NOTIFY_ACK:
+ softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE,
+ DL_NOTIFY_REQ);
+ return;
+
+ case DL_CAPABILITY_ACK:
+ softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE,
+ DL_CAPABILITY_REQ);
+ return;
+
+ case DL_BIND_ACK:
+ softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ);
+ return;
+
+ case DL_CONTROL_ACK:
+ softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE,
+ DL_CONTROL_REQ);
+ return;
+
+ case DL_UNITDATA_IND:
+ case DL_PHYS_ADDR_ACK:
+ /*
+ * a. Because the stream is in DLIOCRAW mode,
+ * DL_UNITDATA_IND messages are not expected.
+ * b. The lower stream should not receive DL_PHYS_ADDR_REQ,
+ * so DL_PHYS_ADDR_ACK messages are also unexpected.
+ */
+ default:
+ cmn_err(CE_WARN, "softmac: got unexpected %s", primstr);
+ break;
+ }
+exit:
+ freemsg(mp);
+ return;
+runt:
+ cmn_err(CE_WARN, "softmac: got runt %s", primstr);
+ freemsg(mp);
+}
+
+void
+softmac_rput_process_notdata(queue_t *rq, mblk_t *mp)
+{
+ softmac_lower_t *slp = rq->q_ptr;
+
+ switch (DB_TYPE(mp)) {
+ case M_PROTO:
+ case M_PCPROTO:
+ softmac_rput_process_proto(rq, mp);
+ break;
+
+ case M_FLUSH:
+ if (*mp->b_rptr & FLUSHR)
+ flushq(rq, FLUSHDATA);
+ if (*mp->b_rptr & FLUSHW)
+ flushq(OTHERQ(rq), FLUSHDATA);
+ putnext(rq, mp);
+ break;
+
+ case M_IOCACK:
+ case M_IOCNAK:
+ case M_COPYIN:
+ case M_COPYOUT:
+ mutex_enter(&slp->sl_mutex);
+ if (!slp->sl_pending_ioctl) {
+ mutex_exit(&slp->sl_mutex);
+ cmn_err(CE_NOTE, "softmac: got unexpected mblk "
+ "type 0x%x", DB_TYPE(mp));
+ freemsg(mp);
+ break;
+ }
+
+ slp->sl_pending_ioctl = B_FALSE;
+ slp->sl_ack_mp = mp;
+ cv_broadcast(&slp->sl_cv);
+ mutex_exit(&slp->sl_mutex);
+ return;
+
+ default:
+ cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x",
+ DB_TYPE(mp));
+ freemsg(mp);
+ break;
+ }
+}
diff --git a/usr/src/uts/common/io/softmac/softmac_dev.c b/usr/src/uts/common/io/softmac/softmac_dev.c
new file mode 100644
index 0000000000..501cec84da
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_dev.c
@@ -0,0 +1,417 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/dld.h>
+#include <inet/common.h>
+#include <sys/stropts.h>
+#include <sys/modctl.h>
+#include <sys/avl.h>
+#include <sys/softmac_impl.h>
+#include <sys/softmac.h>
+
+dev_info_t *softmac_dip = NULL;
+
+static int softmac_open(queue_t *, dev_t *, int, int, cred_t *);
+static int softmac_close(queue_t *);
+static void softmac_rput(queue_t *, mblk_t *);
+static void softmac_rsrv(queue_t *);
+static void softmac_wput(queue_t *, mblk_t *);
+static void softmac_wsrv(queue_t *);
+static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
+static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
+static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
+
+static struct module_info softmac_modinfo = {
+ 0,
+ SOFTMAC_DEV_NAME,
+ 0,
+ INFPSZ,
+ 65536,
+ 1024
+};
+
+/*
+ * hi-water mark is 1 because of the flow control mechanism implemented in
+ * dld. Refer to the comments in dld_str.c for details.
+ */
+static struct module_info softmac_dld_modinfo = {
+ 0,
+ SOFTMAC_DEV_NAME,
+ 0,
+ INFPSZ,
+ 1,
+ 0
+};
+
+static struct qinit softmac_urinit = {
+ (pfi_t)softmac_rput, /* qi_putp */
+ (pfi_t)softmac_rsrv, /* qi_srvp */
+ softmac_open, /* qi_qopen */
+ softmac_close, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &softmac_modinfo /* qi_minfo */
+};
+
+static struct qinit softmac_uwinit = {
+ (pfi_t)softmac_wput, /* qi_putp */
+ (pfi_t)softmac_wsrv, /* qi_srvp */
+ NULL, /* qi_qopen */
+ NULL, /* qi_qclose */
+ NULL, /* qi_qadmin */
+ &softmac_modinfo /* qi_minfo */
+};
+
+static struct streamtab softmac_tab = {
+ &softmac_urinit, /* st_rdinit */
+ &softmac_uwinit /* st_wrinit */
+};
+
+DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
+ softmac_detach, nodev, softmac_info, D_MP, &softmac_tab);
+
+static struct qinit softmac_dld_r_qinit = {
+ NULL, NULL, dld_open, dld_close, NULL, &softmac_dld_modinfo
+};
+
+static struct qinit softmac_dld_w_qinit = {
+ (pfi_t)dld_wput, (pfi_t)dld_wsrv, NULL, NULL, NULL,
+ &softmac_dld_modinfo
+};
+
+static struct fmodsw softmac_fmodsw = {
+ SOFTMAC_DEV_NAME,
+ &softmac_tab,
+ D_MP
+};
+
+static struct modldrv softmac_modldrv = {
+ &mod_driverops,
+ "softmac driver",
+ &softmac_ops
+};
+
+static struct modlstrmod softmac_modlstrmod = {
+ &mod_strmodops,
+ "softmac module",
+ &softmac_fmodsw
+};
+
+static struct modlinkage softmac_modlinkage = {
+ MODREV_1,
+ &softmac_modlstrmod,
+ &softmac_modldrv,
+ NULL
+};
+
+int
+_init(void)
+{
+ int err;
+
+ softmac_init();
+
+ if ((err = mod_install(&softmac_modlinkage)) != 0) {
+ softmac_fini();
+ return (err);
+ }
+
+ return (0);
+}
+
+int
+_fini(void)
+{
+ int err;
+
+ if (softmac_busy())
+ return (EBUSY);
+
+ if ((err = mod_remove(&softmac_modlinkage)) != 0)
+ return (err);
+
+ softmac_fini();
+
+ return (0);
+}
+
+int
+_info(struct modinfo *modinfop)
+{
+ return (mod_info(&softmac_modlinkage, modinfop));
+}
+
+static int
+softmac_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
+{
+ softmac_lower_t *slp;
+ /*
+ * This is a self-cloning driver so that each queue should only
+ * get opened once.
+ */
+ if (rq->q_ptr != NULL)
+ return (EBUSY);
+
+ if (sflag == MODOPEN) {
+ /*
+ * This is the softmac module pushed over an underlying
+ * legacy device. Initialize the lower structure.
+ */
+ if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL)
+ return (ENOMEM);
+
+ slp->sl_wq = WR(rq);
+ cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL);
+ mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&slp->sl_ctl_cv, NULL, CV_DRIVER, NULL);
+ mutex_init(&slp->sl_ctl_mutex, NULL, MUTEX_DRIVER, NULL);
+ slp->sl_pending_prim = DL_PRIM_INVAL;
+ rq->q_ptr = WR(rq)->q_ptr = slp;
+ qprocson(rq);
+ return (0);
+ }
+
+ /*
+ * Regular device open of a softmac DLPI node. We modify
+ * the queues' q_qinfo pointer such that all future STREAMS
+ * operations will go through dld's entry points (including
+ * dld_close()).
+ */
+ rq->q_qinfo = &softmac_dld_r_qinit;
+ WR(rq)->q_qinfo = &softmac_dld_w_qinit;
+ return (dld_open(rq, devp, flag, sflag, credp));
+}
+
+static int
+softmac_close(queue_t *rq)
+{
+ softmac_lower_t *slp = rq->q_ptr;
+
+ /*
+ * Call the appropriate delete routine depending on whether this is
+ * a module or device.
+ */
+ ASSERT(WR(rq)->q_next != NULL);
+
+ qprocsoff(rq);
+
+ slp->sl_softmac = NULL;
+ slp->sl_lh = NULL;
+
+ /*
+ * slp->sl_handle could be non-NULL if it is in the aggregation.
+ */
+ slp->sl_handle = (mac_resource_handle_t)NULL;
+
+ ASSERT(slp->sl_ack_mp == NULL);
+ ASSERT(slp->sl_ctl_inprogress == B_FALSE);
+ ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
+ ASSERT(slp->sl_pending_ioctl == B_FALSE);
+
+ cv_destroy(&slp->sl_cv);
+ mutex_destroy(&slp->sl_mutex);
+ cv_destroy(&slp->sl_ctl_cv);
+ mutex_destroy(&slp->sl_ctl_mutex);
+
+ kmem_free(slp, sizeof (*slp));
+ return (0);
+}
+
+static void
+softmac_rput(queue_t *rq, mblk_t *mp)
+{
+ softmac_lower_t *slp = rq->q_ptr;
+ union DL_primitives *dlp;
+
+ /*
+ * This is the softmac module.
+ */
+ ASSERT(WR(rq)->q_next != NULL);
+ ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
+
+ switch (DB_TYPE(mp)) {
+ case M_DATA:
+ /*
+ * Some drivers start to send up packets even if not in the
+ * DL_IDLE state, where sl_softmac is not set yet. Drop the
+ * packet in this case.
+ */
+ if (slp->sl_softmac == NULL) {
+ freemsg(mp);
+ return;
+ }
+
+ /*
+ * This is the most common case.
+ */
+ if (DB_REF(mp) == 1) {
+ ASSERT(slp->sl_softmac != NULL);
+ /*
+ * We don't need any locks to protect sl_handle
+ * because ip_input() can tolerate if sl_handle
+ * is reset to NULL when DL_CAPAB_POLL is
+ * disabled.
+ */
+ mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp);
+ return;
+ } else {
+ softmac_rput_process_data(slp, mp);
+ }
+ break;
+ case M_PROTO:
+ case M_PCPROTO:
+ if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
+ freemsg(mp);
+ break;
+ }
+ dlp = (union DL_primitives *)mp->b_rptr;
+ if (dlp->dl_primitive == DL_UNITDATA_IND) {
+ cmn_err(CE_WARN, "got unexpected %s message",
+ dl_primstr(DL_UNITDATA_IND));
+ freemsg(mp);
+ break;
+ }
+ /*FALLTHROUGH*/
+ default:
+ softmac_rput_process_notdata(rq, mp);
+ break;
+ }
+}
+
+/* ARGSUSED */
+static void
+softmac_rsrv(queue_t *rq)
+{
+}
+
+static void
+softmac_wput(queue_t *wq, mblk_t *mp)
+{
+ /*
+ * This is the softmac module
+ */
+ ASSERT(wq->q_next != NULL);
+
+ switch (DB_TYPE(mp)) {
+ case M_IOCTL: {
+ struct iocblk *ioc = (struct iocblk *)mp->b_rptr;
+
+ switch (ioc->ioc_cmd) {
+ case SMAC_IOC_START: {
+ softmac_lower_t *slp = wq->q_ptr;
+ smac_ioc_start_t *arg;
+
+ if (ioc->ioc_count != sizeof (*arg)) {
+ miocnak(wq, mp, 0, EINVAL);
+ break;
+ }
+
+ /*
+ * Assign the devname and perstream handle of the
+ * specific lower stream and return it as a part
+ * of the ioctl.
+ */
+ arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
+ arg->si_slp = slp;
+
+ miocack(wq, mp, sizeof (*arg), 0);
+ break;
+ }
+ default:
+ miocnak(wq, mp, 0, EINVAL);
+ break;
+ }
+ break;
+ }
+ default:
+ freemsg(mp);
+ break;
+ }
+}
+
+static void
+softmac_wsrv(queue_t *wq)
+{
+ softmac_lower_t *slp = wq->q_ptr;
+
+ /*
+ * This is the softmac module
+ */
+ ASSERT(wq->q_next != NULL);
+
+ /*
+ * Inform that the tx resource is available; mac_tx_update() will
+ * inform all the upper streams sharing this lower stream.
+ */
+ if (slp->sl_softmac != NULL)
+ mac_tx_update(slp->sl_softmac->smac_mh);
+}
+
+static int
+softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
+{
+ ASSERT(ddi_get_instance(dip) == 0);
+
+ if (cmd != DDI_ATTACH)
+ return (DDI_FAILURE);
+
+ softmac_dip = dip;
+
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
+{
+ if (cmd != DDI_DETACH)
+ return (DDI_FAILURE);
+
+ softmac_dip = NULL;
+ return (DDI_SUCCESS);
+}
+
+/* ARGSUSED */
+static int
+softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
+{
+ switch (infocmd) {
+ case DDI_INFO_DEVT2DEVINFO:
+ if (softmac_dip != NULL) {
+ *result = softmac_dip;
+ return (DDI_SUCCESS);
+ }
+ break;
+
+ case DDI_INFO_DEVT2INSTANCE:
+ *result = NULL;
+ return (DDI_SUCCESS);
+
+ }
+
+ return (DDI_FAILURE);
+}
diff --git a/usr/src/uts/common/io/softmac/softmac_main.c b/usr/src/uts/common/io/softmac/softmac_main.c
new file mode 100644
index 0000000000..8a218c53fb
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_main.c
@@ -0,0 +1,1192 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/*
+ * The softmac driver is used to "unify" non-GLDv3 drivers to the GLDv3
+ * framework. It also creates the kernel datalink structure for each
+ * physical network device.
+ *
+ * Specifically, a softmac will be created for each physical network device
+ * (dip) during the device's post-attach process. When this softmac is
+ * created, the following will also be done:
+ * - create the device's <link name, linkid> mapping;
+ * - register the mac if this is a non-GLDv3 device and the media type is
+ * supported by the GLDv3 framework;
+ * - create the kernel data-link structure for this physical device;
+ *
+ * This softmac will be destroyed during the device's pre-detach process,
+ * and all the above will be undone.
+ */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/cred.h>
+#include <sys/dlpi.h>
+#include <sys/sunndi.h>
+#include <sys/modhash.h>
+#include <sys/stropts.h>
+#include <sys/sysmacros.h>
+#include <sys/vlan.h>
+#include <sys/softmac_impl.h>
+#include <sys/softmac.h>
+#include <sys/dls.h>
+
+/*
+ * Softmac hash table including softmacs for both style-2 and style-1 devices.
+ */
+static krwlock_t softmac_hash_lock;
+static mod_hash_t *softmac_hash;
+
+#define SOFTMAC_HASHSZ 64
+
+static void softmac_mac_register(void *);
+static int softmac_create_datalink(softmac_t *);
+static int softmac_m_start(void *);
+static void softmac_m_stop(void *);
+static int softmac_m_open(void *);
+static void softmac_m_close(void *);
+static boolean_t softmac_m_getcapab(void *, mac_capab_t, void *);
+
+#define SOFTMAC_M_CALLBACK_FLAGS \
+ (MC_RESOURCES | MC_IOCTL | MC_GETCAPAB | MC_OPEN | MC_CLOSE)
+
+static mac_callbacks_t softmac_m_callbacks = {
+ SOFTMAC_M_CALLBACK_FLAGS,
+ softmac_m_stat,
+ softmac_m_start,
+ softmac_m_stop,
+ softmac_m_promisc,
+ softmac_m_multicst,
+ softmac_m_unicst,
+ softmac_m_tx,
+ softmac_m_resources,
+ softmac_m_ioctl,
+ softmac_m_getcapab,
+ softmac_m_open,
+ softmac_m_close
+};
+
+void
+softmac_init()
+{
+ softmac_hash = mod_hash_create_extended("softmac_hash",
+ SOFTMAC_HASHSZ, mod_hash_null_keydtor, mod_hash_null_valdtor,
+ mod_hash_bystr, NULL, mod_hash_strkey_cmp, KM_SLEEP);
+
+ rw_init(&softmac_hash_lock, NULL, RW_DEFAULT, NULL);
+}
+
+void
+softmac_fini()
+{
+ rw_destroy(&softmac_hash_lock);
+ mod_hash_destroy_hash(softmac_hash);
+}
+
+/* ARGSUSED */
+static uint_t
+softmac_exist(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
+{
+ boolean_t *pexist = arg;
+
+ *pexist = B_TRUE;
+ return (MH_WALK_TERMINATE);
+}
+
+boolean_t
+softmac_busy()
+{
+ boolean_t exist = B_FALSE;
+
+ rw_enter(&softmac_hash_lock, RW_READER);
+ mod_hash_walk(softmac_hash, softmac_exist, &exist);
+ rw_exit(&softmac_hash_lock);
+ return (exist);
+}
+
+/*
+ * This function is called for each minor node during the post-attach of
+ * each DDI_NT_NET device instance. Note that it is possible that a device
+ * instance has two minor nodes (DLPI style-1 and style-2), so that for that
+ * specific device, softmac_create() could be called twice.
+ *
+ * A softmac_t is used to track each DDI_NT_NET device, and a softmac_dev_t
+ * is created to track each minor node.
+ *
+ * For each minor node of a legacy device, a taskq is started to finish
+ * softmac_mac_register(), which will finish the rest of work (see comments
+ * above softmac_mac_register()).
+ */
+int
+softmac_create(dev_info_t *dip, dev_t dev)
+{
+ char devname[MAXNAMELEN];
+ softmac_t *softmac;
+ softmac_dev_t *softmac_dev = NULL;
+ datalink_id_t linkid;
+ int index;
+ int ppa, err = 0;
+ mac_handle_t mh;
+
+ /*
+ * Force the softmac driver to be attached.
+ */
+ if (i_ddi_attach_pseudo_node(SOFTMAC_DEV_NAME) == NULL) {
+ cmn_err(CE_WARN, "softmac_create:softmac attach fails");
+ return (ENXIO);
+ }
+
+ ppa = ddi_get_instance(dip);
+ (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
+
+ /*
+ * We expect legacy devices have at most two minor nodes - one style-1
+ * and one style-2.
+ */
+ if (!GLDV3_DRV(ddi_driver_major(dip)) &&
+ i_ddi_minor_node_count(dip, DDI_NT_NET) > 2) {
+ cmn_err(CE_WARN, "%s has more than 2 minor nodes; unsupported",
+ devname);
+ return (ENOTSUP);
+ }
+
+ /*
+ * Check whether the softmac for the specified device already exists
+ */
+ rw_enter(&softmac_hash_lock, RW_WRITER);
+ if ((err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
+ (mod_hash_val_t *)&softmac)) != 0) {
+
+ softmac = kmem_zalloc(sizeof (softmac_t), KM_SLEEP);
+ mutex_init(&softmac->smac_mutex, NULL, MUTEX_DRIVER, NULL);
+ cv_init(&softmac->smac_cv, NULL, CV_DRIVER, NULL);
+ rw_init(&softmac->smac_lock, NULL, RW_DRIVER, NULL);
+ (void) strlcpy(softmac->smac_devname, devname, MAXNAMELEN);
+
+ /*
+ * Insert the softmac into the hash table.
+ */
+ err = mod_hash_insert(softmac_hash,
+ (mod_hash_key_t)softmac->smac_devname,
+ (mod_hash_val_t)softmac);
+ ASSERT(err == 0);
+ }
+
+ mutex_enter(&softmac->smac_mutex);
+ if (softmac->smac_attachok_cnt == 0) {
+ /*
+ * Initialize the softmac if this is the post-attach of the
+ * first minor node.
+ */
+ softmac->smac_flags = 0;
+ softmac->smac_umajor = ddi_driver_major(dip);
+ softmac->smac_uppa = ppa;
+
+ /*
+ * Note that for GLDv3 devices, we create devfs minor nodes
+ * for VLANs as well. Assume a GLDv3 driver on which only
+ * a VLAN is created. During the detachment of this device
+ * instance, the following would happen:
+ * a. the pre-detach callback softmac_destroy() succeeds.
+ * Because the physical link itself is not in use,
+ * softmac_destroy() succeeds and destroys softmac_t;
+ * b. the device detach fails in mac_unregister() because
+ * this MAC is still used by a VLAN.
+ * c. the post-attach callback is then called which leads
+ * us here. Note that ddi_minor_node_count() returns 3
+ * (including the minior node of the VLAN). In that case,
+ * we must correct the minor node count to 2 as that is
+ * the count of minor nodes that go through post-attach.
+ */
+ if (GLDV3_DRV(ddi_driver_major(dip))) {
+ softmac->smac_flags |= SOFTMAC_GLDV3;
+ softmac->smac_cnt = 2;
+ } else {
+ softmac->smac_cnt =
+ i_ddi_minor_node_count(dip, DDI_NT_NET);
+ }
+ }
+
+ index = (getmajor(dev) == ddi_name_to_major("clone"));
+ if (softmac->smac_softmac[index] != NULL) {
+ /*
+ * This is possible if the post_attach() is called:
+ *
+ * a. after pre_detach() fails.
+ *
+ * b. for a new round of reattachment. Note that DACF will not
+ * call pre_detach() for successfully post_attached minor
+ * nodes even when the post-attach failed after all.
+ *
+ * Both seem to be defects in the DACF framework. To work
+ * around it and only clear the SOFTMAC_ATTACH_DONE flag for
+ * the b case, a smac_attached_left field is used to tell
+ * the two cases apart.
+ */
+ ASSERT(softmac->smac_attachok_cnt != 0);
+
+ if (softmac->smac_attached_left != 0)
+ /* case a */
+ softmac->smac_attached_left--;
+ else if (softmac->smac_attachok_cnt != softmac->smac_cnt) {
+ /* case b */
+ softmac->smac_flags &= ~SOFTMAC_ATTACH_DONE;
+ }
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+ return (0);
+ }
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+
+ /*
+ * Inform dlmgmtd of this link so that softmac_hold_device() is able
+ * to know the existence of this link. This could fail if dlmgmtd
+ * is not yet started.
+ */
+ (void) dls_mgmt_create(devname, makedevice(ddi_driver_major(dip),
+ ppa + 1), DATALINK_CLASS_PHYS, DL_OTHER, B_TRUE, &linkid);
+
+ /*
+ * No lock is needed for access this softmac pointer, as pre-detach and
+ * post-attach won't happen at the same time.
+ */
+ mutex_enter(&softmac->smac_mutex);
+
+ softmac_dev = kmem_zalloc(sizeof (softmac_dev_t), KM_SLEEP);
+ softmac_dev->sd_dev = dev;
+ softmac->smac_softmac[index] = softmac_dev;
+
+ /*
+ * Continue to register the mac and create the datalink only when all
+ * the minor nodes are attached.
+ */
+ if (++softmac->smac_attachok_cnt != softmac->smac_cnt) {
+ mutex_exit(&softmac->smac_mutex);
+ return (0);
+ }
+
+ if (!GLDV3_DRV(ddi_driver_major(dip))) {
+
+ /*
+ * Note that this function could be called as a result of
+ * a open() system call, and spec_open() already locked the
+ * snode (SLOCKED is set). Therefore, we must start a
+ * taskq to finish the rest of work to sidestep the risk
+ * that our ldi_open_by_dev() call would again try to hold
+ * the same lock.
+ *
+ * If all the minor nodes have been attached, start the taskq
+ * to finish the rest of the work.
+ */
+ ASSERT(softmac->smac_taskq == NULL);
+ softmac->smac_taskq = taskq_dispatch(system_taskq,
+ softmac_mac_register, softmac, TQ_SLEEP);
+ mutex_exit(&softmac->smac_mutex);
+ return (0);
+ }
+
+ if ((err = mac_open(softmac->smac_devname, &mh)) != 0)
+ goto done;
+
+ softmac->smac_media = (mac_info(mh))->mi_nativemedia;
+ softmac->smac_mh = mh;
+
+ /*
+ * We can safely release the reference on the mac because
+ * this mac will only be unregistered and destroyed when
+ * the device detaches, and the softmac will be destroyed
+ * before then (in the pre-detach routine of the device).
+ */
+ mac_close(mh);
+
+ /*
+ * Create the GLDv3 datalink for this mac.
+ */
+ err = softmac_create_datalink(softmac);
+
+done:
+ if (err != 0) {
+ softmac->smac_mh = NULL;
+ kmem_free(softmac_dev, sizeof (softmac_dev_t));
+ softmac->smac_softmac[index] = NULL;
+ --softmac->smac_attachok_cnt;
+ }
+ ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE));
+ softmac->smac_flags |= SOFTMAC_ATTACH_DONE;
+ softmac->smac_attacherr = err;
+ cv_broadcast(&softmac->smac_cv);
+ mutex_exit(&softmac->smac_mutex);
+ return (err);
+}
+
+static boolean_t
+softmac_m_getcapab(void *arg, mac_capab_t cap, void *cap_data)
+{
+ softmac_t *softmac = arg;
+
+ if (!(softmac->smac_capab_flags & cap))
+ return (B_FALSE);
+
+ switch (cap) {
+ case MAC_CAPAB_HCKSUM: {
+ uint32_t *txflags = cap_data;
+
+ *txflags = softmac->smac_hcksum_txflags;
+ break;
+ }
+ case MAC_CAPAB_LEGACY: {
+ mac_capab_legacy_t *legacy = cap_data;
+
+ legacy->ml_unsup_note = ~softmac->smac_notifications &
+ (DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN | DL_NOTE_SPEED);
+ legacy->ml_dev = makedevice(softmac->smac_umajor,
+ softmac->smac_uppa + 1);
+ break;
+ }
+
+ /*
+ * For the capabilities below, there's nothing for us to fill in;
+ * simply return B_TRUE if we support it.
+ */
+ case MAC_CAPAB_NO_ZCOPY:
+ case MAC_CAPAB_POLL:
+ case MAC_CAPAB_NO_NATIVEVLAN:
+ default:
+ break;
+ }
+ return (B_TRUE);
+}
+
+static int
+softmac_update_info(softmac_t *softmac, datalink_id_t *linkidp)
+{
+ datalink_id_t linkid = DATALINK_INVALID_LINKID;
+ uint32_t media;
+ int err;
+
+ if ((err = dls_mgmt_update(softmac->smac_devname, softmac->smac_media,
+ softmac->smac_flags & SOFTMAC_NOSUPP, &media, &linkid)) == 0) {
+ *linkidp = linkid;
+ }
+
+ if (err == EEXIST) {
+ /*
+ * There is a link name conflict. Either:
+ *
+ * - An existing link with the same device name with a
+ * different media type from of the given type.
+ * Mark this link back to persistent only; or
+ *
+ * - We cannot assign the "suggested" name because
+ * GLDv3 and therefore vanity naming is not supported
+ * for this link type. Delete this link's <link name,
+ * linkid> mapping.
+ */
+ if (media != softmac->smac_media) {
+ cmn_err(CE_WARN, "%s device %s conflicts with "
+ "existing %s device %s.",
+ dl_mactypestr(softmac->smac_media),
+ softmac->smac_devname, dl_mactypestr(media),
+ softmac->smac_devname);
+ (void) dls_mgmt_destroy(linkid, B_FALSE);
+ } else {
+ cmn_err(CE_WARN, "link name %s is already in-use.",
+ softmac->smac_devname);
+ (void) dls_mgmt_destroy(linkid, B_TRUE);
+ }
+
+ cmn_err(CE_WARN, "%s device might not be available "
+ "for use.", softmac->smac_devname);
+ cmn_err(CE_WARN, "See dladm(1M) for more information.");
+ }
+
+ return (err);
+}
+
+/*
+ * This function:
+ * 1. provides the link's media type to dlmgmtd.
+ * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
+ */
+static int
+softmac_create_datalink(softmac_t *softmac)
+{
+ datalink_id_t linkid = DATALINK_INVALID_LINKID;
+ int err;
+
+ ASSERT(MUTEX_HELD(&softmac->smac_mutex));
+
+ /*
+ * First provide the media type of the physical link to dlmgmtd.
+ *
+ * If the new <linkname, linkid> mapping operation failed with EBADF
+ * or ENOENT, it might because the dlmgmtd was not started in time
+ * (e.g., diskless boot); ignore the failure and continue. The
+ * mapping will be recreated once the daemon has started.
+ */
+ if (((err = softmac_update_info(softmac, &linkid)) != 0) &&
+ (err != EBADF) && (err != ENOENT)) {
+ return (err);
+ }
+
+ /*
+ * Create the GLDv3 datalink.
+ */
+ if ((!(softmac->smac_flags & SOFTMAC_NOSUPP)) &&
+ ((err = dls_devnet_create(softmac->smac_mh, linkid)) != 0)) {
+ cmn_err(CE_WARN, "dls_devnet_create failed for %s",
+ softmac->smac_devname);
+ return (err);
+ }
+
+ if (linkid == DATALINK_INVALID_LINKID)
+ softmac->smac_flags |= SOFTMAC_NEED_RECREATE;
+
+ return (0);
+}
+
+/*
+ * This function is only called for legacy devices. It:
+ * 1. registers the MAC for the legacy devices whose media type is supported
+ * by the GLDv3 framework.
+ * 2. creates the GLDv3 datalink if the media type is supported by GLDv3.
+ */
+static void
+softmac_mac_register(void *arg)
+{
+ softmac_t *softmac = arg;
+ softmac_dev_t *softmac_dev;
+ dev_t dev;
+ ldi_handle_t lh = NULL;
+ ldi_ident_t li = NULL;
+ int index;
+ boolean_t native_vlan = B_FALSE;
+ int err;
+
+ /*
+ * Note that we do not need any locks to access this softmac pointer,
+ * as softmac_destroy() will wait until this function is called.
+ */
+ ASSERT(softmac != NULL);
+
+ if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0) {
+ mutex_enter(&softmac->smac_mutex);
+ goto done;
+ }
+
+ /*
+ * Determine whether this legacy device support VLANs by opening
+ * the style-2 device node (if it exists) and attaching to a VLAN
+ * PPA (1000 + ppa).
+ */
+ dev = makedevice(ddi_name_to_major("clone"), softmac->smac_umajor);
+ err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
+ if (err == 0) {
+ if (dl_attach(lh, softmac->smac_uppa + 1 * 1000, NULL) == 0)
+ native_vlan = B_TRUE;
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ }
+
+ err = EINVAL;
+ for (index = 0; index < 2; index++) {
+ dl_info_ack_t dlia;
+ dl_error_ack_t dlea;
+ uint32_t notes;
+ struct strioctl iocb;
+ uint32_t margin;
+ int rval;
+
+ if ((softmac_dev = softmac->smac_softmac[index]) == NULL)
+ continue;
+
+ softmac->smac_dev = dev = softmac_dev->sd_dev;
+ if (ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh,
+ li) != 0) {
+ continue;
+ }
+
+ /*
+ * Pop all the intermediate modules in order to negotiate
+ * capabilities correctly.
+ */
+ while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
+ ;
+
+ /* DLPI style-1 or DLPI style-2? */
+ if ((rval = dl_info(lh, &dlia, NULL, NULL, &dlea)) != 0) {
+ if (rval == ENOTSUP) {
+ cmn_err(CE_NOTE, "softmac: received "
+ "DL_ERROR_ACK to DL_INFO_ACK; "
+ "DLPI errno 0x%x, UNIX errno %d",
+ dlea.dl_errno, dlea.dl_unix_errno);
+ }
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ /*
+ * Currently only DL_ETHER has GLDv3 mac plugin support.
+ * For media types that GLDv3 does not support, create a
+ * link id for it.
+ */
+ if ((softmac->smac_media = dlia.dl_mac_type) != DL_ETHER) {
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ err = 0;
+ break;
+ }
+
+ if ((dlia.dl_provider_style == DL_STYLE2) &&
+ (dl_attach(lh, softmac->smac_uppa, NULL) != 0)) {
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ if ((rval = dl_bind(lh, 0, NULL)) != 0) {
+ if (rval == ENOTSUP) {
+ cmn_err(CE_NOTE, "softmac: received "
+ "DL_ERROR_ACK to DL_BIND_ACK; "
+ "DLPI errno 0x%x, UNIX errno %d",
+ dlea.dl_errno, dlea.dl_unix_errno);
+ }
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ /*
+ * Call dl_info() after dl_bind() because some drivers only
+ * provide correct information (e.g. MAC address) once bound.
+ */
+ softmac->smac_addrlen = sizeof (softmac->smac_unicst_addr);
+ if ((rval = dl_info(lh, &dlia, softmac->smac_unicst_addr,
+ &softmac->smac_addrlen, &dlea)) != 0) {
+ if (rval == ENOTSUP) {
+ cmn_err(CE_NOTE, "softmac: received "
+ "DL_ERROR_ACK to DL_INFO_ACK; "
+ "DLPI errno 0x%x, UNIX errno %d",
+ dlea.dl_errno, dlea.dl_unix_errno);
+ }
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ softmac->smac_style = dlia.dl_provider_style;
+ softmac->smac_saplen = ABS(dlia.dl_sap_length);
+ softmac->smac_min_sdu = dlia.dl_min_sdu;
+ softmac->smac_max_sdu = dlia.dl_max_sdu;
+
+ if ((softmac->smac_saplen != sizeof (uint16_t)) ||
+ (softmac->smac_addrlen != ETHERADDRL) ||
+ (dlia.dl_brdcst_addr_length != ETHERADDRL) ||
+ (dlia.dl_brdcst_addr_offset == 0)) {
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ /*
+ * Check other DLPI capabilities. Note that this must be after
+ * dl_bind() because some drivers return DL_ERROR_ACK if the
+ * stream is not bound. It is also before mac_register(), so
+ * we don't need any lock protection here.
+ *
+ * Softmac always supports POLL.
+ */
+ softmac->smac_capab_flags =
+ (MAC_CAPAB_POLL | MAC_CAPAB_NO_ZCOPY | MAC_CAPAB_LEGACY);
+
+ softmac->smac_no_capability_req = B_FALSE;
+ if (softmac_fill_capab(lh, softmac) != 0)
+ softmac->smac_no_capability_req = B_TRUE;
+
+ /*
+ * Check the margin of the underlying driver.
+ */
+ margin = 0;
+ iocb.ic_cmd = DLIOCMARGININFO;
+ iocb.ic_timout = INFTIM;
+ iocb.ic_len = sizeof (margin);
+ iocb.ic_dp = (char *)&margin;
+ softmac->smac_margin = 0;
+
+ if (ldi_ioctl(lh, I_STR, (intptr_t)&iocb, FKIOCTL, kcred,
+ &rval) == 0) {
+ softmac->smac_margin = margin;
+ }
+
+ /*
+ * If the legacy driver doesn't support DLIOCMARGININFO, but
+ * it can support native VLAN, correct its margin value to 4.
+ */
+ if (native_vlan) {
+ if (softmac->smac_margin == 0)
+ softmac->smac_margin = VLAN_TAGSZ;
+ } else {
+ softmac->smac_capab_flags |= MAC_CAPAB_NO_NATIVEVLAN;
+ }
+
+ /*
+ * Not all drivers support DL_NOTIFY_REQ, so ignore ENOTSUP.
+ */
+ softmac->smac_notifications = 0;
+ notes = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP | DL_NOTE_LINK_DOWN;
+ switch (dl_notify(lh, &notes, NULL)) {
+ case 0:
+ softmac->smac_notifications = notes;
+ break;
+ case ENOTSUP:
+ break;
+ default:
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ continue;
+ }
+
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ err = 0;
+ break;
+ }
+ ldi_ident_release(li);
+
+ mutex_enter(&softmac->smac_mutex);
+
+ if (err != 0)
+ goto done;
+
+ if (softmac->smac_media != DL_ETHER)
+ softmac->smac_flags |= SOFTMAC_NOSUPP;
+
+ /*
+ * Finally, we're ready to register ourselves with the MAC layer
+ * interface; if this succeeds, we're all ready to start()
+ */
+ if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
+ mac_register_t *macp;
+
+ if ((macp = mac_alloc(MAC_VERSION)) == NULL) {
+ err = ENOMEM;
+ goto done;
+ }
+
+ macp->m_type_ident = MAC_PLUGIN_IDENT_ETHER;
+ macp->m_driver = softmac;
+ macp->m_dip = softmac_dip;
+
+ macp->m_margin = softmac->smac_margin;
+ macp->m_src_addr = softmac->smac_unicst_addr;
+ macp->m_min_sdu = softmac->smac_min_sdu;
+ macp->m_max_sdu = softmac->smac_max_sdu;
+ macp->m_callbacks = &softmac_m_callbacks;
+ macp->m_instance = (uint_t)-1;
+
+ err = mac_register(macp, &softmac->smac_mh);
+ mac_free(macp);
+ if (err != 0) {
+ cmn_err(CE_WARN, "mac_register failed for %s",
+ softmac->smac_devname);
+ goto done;
+ }
+ }
+
+ /*
+ * Try to create the datalink for this softmac.
+ */
+ if ((err = softmac_create_datalink(softmac)) != 0) {
+ if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
+ (void) mac_unregister(softmac->smac_mh);
+ softmac->smac_mh = NULL;
+ }
+ }
+
+done:
+ ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE));
+ softmac->smac_flags |= SOFTMAC_ATTACH_DONE;
+ softmac->smac_attacherr = err;
+ softmac->smac_taskq = NULL;
+ cv_broadcast(&softmac->smac_cv);
+ mutex_exit(&softmac->smac_mutex);
+}
+
+int
+softmac_destroy(dev_info_t *dip, dev_t dev)
+{
+ char devname[MAXNAMELEN];
+ softmac_t *softmac;
+ softmac_dev_t *softmac_dev;
+ int index;
+ int ppa, err;
+ datalink_id_t linkid;
+
+ ppa = ddi_get_instance(dip);
+ (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
+
+ rw_enter(&softmac_hash_lock, RW_WRITER);
+ err = mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
+ (mod_hash_val_t *)&softmac);
+ ASSERT(err == 0);
+
+ mutex_enter(&softmac->smac_mutex);
+
+ /*
+ * Fail the predetach routine if this softmac is in-use.
+ */
+ if (softmac->smac_hold_cnt != 0) {
+ softmac->smac_attached_left = softmac->smac_attachok_cnt;
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+ return (EBUSY);
+ }
+
+ /*
+ * Even if the predetach of one minor node has already failed
+ * (smac_attached_left is not 0), the DACF framework will continue
+ * to call the predetach routines of the other minor nodes,
+ * so we fail these calls here.
+ */
+ if (softmac->smac_attached_left != 0) {
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+ return (EBUSY);
+ }
+
+ if (softmac->smac_attachok_cnt != softmac->smac_cnt)
+ goto done;
+
+ /*
+ * This is the detach for the first minor node. Wait until all the
+ * minor nodes are attached.
+ */
+ while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE))
+ cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
+
+ if (softmac->smac_mh != NULL) {
+ if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
+ if ((err = dls_devnet_destroy(softmac->smac_mh,
+ &linkid)) != 0) {
+ goto done;
+ }
+ }
+ /*
+ * If softmac_mac_register() succeeds in registering the mac
+ * of the legacy device, unregister it.
+ */
+ if (!(softmac->smac_flags & (SOFTMAC_GLDV3 | SOFTMAC_NOSUPP))) {
+ if ((err = mac_unregister(softmac->smac_mh)) != 0) {
+ (void) dls_devnet_create(softmac->smac_mh,
+ linkid);
+ goto done;
+ }
+ }
+ softmac->smac_mh = NULL;
+ }
+ softmac->smac_flags &= ~SOFTMAC_ATTACH_DONE;
+
+done:
+ if (err == 0) {
+ /*
+ * Free softmac_dev
+ */
+ index = (getmajor(dev) == ddi_name_to_major("clone"));
+ softmac_dev = softmac->smac_softmac[index];
+ ASSERT(softmac_dev != NULL);
+ softmac->smac_softmac[index] = NULL;
+ kmem_free(softmac_dev, sizeof (softmac_dev_t));
+
+ if (--softmac->smac_attachok_cnt == 0) {
+ mod_hash_val_t hashval;
+
+ err = mod_hash_remove(softmac_hash,
+ (mod_hash_key_t)devname,
+ (mod_hash_val_t *)&hashval);
+ ASSERT(err == 0);
+
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+
+ ASSERT(softmac->smac_taskq == NULL);
+ ASSERT(!(softmac->smac_flags & SOFTMAC_ATTACH_DONE));
+ mutex_destroy(&softmac->smac_mutex);
+ cv_destroy(&softmac->smac_cv);
+ rw_destroy(&softmac->smac_lock);
+ kmem_free(softmac, sizeof (softmac_t));
+ return (0);
+ }
+ } else {
+ softmac->smac_attached_left = softmac->smac_attachok_cnt;
+ }
+
+ mutex_exit(&softmac->smac_mutex);
+ rw_exit(&softmac_hash_lock);
+ return (err);
+}
+
+/*
+ * This function is called as the result of a newly started dlmgmtd daemon.
+ *
+ * We walk through every softmac that was created but failed to notify
+ * dlmgmtd about it (whose SOFTMAC_NEED_RECREATE flag is set). This occurs
+ * when softmacs are created before dlmgmtd is ready. For example, during
+ * diskless boot, a network device is used (and therefore attached) before
+ * the datalink-management service starts dlmgmtd.
+ */
+/* ARGSUSED */
+static uint_t
+softmac_mac_recreate(mod_hash_key_t key, mod_hash_val_t *val, void *arg)
+{
+ softmac_t *softmac = (softmac_t *)val;
+ datalink_id_t linkid;
+ int err;
+
+ ASSERT(RW_READ_HELD(&softmac_hash_lock));
+
+ /*
+ * Wait for softmac_create() and softmac_mac_register() to exit.
+ */
+ mutex_enter(&softmac->smac_mutex);
+ while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE))
+ cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
+
+ if ((softmac->smac_attacherr != 0) ||
+ !(softmac->smac_flags & SOFTMAC_NEED_RECREATE)) {
+ mutex_exit(&softmac->smac_mutex);
+ return (MH_WALK_CONTINUE);
+ }
+
+ if (dls_mgmt_create(softmac->smac_devname,
+ makedevice(softmac->smac_umajor, softmac->smac_uppa + 1),
+ DATALINK_CLASS_PHYS, softmac->smac_media, B_TRUE, &linkid) != 0) {
+ mutex_exit(&softmac->smac_mutex);
+ return (MH_WALK_CONTINUE);
+ }
+
+ if ((err = softmac_update_info(softmac, &linkid)) != 0) {
+ cmn_err(CE_WARN, "softmac: softmac_update_info() for %s "
+ "failed (%d)", softmac->smac_devname, err);
+ mutex_exit(&softmac->smac_mutex);
+ return (MH_WALK_CONTINUE);
+ }
+
+ /*
+ * Create a link for this MAC. The link name will be the same
+ * as the MAC name.
+ */
+ if (!(softmac->smac_flags & SOFTMAC_NOSUPP)) {
+ err = dls_devnet_recreate(softmac->smac_mh, linkid);
+ if (err != 0) {
+ cmn_err(CE_WARN, "softmac: dls_devnet_recreate() for "
+ "%s (linkid %d) failed (%d)",
+ softmac->smac_devname, linkid, err);
+ }
+ }
+
+ softmac->smac_flags &= ~SOFTMAC_NEED_RECREATE;
+ mutex_exit(&softmac->smac_mutex);
+
+ return (MH_WALK_CONTINUE);
+}
+
+/*
+ * See comments above softmac_mac_recreate().
+ */
+void
+softmac_recreate()
+{
+ /*
+ * Walk through the softmac_hash table. Request to create the
+ * [link name, linkid] mapping if we failed to do so.
+ */
+ rw_enter(&softmac_hash_lock, RW_READER);
+ mod_hash_walk(softmac_hash, softmac_mac_recreate, NULL);
+ rw_exit(&softmac_hash_lock);
+}
+
+/* ARGSUSED */
+static int
+softmac_m_start(void *arg)
+{
+ return (0);
+}
+
+/* ARGSUSED */
+static void
+softmac_m_stop(void *arg)
+{
+}
+
+/*
+ * Set up the lower stream above the legacy device which is shared by
+ * GLDv3 MAC clients. Put the lower stream into DLIOCRAW mode to send
+ * and receive the raw data. Further, put the lower stream into
+ * DL_PROMISC_SAP mode to receive all packets of interest.
+ */
+static int
+softmac_lower_setup(softmac_t *softmac, softmac_lower_t **slpp)
+{
+ ldi_ident_t li;
+ dev_t dev;
+ ldi_handle_t lh = NULL;
+ softmac_lower_t *slp = NULL;
+ smac_ioc_start_t start_arg;
+ struct strioctl strioc;
+ uint32_t notifications;
+ int err, rval;
+
+ if ((err = ldi_ident_from_dip(softmac_dip, &li)) != 0)
+ return (err);
+
+ dev = softmac->smac_dev;
+ err = ldi_open_by_dev(&dev, OTYP_CHR, FREAD|FWRITE, kcred, &lh, li);
+ ldi_ident_release(li);
+ if (err != 0)
+ goto done;
+
+ /*
+ * Pop all the intermediate modules. The autopushed modules will
+ * be pushed when the softmac node is opened.
+ */
+ while (ldi_ioctl(lh, I_POP, 0, FKIOCTL, kcred, &rval) == 0)
+ ;
+
+ if ((softmac->smac_style == DL_STYLE2) &&
+ ((err = dl_attach(lh, softmac->smac_uppa, NULL)) != 0)) {
+ goto done;
+ }
+
+ /*
+ * Put the lower stream into DLIOCRAW mode to send/receive raw data.
+ */
+ if ((err = ldi_ioctl(lh, DLIOCRAW, 0, FKIOCTL, kcred, &rval)) != 0)
+ goto done;
+
+ /*
+ * Then push the softmac shim layer atop the lower stream.
+ */
+ if ((err = ldi_ioctl(lh, I_PUSH, (intptr_t)SOFTMAC_DEV_NAME, FKIOCTL,
+ kcred, &rval)) != 0) {
+ goto done;
+ }
+
+ /*
+ * Send the ioctl to get the slp pointer.
+ */
+ strioc.ic_cmd = SMAC_IOC_START;
+ strioc.ic_timout = INFTIM;
+ strioc.ic_len = sizeof (start_arg);
+ strioc.ic_dp = (char *)&start_arg;
+
+ if ((err = ldi_ioctl(lh, I_STR, (intptr_t)&strioc, FKIOCTL,
+ kcred, &rval)) != 0) {
+ goto done;
+ }
+ slp = start_arg.si_slp;
+ slp->sl_lh = lh;
+ slp->sl_softmac = softmac;
+ *slpp = slp;
+
+ /*
+ * Bind to SAP 2 on token ring, 0 on other interface types.
+ * (SAP 0 has special significance on token ring).
+ * Note that the receive-side packets could come anytime after bind.
+ */
+ if (softmac->smac_media == DL_TPR)
+ err = softmac_send_bind_req(slp, 2);
+ else
+ err = softmac_send_bind_req(slp, 0);
+ if (err != 0)
+ goto done;
+
+ /*
+ * Put the lower stream into DL_PROMISC_SAP mode to receive all
+ * packets of interest.
+ *
+ * Some drivers (e.g. the old legacy eri driver) incorrectly pass up
+ * packets to DL_PROMISC_SAP stream when the lower stream is not bound,
+ * so we send DL_PROMISON_REQ after DL_BIND_REQ.
+ */
+ if ((err = softmac_send_promisc_req(slp, DL_PROMISC_SAP, B_TRUE)) != 0)
+ goto done;
+
+ /*
+ * Enable the capabilities the underlying driver claims to support.
+ * Some drivers require this to be called after the stream is bound.
+ */
+ if ((err = softmac_capab_enable(slp)) != 0)
+ goto done;
+
+ /*
+ * Send the DL_NOTIFY_REQ to enable certain DL_NOTIFY_IND.
+ * We don't have to wait for the ack.
+ */
+ notifications = DL_NOTE_PHYS_ADDR | DL_NOTE_LINK_UP |
+ DL_NOTE_LINK_DOWN | DL_NOTE_PROMISC_ON_PHYS |
+ DL_NOTE_PROMISC_OFF_PHYS;
+
+ (void) softmac_send_notify_req(slp,
+ (notifications & softmac->smac_notifications));
+
+done:
+ if (err != 0)
+ (void) ldi_close(lh, FREAD|FWRITE, kcred);
+ return (err);
+}
+
+static int
+softmac_m_open(void *arg)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp;
+ int err;
+
+ rw_enter(&softmac->smac_lock, RW_READER);
+ if (softmac->smac_state == SOFTMAC_READY)
+ goto done;
+ rw_exit(&softmac->smac_lock);
+
+ if ((err = softmac_lower_setup(softmac, &slp)) != 0)
+ return (err);
+
+ rw_enter(&softmac->smac_lock, RW_WRITER);
+ ASSERT(softmac->smac_state == SOFTMAC_INITIALIZED);
+ softmac->smac_lower = slp;
+ softmac->smac_state = SOFTMAC_READY;
+done:
+ rw_exit(&softmac->smac_lock);
+ return (0);
+}
+
+static void
+softmac_m_close(void *arg)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp;
+
+ rw_enter(&softmac->smac_lock, RW_WRITER);
+ slp = softmac->smac_lower;
+ ASSERT(slp != NULL);
+
+ /*
+ * Note that slp is destroyed when lh is closed.
+ */
+ (void) ldi_close(slp->sl_lh, FREAD|FWRITE, kcred);
+ softmac->smac_state = SOFTMAC_INITIALIZED;
+ softmac->smac_lower = NULL;
+ rw_exit(&softmac->smac_lock);
+}
+
+int
+softmac_hold_device(dev_t dev, dls_dev_handle_t *ddhp)
+{
+ dev_info_t *dip;
+ char devname[MAXNAMELEN];
+ softmac_t *softmac;
+ int ppa, err;
+
+ if ((ppa = getminor(dev) - 1) > 1000)
+ return (ENOENT);
+
+ /*
+ * First try to hold this device instance to force the MAC
+ * to be registered.
+ */
+ if ((dip = ddi_hold_devi_by_instance(getmajor(dev), ppa, 0)) == NULL)
+ return (ENOENT);
+
+ if ((ddi_driver_major(dip) != getmajor(dev)) ||
+ !NETWORK_DRV(getmajor(dev))) {
+ ddi_release_devi(dip);
+ return (ENOENT);
+ }
+
+ /*
+ * This is a network device; wait for its softmac to be registered.
+ */
+ (void) snprintf(devname, MAXNAMELEN, "%s%d", ddi_driver_name(dip), ppa);
+again:
+ rw_enter(&softmac_hash_lock, RW_READER);
+
+ if (mod_hash_find(softmac_hash, (mod_hash_key_t)devname,
+ (mod_hash_val_t *)&softmac) != 0) {
+ /*
+ * This is rare but possible. It could happen when pre-detach
+ * routine of the device succeeds. But the softmac will then
+ * be recreated when device fails to detach (as this device
+ * is held).
+ */
+ rw_exit(&softmac_hash_lock);
+ goto again;
+ }
+
+ /*
+ * Bump smac_hold_cnt to prevent device detach.
+ */
+ mutex_enter(&softmac->smac_mutex);
+ softmac->smac_hold_cnt++;
+ mutex_exit(&softmac->smac_mutex);
+
+ rw_exit(&softmac_hash_lock);
+
+ /*
+ * Wait till the device is fully attached.
+ */
+ mutex_enter(&softmac->smac_mutex);
+ while (!(softmac->smac_flags & SOFTMAC_ATTACH_DONE))
+ cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
+
+ if ((err = softmac->smac_attacherr) == 0) {
+ /*
+ * If softmac is successfully attached, set smac_udip
+ * which is used in softmac_rele_device().
+ */
+ ASSERT(softmac->smac_udip == NULL ||
+ softmac->smac_udip == dip);
+ softmac->smac_udip = dip;
+ *ddhp = (dls_dev_handle_t)softmac;
+ }
+ mutex_exit(&softmac->smac_mutex);
+
+ if (err != 0)
+ softmac_rele_device((dls_dev_handle_t)softmac);
+
+ return (err);
+}
+
+void
+softmac_rele_device(dls_dev_handle_t ddh)
+{
+ softmac_t *softmac;
+ dev_info_t *dip;
+
+ if (ddh == NULL)
+ return;
+
+ softmac = (softmac_t *)ddh;
+ mutex_enter(&softmac->smac_mutex);
+ dip = softmac->smac_udip;
+ if (--softmac->smac_hold_cnt == 0)
+ softmac->smac_udip = NULL;
+ mutex_exit(&softmac->smac_mutex);
+
+ ddi_release_devi(dip);
+}
diff --git a/usr/src/uts/common/io/softmac/softmac_pkt.c b/usr/src/uts/common/io/softmac/softmac_pkt.c
new file mode 100644
index 0000000000..8848dc755a
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_pkt.c
@@ -0,0 +1,320 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/strsubr.h>
+#include <inet/led.h>
+#include <sys/softmac_impl.h>
+
+/*
+ * Macro to check whether the write-queue of the lower stream is full.
+ *
+ * Because softmac is pushed right above the underlying device and
+ * _I_INSERT/_I_REMOVE is not processed in the lower stream, it is
+ * safe to directly access the q_next pointer.
+ */
+#define CANPUTNEXT(q) \
+ (!((q)->q_next->q_nfsrv->q_flag & QFULL) || canput((q)->q_next))
+
+mblk_t *
+softmac_m_tx(void *arg, mblk_t *mp)
+{
+ queue_t *wq = ((softmac_t *)arg)->smac_lower->sl_wq;
+
+ /*
+ * Optimize for the most common case.
+ */
+ if (mp->b_cont == NULL) {
+ if (!CANPUTNEXT(wq))
+ return (mp);
+
+ mp->b_flag |= MSGNOLOOP;
+ putnext(wq, mp);
+ return (NULL);
+ }
+
+ while (mp != NULL) {
+ mblk_t *next = mp->b_next;
+
+ if (!CANPUTNEXT(wq))
+ break;
+ mp->b_next = NULL;
+ mp->b_flag |= MSGNOLOOP;
+ putnext(wq, mp);
+ mp = next;
+ }
+ return (mp);
+}
+
+/*ARGSUSED*/
+static void
+softmac_blank(void *arg, time_t ticks, uint_t count)
+{
+}
+
+void
+softmac_m_resources(void *arg)
+{
+ softmac_t *softmac = arg;
+ softmac_lower_t *slp = softmac->smac_lower;
+ mac_rx_fifo_t mrf;
+
+ ASSERT((softmac->smac_state == SOFTMAC_READY) && (slp != NULL));
+
+ /*
+ * Register rx resources and save resource handle for future reference.
+ * Note that the mac_resources() function must be called when the lower
+ * stream is plumbed.
+ */
+
+ mutex_enter(&slp->sl_mutex);
+
+ mrf.mrf_type = MAC_RX_FIFO;
+ mrf.mrf_blank = softmac_blank;
+ mrf.mrf_arg = slp;
+ mrf.mrf_normal_blank_time = SOFTMAC_BLANK_TICKS;
+ mrf.mrf_normal_pkt_count = SOFTMAC_BLANK_PKT_COUNT;
+
+ slp->sl_handle =
+ mac_resource_add(softmac->smac_mh, (mac_resource_t *)&mrf);
+
+ mutex_exit(&slp->sl_mutex);
+}
+
+void
+softmac_rput_process_data(softmac_lower_t *slp, mblk_t *mp)
+{
+ /*
+ * When packets arrive, the softmac might not be fully started.
+ */
+ ASSERT((slp->sl_softmac != NULL));
+ ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
+
+ if (DB_REF(mp) > 1) {
+ mblk_t *tmp;
+
+ if ((tmp = copymsg(mp)) == NULL) {
+ cmn_err(CE_WARN, "softmac_rput_process_data: "
+ "copymsg failed");
+ goto failed;
+ }
+ freemsg(mp);
+ mp = tmp;
+ }
+
+ mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp);
+ return;
+
+failed:
+ freemsg(mp);
+}
+
+#define ACKTIMEOUT (10 * hz)
+
+/*
+ * Serialize control message processing.
+ */
+static void
+softmac_serialize_enter(softmac_lower_t *slp)
+{
+ mutex_enter(&slp->sl_ctl_mutex);
+ while (slp->sl_ctl_inprogress)
+ cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex);
+
+ ASSERT(!slp->sl_ctl_inprogress);
+ ASSERT(!slp->sl_pending_ioctl);
+ ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
+
+ slp->sl_ctl_inprogress = B_TRUE;
+ mutex_exit(&slp->sl_ctl_mutex);
+}
+
+static void
+softmac_serialize_exit(softmac_lower_t *slp)
+{
+ mutex_enter(&slp->sl_ctl_mutex);
+
+ ASSERT(slp->sl_ctl_inprogress);
+ ASSERT(!slp->sl_pending_ioctl);
+ ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
+
+ slp->sl_ctl_inprogress = B_FALSE;
+ cv_broadcast(&slp->sl_ctl_cv);
+ mutex_exit(&slp->sl_ctl_mutex);
+}
+
+static int
+dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno)
+{
+ return (error == DL_SYSERR ? unix_errno : EINVAL);
+}
+
+static int
+softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim,
+ t_uscalar_t ack, mblk_t **mpp)
+{
+ union DL_primitives *dlp;
+ int err = 0;
+
+ softmac_serialize_enter(slp);
+
+ /*
+ * Record the pending DLPI primitive.
+ */
+ mutex_enter(&slp->sl_mutex);
+ slp->sl_pending_prim = dl_prim;
+ mutex_exit(&slp->sl_mutex);
+
+ putnext(slp->sl_wq, mp);
+
+ mutex_enter(&slp->sl_mutex);
+ while (slp->sl_pending_prim != DL_PRIM_INVAL) {
+ if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex,
+ lbolt + ACKTIMEOUT) == -1)
+ break;
+ }
+
+ mp = slp->sl_ack_mp;
+ slp->sl_ack_mp = NULL;
+
+ /*
+ * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim
+ * won't be set to DL_PRIM_INVAL.
+ */
+ ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL);
+
+ slp->sl_pending_prim = DL_PRIM_INVAL;
+ mutex_exit(&slp->sl_mutex);
+
+ if (mp != NULL) {
+ dlp = (union DL_primitives *)mp->b_rptr;
+
+ if (dlp->dl_primitive == DL_ERROR_ACK) {
+ err = dlpi_get_errno(dlp->error_ack.dl_errno,
+ dlp->error_ack.dl_unix_errno);
+ } else {
+ ASSERT(dlp->dl_primitive == ack);
+ }
+ } else {
+ err = ENOMSG;
+ }
+
+ if (mpp != NULL)
+ *mpp = mp;
+ else
+ freemsg(mp);
+
+ softmac_serialize_exit(slp);
+ return (err);
+}
+
+void
+softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
+{
+ softmac_serialize_enter(slp);
+
+ /*
+ * Record that ioctl processing is currently in progress.
+ */
+ mutex_enter(&slp->sl_mutex);
+ slp->sl_pending_ioctl = B_TRUE;
+ mutex_exit(&slp->sl_mutex);
+
+ putnext(slp->sl_wq, mp);
+
+ mutex_enter(&slp->sl_mutex);
+ while (slp->sl_pending_ioctl)
+ cv_wait(&slp->sl_cv, &slp->sl_mutex);
+ mp = slp->sl_ack_mp;
+ slp->sl_ack_mp = NULL;
+ mutex_exit(&slp->sl_mutex);
+
+ ASSERT(mpp != NULL && mp != NULL);
+ *mpp = mp;
+
+ softmac_serialize_exit(slp);
+}
+
+static int
+softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive,
+ t_uscalar_t error, t_uscalar_t unix_errno)
+{
+ union DL_primitives *dlp;
+
+ if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO,
+ DL_ERROR_ACK)) == NULL)
+ return (ENOMEM);
+
+ dlp = (union DL_primitives *)(*mpp)->b_rptr;
+ dlp->error_ack.dl_error_primitive = error_primitive;
+ dlp->error_ack.dl_errno = error;
+ dlp->error_ack.dl_unix_errno = unix_errno;
+
+ return (0);
+}
+
+int
+softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp)
+{
+ int err = 0;
+ t_uscalar_t dl_prim;
+
+ dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
+
+ ASSERT(slp->sl_softmac != NULL);
+
+ switch (dl_prim) {
+ case DL_ENABMULTI_REQ:
+ case DL_DISABMULTI_REQ:
+ case DL_SET_PHYS_ADDR_REQ:
+ case DL_UNBIND_REQ:
+ case DL_UDQOS_REQ:
+ case DL_PROMISCON_REQ:
+ case DL_PROMISCOFF_REQ:
+ err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp);
+ break;
+ case DL_BIND_REQ:
+ err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp);
+ break;
+ case DL_NOTIFY_REQ:
+ err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp);
+ break;
+ case DL_CONTROL_REQ:
+ err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp);
+ break;
+ case DL_CAPABILITY_REQ:
+ err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp);
+ break;
+ default:
+ if (mpp != NULL) {
+ *mpp = mp;
+ err = softmac_mexchange_error_ack(mpp, dl_prim,
+ DL_UNSUPPORTED, 0);
+ }
+ break;
+ }
+ return (err);
+}
diff --git a/usr/src/uts/common/io/softmac/softmac_stat.c b/usr/src/uts/common/io/softmac/softmac_stat.c
new file mode 100644
index 0000000000..78b5306c86
--- /dev/null
+++ b/usr/src/uts/common/io/softmac/softmac_stat.c
@@ -0,0 +1,270 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+/*
+ * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <sys/types.h>
+#include <sys/kstat.h>
+#include <sys/mac.h>
+#include <sys/dls.h>
+#include <sys/softmac_impl.h>
+
+typedef struct i_softmac_stat_info_s {
+ uint_t ssi_stat;
+ char *ssi_name;
+ char *ssi_alias;
+} i_softmac_stat_info_t;
+
+/*
+ * Must be the same order as mac_driver_stat.
+ */
+static i_softmac_stat_info_t i_softmac_driver_si[] = {
+ { MAC_STAT_IFSPEED, "ifspeed", "link_speed" },
+ { MAC_STAT_MULTIRCV, "multircv", NULL },
+ { MAC_STAT_BRDCSTRCV, "brdcstrcv", NULL },
+ { MAC_STAT_MULTIXMT, "multixmt", NULL },
+ { MAC_STAT_BRDCSTXMT, "brdcstxmt", NULL },
+ { MAC_STAT_NORCVBUF, "norcvbuf", "rx_no_buf" },
+ { MAC_STAT_IERRORS, "ierrors", NULL },
+ { MAC_STAT_UNKNOWNS, "unknowns", NULL },
+ { MAC_STAT_NOXMTBUF, "noxmtbuf", "No Txpkt " },
+ { MAC_STAT_OERRORS, "oerrors", NULL },
+ { MAC_STAT_COLLISIONS, "collisions", NULL },
+ { MAC_STAT_RBYTES, "rbytes64", "rbytes" },
+ { MAC_STAT_IPACKETS, "ipackets64", "ipackets" },
+ { MAC_STAT_OBYTES, "obytes64", "obytes" },
+ { MAC_STAT_OPACKETS, "opackets64", "opackets" },
+ { MAC_STAT_UNDERFLOWS, "uflo", NULL },
+ { MAC_STAT_OVERFLOWS, "oflo", NULL }
+};
+
+/*
+ * Must be the same order as ether_stat.
+ */
+static i_softmac_stat_info_t i_softmac_ether_si[] = {
+ { ETHER_STAT_ALIGN_ERRORS, "align_errors",
+ "alignment_err" },
+ { ETHER_STAT_FCS_ERRORS, "fcs_errors", "crc_err" },
+ { ETHER_STAT_FIRST_COLLISIONS, "first_collisions", NULL },
+ { ETHER_STAT_MULTI_COLLISIONS, "multi_collisions", NULL },
+ { ETHER_STAT_SQE_ERRORS, "sqe_errors", NULL },
+ { ETHER_STAT_DEFER_XMTS, "defer_xmts", NULL },
+ { ETHER_STAT_TX_LATE_COLLISIONS, "tx_late_collisions",
+ "late_collisions" },
+ { ETHER_STAT_EX_COLLISIONS, "ex_collisions",
+ "excessive_collisions" },
+ { ETHER_STAT_MACXMT_ERRORS, "macxmt_errors", NULL },
+ { ETHER_STAT_CARRIER_ERRORS, "carrier_errors", NULL },
+ { ETHER_STAT_TOOLONG_ERRORS, "toolong_errors", "length_err" },
+ { ETHER_STAT_MACRCV_ERRORS, "macrcv_errors",
+ "Rx Error Count" },
+
+ { ETHER_STAT_XCVR_ADDR, "xcvr_addr", NULL },
+ { ETHER_STAT_XCVR_ID, "xcvr_id", NULL },
+ { ETHER_STAT_XCVR_INUSE, "xcvr_inuse", NULL },
+
+ { ETHER_STAT_CAP_1000FDX, "cap_1000fdx", NULL },
+ { ETHER_STAT_CAP_1000HDX, "cap_1000hdx", NULL },
+ { ETHER_STAT_CAP_100FDX, "cap_100fdx", NULL },
+ { ETHER_STAT_CAP_100HDX, "cap_100hdx", NULL },
+ { ETHER_STAT_CAP_10FDX, "cap_10fdx", NULL },
+ { ETHER_STAT_CAP_10HDX, "cap_10hdx", NULL },
+ { ETHER_STAT_CAP_ASMPAUSE, "cap_asmpause", NULL },
+ { ETHER_STAT_CAP_PAUSE, "cap_pause", NULL },
+ { ETHER_STAT_CAP_AUTONEG, "cap_autoneg", NULL },
+
+ { ETHER_STAT_ADV_CAP_1000FDX, "adv_cap_1000fdx", NULL },
+ { ETHER_STAT_ADV_CAP_1000HDX, "adv_cap_1000hdx", NULL },
+ { ETHER_STAT_ADV_CAP_100FDX, "adv_cap_100fdx", NULL },
+ { ETHER_STAT_ADV_CAP_100HDX, "adv_cap_100hdx", NULL },
+ { ETHER_STAT_ADV_CAP_10FDX, "adv_cap_10fdx", NULL },
+ { ETHER_STAT_ADV_CAP_10HDX, "adv_cap_10hdx", NULL },
+ { ETHER_STAT_ADV_CAP_ASMPAUSE, "adv_cap_asmpause", NULL },
+ { ETHER_STAT_ADV_CAP_PAUSE, "adv_cap_pause", NULL },
+ { ETHER_STAT_ADV_CAP_AUTONEG, "adv_cap_autoneg", NULL },
+
+ { ETHER_STAT_LP_CAP_1000FDX, "lp_cap_1000fdx", NULL },
+ { ETHER_STAT_LP_CAP_1000HDX, "lp_cap_1000hdx", NULL },
+ { ETHER_STAT_LP_CAP_100FDX, "lp_cap_100fdx", NULL },
+ { ETHER_STAT_LP_CAP_100HDX, "lp_cap_100hdx", NULL },
+ { ETHER_STAT_LP_CAP_10FDX, "lp_cap_10fdx", NULL },
+ { ETHER_STAT_LP_CAP_10HDX, "lp_cap_10hdx", NULL },
+ { ETHER_STAT_LP_CAP_ASMPAUSE, "lp_cap_asmpause", NULL },
+ { ETHER_STAT_LP_CAP_PAUSE, "lp_cap_pause", NULL },
+ { ETHER_STAT_LP_CAP_AUTONEG, "lp_cap_autoneg", NULL },
+
+ { ETHER_STAT_LINK_ASMPAUSE, "link_asmpause", NULL },
+ { ETHER_STAT_LINK_PAUSE, "link_pause", NULL },
+ { ETHER_STAT_LINK_AUTONEG, "link_autoneg", NULL },
+ { ETHER_STAT_LINK_DUPLEX, "link_duplex", "duplex" },
+
+ { ETHER_STAT_TOOSHORT_ERRORS, "runt_errors", NULL },
+ { ETHER_STAT_CAP_REMFAULT, "cap_rem_fault", NULL },
+ { ETHER_STAT_ADV_REMFAULT, "adv_rem_fault", NULL },
+ { ETHER_STAT_LP_REMFAULT, "lp_rem_fault", NULL },
+
+ { ETHER_STAT_JABBER_ERRORS, "jabber_errors", NULL },
+ { ETHER_STAT_CAP_100T4, "cap_100T4", NULL },
+ { ETHER_STAT_ADV_CAP_100T4, "adv_cap_100T4", NULL },
+ { ETHER_STAT_LP_CAP_100T4, "lp_cap_100T4", NULL }
+};
+
+static kstat_t *softmac_hold_dev_kstat(softmac_t *);
+static void softmac_rele_dev_kstat(kstat_t *);
+static int softmac_get_kstat(kstat_t *, char *, uint64_t *);
+
+static kstat_t *
+softmac_hold_dev_kstat(softmac_t *softmac)
+{
+ char drv[MAXLINKNAMELEN];
+ uint_t ppa;
+ kstat_t *ksp;
+
+ if (ddi_parse(softmac->smac_devname, drv, &ppa) != DDI_SUCCESS)
+ return (NULL);
+
+ /*
+ * Find the kstat by the module name and the instance number.
+ */
+ ksp = kstat_hold_byname(drv, ppa, softmac->smac_devname, ALL_ZONES);
+ if (ksp != NULL) {
+ KSTAT_ENTER(ksp);
+
+ if ((ksp->ks_data != NULL) &&
+ (ksp->ks_type == KSTAT_TYPE_NAMED)) {
+ /*
+ * Update the kstat to get the latest statistics.
+ */
+ if (KSTAT_UPDATE(ksp, KSTAT_READ) == 0)
+ return (ksp);
+ }
+
+ KSTAT_EXIT(ksp);
+ kstat_rele(ksp);
+ }
+ return (NULL);
+}
+
+static void
+softmac_rele_dev_kstat(kstat_t *ksp)
+{
+ KSTAT_EXIT(ksp);
+ kstat_rele(ksp);
+}
+
+/*
+ * The kstat needs to be held when calling this function.
+ */
+static int
+softmac_get_kstat(kstat_t *ksp, char *name, uint64_t *valp)
+{
+ kstat_named_t *knp;
+ int i;
+ int ret = ENOTSUP;
+
+ if (name == NULL)
+ return (ret);
+
+ /*
+ * Search the kstat with the given name.
+ */
+ for (i = 0, knp = KSTAT_NAMED_PTR(ksp); i < ksp->ks_ndata; i++, knp++) {
+ if (strcmp(knp->name, name) == 0) {
+ switch (knp->data_type) {
+ case KSTAT_DATA_INT32:
+ case KSTAT_DATA_UINT32:
+ *valp = (uint64_t)(knp->value.ui32);
+ ret = 0;
+ break;
+ case KSTAT_DATA_INT64:
+ case KSTAT_DATA_UINT64:
+ *valp = knp->value.ui64;
+ ret = 0;
+ break;
+#ifdef _LP64
+ case KSTAT_DATA_LONG:
+ case KSTAT_DATA_ULONG:
+ *valp = (uint64_t)knp->value.ul;
+ ret = 0;
+ break;
+#endif
+ case KSTAT_DATA_CHAR:
+ if (strcmp(name, "duplex") != 0)
+ break;
+ if (strncmp(knp->value.c, "full", 4) == 0)
+ *valp = LINK_DUPLEX_FULL;
+ else if (strncmp(knp->value.c, "half", 4) == 0)
+ *valp = LINK_DUPLEX_HALF;
+ else
+ *valp = LINK_DUPLEX_UNKNOWN;
+ ret = 0;
+ break;
+ }
+ break;
+ }
+ }
+
+ return (ret);
+}
+
+int
+softmac_m_stat(void *arg, uint_t stat, uint64_t *val)
+{
+ softmac_t *softmac = arg;
+ kstat_t *ksp;
+ uint_t index;
+ int ret;
+
+ if ((ksp = softmac_hold_dev_kstat(softmac)) == NULL)
+ return (ENOTSUP);
+
+ if (IS_MAC_STAT(stat)) {
+ index = stat - MAC_STAT_MIN;
+ if ((ret = softmac_get_kstat(ksp,
+ i_softmac_driver_si[index].ssi_name, val)) != 0) {
+ ret = softmac_get_kstat(ksp,
+ i_softmac_driver_si[index].ssi_alias, val);
+ }
+ } else {
+ ASSERT(IS_MACTYPE_STAT(stat));
+ index = stat - MACTYPE_STAT_MIN;
+
+ switch (softmac->smac_media) {
+ case DL_ETHER:
+ if ((ret = softmac_get_kstat(ksp,
+ i_softmac_ether_si[index].ssi_name, val)) != 0) {
+ ret = softmac_get_kstat(ksp,
+ i_softmac_ether_si[index].ssi_alias, val);
+ }
+ break;
+ default:
+ ret = ENOTSUP;
+ break;
+ }
+ }
+
+ softmac_rele_dev_kstat(ksp);
+ return (ret);
+}