dld_str.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2005 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
/*
* Data-Link Driver
*/
#include <sys/types.h>
#include <sys/stream.h>
#include <sys/stropts.h>
#include <sys/strsun.h>
#include <sys/strsubr.h>
#include <sys/atomic.h>
#include <sys/sdt.h>
#include <sys/mac.h>
#include <sys/dls.h>
#include <sys/dld.h>
#include <sys/dld_impl.h>
#include <sys/taskq.h>
#include <sys/vlan.h>
static int str_constructor(void *, void *, int);
static void str_destructor(void *, void *);
static void str_m_put(dld_str_t *, mblk_t *);
static void str_m_srv(dld_str_t *, mblk_t *);
static void str_mdata_fastpath_put(dld_str_t *, mblk_t *);
static void str_mdata_raw_put(dld_str_t *, mblk_t *);
static void str_mdata_srv(dld_str_t *, mblk_t *);
static void str_mproto_put(dld_str_t *, mblk_t *);
static void str_mpcproto_put(dld_str_t *, mblk_t *);
static void str_mioctl_put(dld_str_t *, mblk_t *);
static void str_mflush_put(dld_str_t *, mblk_t *);
static mblk_t *str_unitdata_ind(dld_str_t *, mblk_t *);
static void str_notify_promisc_on_phys(dld_str_t *);
static void str_notify_promisc_off_phys(dld_str_t *);
static void str_notify_phys_addr(dld_str_t *, const uint8_t *);
static void str_notify_link_up(dld_str_t *);
static void str_notify_link_down(dld_str_t *);
static void str_notify_capab_reneg(dld_str_t *);
static void str_notify_speed(dld_str_t *, uint32_t);
static void str_notify(void *, mac_notify_type_t);
static void str_putbq(queue_t *q, mblk_t *mp);
static uint32_t str_count;
static kmem_cache_t *str_cachep;
typedef struct str_msg_info {
uint8_t smi_type;
const char *smi_txt;
void (*smi_put)(dld_str_t *, mblk_t *);
void (*smi_srv)(dld_str_t *, mblk_t *);
} str_msg_info_t;
/*
* Normal priority message jump table.
*/
str_msg_info_t str_mi[] = {
{ M_DATA, "M_DATA", str_m_put, str_m_srv },
{ M_PROTO, "M_PROTO", str_mproto_put, str_m_srv },
{ 0x02, "undefined", str_m_put, str_m_srv },
{ 0x03, "undefined", str_m_put, str_m_srv },
{ 0x04, "undefined", str_m_put, str_m_srv },
{ 0x05, "undefined", str_m_put, str_m_srv },
{ 0x06, "undefined", str_m_put, str_m_srv },
{ 0x07, "undefined", str_m_put, str_m_srv },
{ M_BREAK, "M_BREAK", str_m_put, str_m_srv },
{ M_PASSFP, "M_PASSFP", str_m_put, str_m_srv },
{ M_EVENT, "M_EVENT", str_m_put, str_m_srv },
{ M_SIG, "M_SIG", str_m_put, str_m_srv },
{ M_DELAY, "M_DELAY", str_m_put, str_m_srv },
{ M_CTL, "M_CTL", str_m_put, str_m_srv },
{ M_IOCTL, "M_IOCTL", str_mioctl_put, str_m_srv },
{ M_SETOPTS, "M_SETOPTS", str_m_put, str_m_srv },
{ M_RSE, "M_RSE", str_m_put, str_m_srv }
};
#define STR_MI_COUNT (sizeof (str_mi) / sizeof (str_mi[0]))
/*
* High priority message jump table.
*/
str_msg_info_t str_pmi[] = {
{ 0x80, "undefined", str_m_put, str_m_srv },
{ M_IOCACK, "M_IOCACK", str_m_put, str_m_srv },
{ M_IOCNAK, "M_IOCNAK", str_m_put, str_m_srv },
{ M_PCPROTO, "M_PCPROTO", str_mpcproto_put, str_m_srv },
{ M_PCSIG, "M_PCSIG", str_m_put, str_m_srv },
{ M_READ, "M_READ", str_m_put, str_m_srv },
{ M_FLUSH, "M_FLUSH", str_mflush_put, str_m_srv },
{ M_STOP, "M_STOP", str_m_put, str_m_srv },
{ M_START, "M_START", str_m_put, str_m_srv },
{ M_HANGUP, "M_HANGUP", str_m_put, str_m_srv },
{ M_ERROR, "M_ERROR", str_m_put, str_m_srv },
{ M_COPYIN, "M_COPYIN", str_m_put, str_m_srv },
{ M_COPYOUT, "M_COPYOUT", str_m_put, str_m_srv },
{ M_IOCDATA, "M_IOCDATA", str_m_put, str_m_srv },
{ M_PCRSE, "M_PCRSE", str_m_put, str_m_srv },
{ M_STOPI, "M_STOPI", str_m_put, str_m_srv },
{ M_STARTI, "M_STARTI", str_m_put, str_m_srv },
{ M_PCEVENT, "M_PCEVENT", str_m_put, str_m_srv },
{ M_UNHANGUP, "M_UNHANGUP", str_m_put, str_m_srv }
};
#define STR_PMI_COUNT (sizeof (str_pmi) / sizeof (str_pmi[0]))
/*
* Initialize this module's data structures.
*/
void
dld_str_init(void)
{
/*
* Create dld_str_t object cache.
*/
str_cachep = kmem_cache_create("dld_str_cache", sizeof (dld_str_t),
0, str_constructor, str_destructor, NULL, NULL, NULL, 0);
ASSERT(str_cachep != NULL);
}
/*
* Tear down this module's data structures.
*/
int
dld_str_fini(void)
{
/*
* Make sure that there are no objects in use.
*/
if (str_count != 0)
return (EBUSY);
/*
* Destroy object cache.
*/
kmem_cache_destroy(str_cachep);
return (0);
}
/*
* Create a new dld_str_t object.
*/
dld_str_t *
dld_str_create(queue_t *rq)
{
dld_str_t *dsp;
/*
* Allocate an object from the cache.
*/
dsp = kmem_cache_alloc(str_cachep, KM_SLEEP);
atomic_add_32(&str_count, 1);
/*
* Initialize the queue pointers.
*/
ASSERT(RD(rq) == rq);
dsp->ds_rq = rq;
dsp->ds_wq = WR(rq);
rq->q_ptr = WR(rq)->q_ptr = (void *)dsp;
return (dsp);
}
/*
* Destroy a dld_str_t object.
*/
void
dld_str_destroy(dld_str_t *dsp)
{
queue_t *rq;
queue_t *wq;
/*
* Clear the queue pointers.
*/
rq = dsp->ds_rq;
wq = dsp->ds_wq;
ASSERT(wq == WR(rq));
rq->q_ptr = wq->q_ptr = NULL;
dsp->ds_rq = dsp->ds_wq = NULL;
/*
* Clear down notifications.
*/
dsp->ds_notifications = 0;
/*
* Free the object back to the cache.
*/
kmem_cache_free(str_cachep, dsp);
atomic_add_32(&str_count, -1);
}
/*
* kmem_cache contructor function: see kmem_cache_create(9f).
*/
/*ARGSUSED*/
static int
str_constructor(void *buf, void *cdrarg, int kmflags)
{
dld_str_t *dsp = buf;
bzero(buf, sizeof (dld_str_t));
/*
* Take a copy of the global message handler jump tables.
*/
ASSERT(dsp->ds_mi == NULL);
if ((dsp->ds_mi = kmem_zalloc(sizeof (str_mi), kmflags)) == NULL)
return (-1);
bcopy(str_mi, dsp->ds_mi, sizeof (str_mi));
ASSERT(dsp->ds_pmi == NULL);
if ((dsp->ds_pmi = kmem_zalloc(sizeof (str_pmi), kmflags)) == NULL) {
kmem_free(dsp->ds_mi, sizeof (str_mi));
dsp->ds_mi = NULL;
return (-1);
}
bcopy(str_pmi, dsp->ds_pmi, sizeof (str_pmi));
/*
* Allocate a new minor number.
*/
if ((dsp->ds_minor = dld_minor_hold(kmflags == KM_SLEEP)) == 0) {
kmem_free(dsp->ds_mi, sizeof (str_mi));
dsp->ds_mi = NULL;
kmem_free(dsp->ds_pmi, sizeof (str_pmi));
dsp->ds_pmi = NULL;
return (-1);
}
/*
* Initialize the DLPI state machine.
*/
dsp->ds_dlstate = DL_UNATTACHED;
return (0);
}
/*
* kmem_cache destructor function.
*/
/*ARGSUSED*/
static void
str_destructor(void *buf, void *cdrarg)
{
dld_str_t *dsp = buf;
/*
* Make sure the DLPI state machine was reset.
*/
ASSERT(dsp->ds_dlstate == DL_UNATTACHED);
/*
* Make sure the data-link interface was closed.
*/
ASSERT(dsp->ds_mh == NULL);
ASSERT(dsp->ds_dc == NULL);
/*
* Make sure enabled notifications are cleared.
*/
ASSERT(dsp->ds_notifications == 0);
/*
* Make sure polling is disabled.
*/
ASSERT(!dsp->ds_polling);
/*
* Make sure M_DATA message handling is disabled.
*/
ASSERT(dsp->ds_mi[M_DATA].smi_put == str_m_put);
ASSERT(dsp->ds_mi[M_DATA].smi_srv == str_m_srv);
/*
* Release the minor number.
*/
dld_minor_rele(dsp->ds_minor);
/*
* Clear down the jump tables.
*/
kmem_free(dsp->ds_mi, sizeof (str_mi));
dsp->ds_mi = NULL;
kmem_free(dsp->ds_pmi, sizeof (str_pmi));
dsp->ds_pmi = NULL;
}
/*
* Called from put(9e) to process a streams message.
*/
void
dld_str_put(dld_str_t *dsp, mblk_t *mp)
{
uint8_t type;
str_msg_info_t *smip;
/*
* Look up the message handler from the appropriate jump table.
*/
if ((type = DB_TYPE(mp)) & QPCTL) {
/*
* Clear the priority bit to index into the jump table.
*/
type &= ~QPCTL;
/*
* Check the message is not out of range for the jump table.
*/
if (type >= STR_PMI_COUNT)
goto unknown;
/*
* Get the handler from the jump table.
*/
smip = &(dsp->ds_pmi[type]);
/*
* OR the priorty bit back in to restore the original message
* type.
*/
type |= QPCTL;
} else {
/*
* Check the message is not out of range for the jump table.
*/
if (type >= STR_MI_COUNT)
goto unknown;
/*
* Get the handler from the jump table.
*/
smip = &(dsp->ds_mi[type]);
}
ASSERT(smip->smi_type == type);
smip->smi_put(dsp, mp);
return;
unknown:
str_m_put(dsp, mp);
}
/*
* Called from srv(9e) to process a streams message.
*/
void
dld_str_srv(dld_str_t *dsp, mblk_t *mp)
{
uint8_t type;
str_msg_info_t *smip;
/*
* Look up the message handler from the appropriate jump table.
*/
if ((type = DB_TYPE(mp)) & QPCTL) {
/*
* Clear the priority bit to index into the jump table.
*/
type &= ~QPCTL;
/*
* Check the message is not out of range for the jump table.
*/
if (type >= STR_PMI_COUNT)
goto unknown;
/*
* Get the handler from the jump table.
*/
smip = &(dsp->ds_pmi[type]);
/*
* OR the priorty bit back in to restore the original message
* type.
*/
type |= QPCTL;
} else {
/*
* Check the message is not out of range for the jump table.
*/
if (type >= STR_MI_COUNT)
goto unknown;
/*
* Get the handler from the jump table.
*/
ASSERT(type < STR_MI_COUNT);
smip = &(dsp->ds_mi[type]);
}
ASSERT(smip->smi_type == type);
smip->smi_srv(dsp, mp);
return;
unknown:
str_m_srv(dsp, mp);
}
/*
* M_DATA put (IP fast-path mode)
*/
static void
str_mdata_fastpath_put(dld_str_t *dsp, mblk_t *mp)
{
queue_t *q = dsp->ds_wq;
/*
* If something is already queued then we must queue to avoid
* re-ordering.
*/
if (q->q_first != NULL) {
(void) putq(q, mp);
return;
}
/*
* Attempt to transmit the packet.
*/
if ((mp = dls_tx(dsp->ds_dc, mp)) != NULL) {
(void) putbq(q, mp);
qenable(q);
}
}
/*
* M_DATA put (raw mode)
*/
static void
str_mdata_raw_put(dld_str_t *dsp, mblk_t *mp)
{
queue_t *q = dsp->ds_wq;
struct ether_header *ehp;
mblk_t *bp;
size_t size;
size_t hdrlen;
size = MBLKL(mp);
if (size < sizeof (struct ether_header))
goto discard;
hdrlen = sizeof (struct ether_header);
ehp = (struct ether_header *)mp->b_rptr;
if (ntohs(ehp->ether_type) == VLAN_TPID) {
struct ether_vlan_header *evhp;
if (size < sizeof (struct ether_vlan_header))
goto discard;
/*
* Replace vtag with our own
*/
evhp = (struct ether_vlan_header *)ehp;
evhp->ether_tci = htons(VLAN_TCI(dsp->ds_pri,
ETHER_CFI, dsp->ds_vid));
hdrlen = sizeof (struct ether_vlan_header);
}
/*
* Check the packet is not too big and that any remaining
* fragment list is composed entirely of M_DATA messages. (We
* know the first fragment was M_DATA otherwise we could not
* have got here).
*/
for (bp = mp->b_next; bp != NULL; bp = bp->b_cont) {
if (DB_TYPE(bp) != M_DATA)
goto discard;
size += MBLKL(bp);
}
if (size > dsp->ds_mip->mi_sdu_max + hdrlen)
goto discard;
/*
* If something is already queued then we must queue to avoid
* re-ordering.
*/
if (q->q_first != NULL) {
(void) putq(q, bp);
return;
}
/*
* Attempt to transmit the packet.
*/
if ((mp = dls_tx(dsp->ds_dc, mp)) != NULL) {
(void) putbq(q, mp);
qenable(q);
}
return;
discard:
freemsg(mp);
}
/*
* M_DATA srv
*/
static void
str_mdata_srv(dld_str_t *dsp, mblk_t *mp)
{
queue_t *q = dsp->ds_wq;
/*
* Attempt to transmit the packet.
*/
if ((mp = dls_tx(dsp->ds_dc, mp)) == NULL)
return;
(void) str_putbq(q, mp);
qenable(q);
}
/*
* M_PROTO put
*/
static void
str_mproto_put(dld_str_t *dsp, mblk_t *mp)
{
dld_proto(dsp, mp);
}
/*
* M_PCPROTO put
*/
static void
str_mpcproto_put(dld_str_t *dsp, mblk_t *mp)
{
dld_proto(dsp, mp);
}
/*
* M_IOCTL put
*/
static void
str_mioctl_put(dld_str_t *dsp, mblk_t *mp)
{
dld_ioc(dsp, mp);
}
/*
* M_FLUSH put
*/
/*ARGSUSED*/
static void
str_mflush_put(dld_str_t *dsp, mblk_t *mp)
{
queue_t *q = dsp->ds_wq;
if (*mp->b_rptr & FLUSHW) {
flushq(q, FLUSHALL);
*mp->b_rptr &= ~FLUSHW;
}
if (*mp->b_rptr & FLUSHR)
qreply(q, mp);
else
freemsg(mp);
}
/*
* M_* put.
*/
/*ARGSUSED*/
static void
str_m_put(dld_str_t *dsp, mblk_t *mp)
{
freemsg(mp);
}
/*
* M_* put.
*/
/*ARGSUSED*/
static void
str_m_srv(dld_str_t *dsp, mblk_t *mp)
{
freemsgchain(mp);
}
/*
* Process DL_ATTACH_REQ (style 2) or open(2) (style 1).
*/
int
dld_str_attach(dld_str_t *dsp, dld_ppa_t *dpp)
{
int err;
dls_channel_t dc;
uint_t addr_length;
ASSERT(dsp->ds_dc == NULL);
/*
* Open a channel.
*/
if ((err = dls_open(dpp->dp_name, &dc)) != 0)
return (err);
/*
* Cache the MAC interface handle, a pointer to the immutable MAC
* information and the current and 'factory' MAC address.
*/
dsp->ds_mh = dls_mac(dc);
dsp->ds_mip = mac_info(dsp->ds_mh);
mac_unicst_get(dsp->ds_mh, dsp->ds_curr_addr);
addr_length = dsp->ds_mip->mi_addr_length;
bcopy(dsp->ds_mip->mi_unicst_addr, dsp->ds_fact_addr, addr_length);
/*
* Cache the interface VLAN identifier. (This will be VLAN_ID_NONE for
* a non-VLAN interface).
*/
dsp->ds_vid = dls_vid(dc);
/*
* Set the default packet priority.
*/
dsp->ds_pri = 0;
/*
* Add a notify function so that the we get updates from the MAC.
*/
dsp->ds_mnh = mac_notify_add(dsp->ds_mh, str_notify, (void *)dsp);
dsp->ds_dc = dc;
return (0);
}
/*
* Process DL_DETACH_REQ (style 2) or close(2) (style 1). Can also be called
* from close(2) for style 2.
*/
void
dld_str_detach(dld_str_t *dsp)
{
/*
* Remove the notify function.
*/
mac_notify_remove(dsp->ds_mh, dsp->ds_mnh);
/*
* Make sure the M_DATA handler is reset.
*/
dld_str_tx_drop(dsp);
/*
* Clear the polling flag.
*/
dsp->ds_polling = B_FALSE;
/*
* Close the channel.
*/
dls_close(dsp->ds_dc);
dsp->ds_dc = NULL;
dsp->ds_mh = NULL;
}
/*
* Enable raw mode for this stream. This mode is mutually exclusive with
* fast-path and/or polling.
*/
void
dld_str_tx_raw(dld_str_t *dsp)
{
/*
* Enable M_DATA message handling.
*/
dsp->ds_mi[M_DATA].smi_put = str_mdata_raw_put;
dsp->ds_mi[M_DATA].smi_srv = str_mdata_srv;
}
/*
* Enable fast-path for this stream.
*/
void
dld_str_tx_fastpath(dld_str_t *dsp)
{
/*
* Enable M_DATA message handling.
*/
dsp->ds_mi[M_DATA].smi_put = str_mdata_fastpath_put;
dsp->ds_mi[M_DATA].smi_srv = str_mdata_srv;
}
/*
* Disable fast-path or raw mode.
*/
void
dld_str_tx_drop(dld_str_t *dsp)
{
/*
* Disable M_DATA message handling.
*/
dsp->ds_mi[M_DATA].smi_put = str_m_put;
dsp->ds_mi[M_DATA].smi_srv = str_m_srv;
}
/*
* Raw mode receive function.
*/
/*ARGSUSED*/
void
dld_str_rx_raw(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
size_t header_length)
{
dld_str_t *dsp = (dld_str_t *)arg;
mblk_t *next;
ASSERT(mp != NULL);
do {
/*
* Get the pointer to the next packet in the chain and then
* clear b_next before the packet gets passed on.
*/
next = mp->b_next;
mp->b_next = NULL;
/*
* Wind back b_rptr to point at the MAC header.
*/
ASSERT(mp->b_rptr >= DB_BASE(mp) + header_length);
mp->b_rptr -= header_length;
if (header_length == sizeof (struct ether_vlan_header)) {
/*
* Strip off the vtag
*/
ovbcopy(mp->b_rptr, mp->b_rptr + VLAN_TAGSZ,
2 * ETHERADDRL);
mp->b_rptr += VLAN_TAGSZ;
}
/*
* Pass the packet on.
*/
putnext(dsp->ds_rq, mp);
/*
* Move on to the next packet in the chain.
*/
mp = next;
} while (mp != NULL);
}
/*
* Fast-path receive function.
*/
/*ARGSUSED*/
void
dld_str_rx_fastpath(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
size_t header_length)
{
dld_str_t *dsp = (dld_str_t *)arg;
mblk_t *next;
ASSERT(mp != NULL);
do {
/*
* Get the pointer to the next packet in the chain and then
* clear b_next before the packet gets passed on.
*/
next = mp->b_next;
mp->b_next = NULL;
/*
* Pass the packet on.
*/
putnext(dsp->ds_rq, mp);
/*
* Move on to the next packet in the chain.
*/
mp = next;
} while (mp != NULL);
}
/*
* Default receive function (send DL_UNITDATA_IND messages).
*/
/*ARGSUSED*/
void
dld_str_rx_unitdata(void *arg, mac_resource_handle_t mrh, mblk_t *mp,
size_t header_length)
{
dld_str_t *dsp = (dld_str_t *)arg;
mblk_t *ud_mp;
mblk_t *next;
ASSERT(mp != NULL);
do {
/*
* Get the pointer to the next packet in the chain and then
* clear b_next before the packet gets passed on.
*/
next = mp->b_next;
mp->b_next = NULL;
/*
* Wind back b_rptr to point at the MAC header.
*/
ASSERT(mp->b_rptr >= DB_BASE(mp) + header_length);
mp->b_rptr -= header_length;
/*
* Create the DL_UNITDATA_IND M_PROTO.
*/
if ((ud_mp = str_unitdata_ind(dsp, mp)) == NULL) {
freemsgchain(mp);
return;
}
/*
* Advance b_rptr to point at the payload again.
*/
mp->b_rptr += header_length;
/*
* Prepend the DL_UNITDATA_IND.
*/
ud_mp->b_cont = mp;
/*
* Send the message.
*/
putnext(dsp->ds_rq, ud_mp);
/*
* Move on to the next packet in the chain.
*/
mp = next;
} while (mp != NULL);
}
/*
* Generate DL_NOTIFY_IND messages to notify the DLPI consumer of the
* current state of the interface.
*/
void
dld_str_notify_ind(dld_str_t *dsp)
{
mac_notify_type_t type;
for (type = 0; type < MAC_NNOTE; type++)
str_notify(dsp, type);
}
typedef struct dl_unitdata_ind_wrapper {
dl_unitdata_ind_t dl_unitdata;
uint8_t dl_dest_addr[MAXADDRLEN + sizeof (uint16_t)];
uint8_t dl_src_addr[MAXADDRLEN + sizeof (uint16_t)];
} dl_unitdata_ind_wrapper_t;
/*
* Create a DL_UNITDATA_IND M_PROTO message.
*/
static mblk_t *
str_unitdata_ind(dld_str_t *dsp, mblk_t *mp)
{
mblk_t *nmp;
dl_unitdata_ind_wrapper_t *dlwp;
dl_unitdata_ind_t *dlp;
dls_header_info_t dhi;
uint_t addr_length;
uint8_t *daddr;
uint8_t *saddr;
/*
* Get the packet header information.
*/
dls_header_info(dsp->ds_dc, mp, &dhi);
/*
* Allocate a message large enough to contain the wrapper structure
* defined above.
*/
if ((nmp = mexchange(dsp->ds_wq, NULL,
sizeof (dl_unitdata_ind_wrapper_t), M_PROTO,
DL_UNITDATA_IND)) == NULL)
return (NULL);
dlwp = (dl_unitdata_ind_wrapper_t *)nmp->b_rptr;
dlp = &(dlwp->dl_unitdata);
ASSERT(dlp == (dl_unitdata_ind_t *)nmp->b_rptr);
ASSERT(dlp->dl_primitive == DL_UNITDATA_IND);
/*
* Copy in the destination address.
*/
addr_length = dsp->ds_mip->mi_addr_length;
daddr = dlwp->dl_dest_addr;
dlp->dl_dest_addr_offset = (uintptr_t)daddr - (uintptr_t)dlp;
bcopy(dhi.dhi_daddr, daddr, addr_length);
/*
* Set the destination DLSAP to our bound DLSAP value.
*/
*(uint16_t *)(daddr + addr_length) = dsp->ds_sap;
dlp->dl_dest_addr_length = addr_length + sizeof (uint16_t);
/*
* If the destination address was a group address then
* dl_group_address field should be non-zero.
*/
dlp->dl_group_address = dhi.dhi_isgroup;
/*
* Copy in the source address.
*/
saddr = dlwp->dl_src_addr;
dlp->dl_src_addr_offset = (uintptr_t)saddr - (uintptr_t)dlp;
bcopy(dhi.dhi_saddr, saddr, addr_length);
/*
* Set the source DLSAP to the packet ethertype.
*/
*(uint16_t *)(saddr + addr_length) = dhi.dhi_ethertype;
dlp->dl_src_addr_length = addr_length + sizeof (uint16_t);
return (nmp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_PROMISC_ON_PHYS
*/
static void
str_notify_promisc_on_phys(dld_str_t *dsp)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_PROMISC_ON_PHYS))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_PROMISC_ON_PHYS;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_PROMISC_OFF_PHYS
*/
static void
str_notify_promisc_off_phys(dld_str_t *dsp)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_PROMISC_OFF_PHYS))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_PROMISC_OFF_PHYS;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_PHYS_ADDR
*/
static void
str_notify_phys_addr(dld_str_t *dsp, const uint8_t *addr)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
uint_t addr_length;
uint16_t ethertype;
if (!(dsp->ds_notifications & DL_NOTE_PHYS_ADDR))
return;
addr_length = dsp->ds_mip->mi_addr_length;
if ((mp = mexchange(dsp->ds_wq, NULL,
sizeof (dl_notify_ind_t) + addr_length + sizeof (uint16_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_PHYS_ADDR;
dlip->dl_data = DL_CURR_PHYS_ADDR;
dlip->dl_addr_offset = sizeof (dl_notify_ind_t);
dlip->dl_addr_length = addr_length + sizeof (uint16_t);
bcopy(addr, &dlip[1], addr_length);
ethertype = (dsp->ds_sap < ETHERTYPE_802_MIN) ? 0 : dsp->ds_sap;
*(uint16_t *)((uchar_t *)(dlip + 1) + addr_length) =
ethertype;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_LINK_UP
*/
static void
str_notify_link_up(dld_str_t *dsp)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_LINK_UP))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_LINK_UP;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_LINK_DOWN
*/
static void
str_notify_link_down(dld_str_t *dsp)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_LINK_DOWN))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_LINK_DOWN;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_SPEED
*/
static void
str_notify_speed(dld_str_t *dsp, uint32_t speed)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_SPEED))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_SPEED;
dlip->dl_data = speed;
qreply(dsp->ds_wq, mp);
}
/*
* DL_NOTIFY_IND: DL_NOTE_CAPAB_RENEG
*/
static void
str_notify_capab_reneg(dld_str_t *dsp)
{
mblk_t *mp;
dl_notify_ind_t *dlip;
if (!(dsp->ds_notifications & DL_NOTE_CAPAB_RENEG))
return;
if ((mp = mexchange(dsp->ds_wq, NULL, sizeof (dl_notify_ind_t),
M_PROTO, 0)) == NULL)
return;
bzero(mp->b_rptr, sizeof (dl_notify_ind_t));
dlip = (dl_notify_ind_t *)mp->b_rptr;
dlip->dl_primitive = DL_NOTIFY_IND;
dlip->dl_notification = DL_NOTE_CAPAB_RENEG;
qreply(dsp->ds_wq, mp);
}
/*
* MAC notification callback.
*/
static void
str_notify(void *arg, mac_notify_type_t type)
{
dld_str_t *dsp = (dld_str_t *)arg;
queue_t *q = dsp->ds_wq;
switch (type) {
case MAC_NOTE_TX:
enableok(q);
qenable(q);
break;
case MAC_NOTE_DEVPROMISC:
/*
* Send the appropriate DL_NOTIFY_IND.
*/
if (mac_promisc_get(dsp->ds_mh, MAC_DEVPROMISC))
str_notify_promisc_on_phys(dsp);
else
str_notify_promisc_off_phys(dsp);
break;
case MAC_NOTE_PROMISC:
break;
case MAC_NOTE_UNICST:
/*
* This notification is sent whenever the MAC unicast address
* changes. We need to re-cache the address.
*/
mac_unicst_get(dsp->ds_mh, dsp->ds_curr_addr);
/*
* Send the appropriate DL_NOTIFY_IND.
*/
str_notify_phys_addr(dsp, dsp->ds_curr_addr);
break;
case MAC_NOTE_LINK:
/*
* This notification is sent every time the MAC driver
* updates the link state.
*/
switch (mac_link_get(dsp->ds_mh)) {
case LINK_STATE_UP:
/*
* The link is up so send the appropriate
* DL_NOTIFY_IND.
*/
str_notify_link_up(dsp);
/*
* If we can find the link speed then send a
* DL_NOTIFY_IND for that too.
*/
if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED]) {
uint64_t val;
val = mac_stat_get(dsp->ds_mh,
MAC_STAT_IFSPEED);
str_notify_speed(dsp,
(uint32_t)(val / 1000ull));
}
break;
case LINK_STATE_DOWN:
/*
* The link is down so send the appropriate
* DL_NOTIFY_IND.
*/
str_notify_link_down(dsp);
break;
default:
break;
}
break;
case MAC_NOTE_RESOURCE:
/*
* This notification is sent whenever the MAC resources
* change. We need to renegotiate the capabilities.
* Send the appropriate DL_NOTIFY_IND.
*/
str_notify_capab_reneg(dsp);
break;
default:
ASSERT(B_FALSE);
break;
}
}
/*
* Put a chain of packets back on the queue.
*/
static void
str_putbq(queue_t *q, mblk_t *mp)
{
mblk_t *bp = NULL;
mblk_t *nextp;
/*
* Reverse the order of the chain.
*/
while (mp != NULL) {
nextp = mp->b_next;
mp->b_next = bp;
bp = mp;
mp = nextp;
}
/*
* Walk the reversed chain and put each message back on the
* queue.
*/
while (bp != NULL) {
nextp = bp->b_next;
bp->b_next = NULL;
(void) putbq(q, bp);
bp = nextp;
}
}