#include <sys/types.h>
#include <sys/errno.h>
#include <sys/param.h>
#include <sys/mkdev.h>
#include <sys/sysmacros.h>
#include <sys/systm.h>
#include <sys/stropts.h>
#include <sys/stream.h>
#include <sys/kmem.h>
#include <sys/conf.h>
#include <sys/ddi.h>
#include <sys/devops.h>
#include <sys/sunddi.h>
#include <sys/ksynch.h>
#include <sys/dlpi.h>
#include <sys/ethernet.h>
#include <sys/strsun.h>
#include <sys/stat.h>
#include <netinet/in.h>
#include <sys/llc1.h>
#include <sys/kstat.h>
#include <sys/debug.h>
static int llc1_open(queue_t *q, dev_t *dev, int flag, int sflag,
cred_t *cred);
static int llc1_close(queue_t *q, int flag, cred_t *cred);
static int llc1_uwput(queue_t *q, mblk_t *mp);
static int llc1_uwsrv(queue_t *q);
static int llc1_lrsrv(queue_t *q);
static int llc1_getinfo(dev_info_t *, ddi_info_cmd_t, void *, void **);
static int llc1_detach(dev_info_t *dev, ddi_detach_cmd_t cmd);
static int llc1_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd);
static mblk_t *llc1_form_udata(llc1_t *lld, llc_mac_info_t *macinfo,
mblk_t *mp);
static mblk_t *llc1_xid_reply(llc_mac_info_t *macinfo, mblk_t *mp, int sap);
static mblk_t *llc1_xid_ind_con(llc1_t *lld, llc_mac_info_t *macinfo,
mblk_t *mp);
static mblk_t *llc1_test_reply(llc_mac_info_t *macinfo, mblk_t *mp, int sap);
static mblk_t *llc1_test_ind_con(llc1_t *lld, llc_mac_info_t *macinfo,
mblk_t *mp);
static void llc1_ioctl(queue_t *q, mblk_t *mp);
static void llc1_recv(llc_mac_info_t *macinfo, mblk_t *mp);
static void llc1_req_raw(llc_mac_info_t *macinfo);
static void llc1_find_waiting(llc_mac_info_t *macinfo, mblk_t *mp, long prim);
static minor_t llc1_findminor(llc1dev_t *device);
static void llc1_send_disable_multi(llc_mac_info_t *, llc_mcast_t *);
static void llc1insque(void *elem, void *pred);
static void llc1remque(void *arg);
static void llc1error();
static int llc1_subs_unbind(void);
static void llc1_init_kstat(llc_mac_info_t *macinfo);
static void llc1_uninit_kstat(llc_mac_info_t *macinfo);
static int llc1_update_kstat(kstat_t *ksp, int rw);
static int llc1_broadcast(struct ether_addr *addr, llc_mac_info_t *macinfo);
static int llc1_unbind(queue_t *q, mblk_t *mp);
static int llc1_subs_bind(queue_t *q, mblk_t *mp);
static int llc1_unitdata(queue_t *q, mblk_t *mp);
static int llc1_inforeq(queue_t *q, mblk_t *mp);
static int llc1attach(queue_t *q, mblk_t *mp);
static void llc1_send_bindreq(llc_mac_info_t *macinfo);
static int llc1_req_info(queue_t *q);
static int llc1_cmds(queue_t *q, mblk_t *mp);
static int llc1_setppa(struct ll_snioc *snioc);
static int llc1_getppa(llc_mac_info_t *macinfo, struct ll_snioc *snioc);
static int llc1_bind(queue_t *q, mblk_t *mp);
static int llc1unattach(queue_t *q, mblk_t *mp);
static int llc1_enable_multi(queue_t *q, mblk_t *mp);
static int llc1_disable_multi(queue_t *q, mblk_t *mp);
static int llc1_xid_req_res(queue_t *q, mblk_t *mp, int req_or_res);
static int llc1_test_req_res(queue_t *q, mblk_t *mp, int req_or_res);
static int llc1_local(struct ether_addr *addr, llc_mac_info_t *macinfo);
static int llc1_snap_match(llc1_t *lld, struct snaphdr *snap);
static struct module_info llc1_minfo = {
LLC1IDNUM,
"llc1",
0,
LLC1_DEFMAX,
LLC1_HIWATER,
LLC1_LOWATER,
};
static struct qinit llc1_rint = {
NULL,
NULL,
llc1_open,
llc1_close,
NULL,
&llc1_minfo,
NULL
};
static struct qinit llc1_wint = {
llc1_uwput,
llc1_uwsrv,
NULL,
NULL,
NULL,
&llc1_minfo,
NULL
};
static struct qinit llc1_muxrint = {
putq,
llc1_lrsrv,
NULL,
NULL,
NULL,
&llc1_minfo,
NULL
};
static struct qinit llc1_muxwint = {
NULL,
NULL,
NULL,
NULL,
NULL,
&llc1_minfo,
NULL
};
struct streamtab llc1_info = {
&llc1_rint,
&llc1_wint,
&llc1_muxrint,
&llc1_muxwint
};
#if !defined(BUILD_STATIC)
#include <sys/modctl.h>
DDI_DEFINE_STREAM_OPS(llc1_ops, nulldev, nulldev, llc1_attach,
llc1_detach, nodev, llc1_getinfo, D_MP | D_MTPERMOD, &llc1_info,
ddi_quiesce_not_supported);
static struct modldrv modldrv = {
&mod_driverops,
"LLC Class 1 Driver",
&llc1_ops,
};
static struct modlinkage modlinkage = {
MODREV_1, (void *)&modldrv, NULL
};
int
_init(void)
{
return (mod_install(&modlinkage));
}
int
_fini(void)
{
return (mod_remove(&modlinkage));
}
int
_info(struct modinfo *modinfop)
{
return (mod_info(&modlinkage, modinfop));
}
#endif
#ifdef LLC1_DEBUG
extern int llc1_debug = 0x0;
#endif
#define GETSTRUCT(structure, number) \
(kmem_zalloc(sizeof (structure) * (number), KM_NOSLEEP))
#define GETBUF(structure, size) \
(kmem_zalloc(size, KM_NOSLEEP))
static struct llc1device llc1_device_list;
static int
llc1_attach(dev_info_t *devinfo, ddi_attach_cmd_t cmd)
{
if (cmd != DDI_ATTACH)
return (DDI_FAILURE);
if (!(llc1_device_list.llc1_status & LLC1_ATTACHED)) {
llc1_device_list.llc1_status |= LLC1_ATTACHED;
rw_init(&llc1_device_list.llc1_rwlock, NULL, RW_DRIVER, NULL);
llc1_device_list.llc1_str_next =
llc1_device_list.llc1_str_prev =
(llc1_t *)&llc1_device_list.llc1_str_next;
llc1_device_list.llc1_mac_next =
llc1_device_list.llc1_mac_prev =
(llc_mac_info_t *)&llc1_device_list.llc1_mac_next;
}
ddi_set_driver_private(devinfo, &llc1_device_list);
if (ddi_create_minor_node(devinfo, "llc1", S_IFCHR,
0, DDI_PSEUDO, CLONE_DEV) == DDI_FAILURE) {
llc1error(devinfo, "ddi_create_minor_node failed");
ddi_remove_minor_node(devinfo, NULL);
return (DDI_FAILURE);
}
llc1_device_list.llc1_multisize = ddi_getprop(DDI_DEV_T_NONE,
devinfo, 0, "multisize", 0);
if (llc1_device_list.llc1_multisize == 0)
llc1_device_list.llc1_multisize = LLC1_MAX_MULTICAST;
ddi_report_dev(devinfo);
return (DDI_SUCCESS);
}
static int
llc1_detach(dev_info_t *dev, ddi_detach_cmd_t cmd)
{
if (cmd != DDI_DETACH) {
return (DDI_FAILURE);
}
if (llc1_device_list.llc1_ndevice > 0)
return (DDI_FAILURE);
rw_destroy(&llc1_device_list.llc1_rwlock);
llc1_device_list.llc1_status = 0;
ddi_remove_minor_node(dev, NULL);
return (DDI_SUCCESS);
}
static int
llc1_getinfo(dev_info_t *dev, ddi_info_cmd_t cmd, void *arg, void **result)
{
int error;
switch (cmd) {
case DDI_INFO_DEVT2DEVINFO:
if (dev == NULL) {
error = DDI_FAILURE;
} else {
*result = (void *)dev;
error = DDI_SUCCESS;
}
break;
case DDI_INFO_DEVT2INSTANCE:
*result = (void *)0;
error = DDI_SUCCESS;
break;
default:
error = DDI_FAILURE;
}
return (error);
}
static int
llc1_open(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *cred)
{
llc1_t *llc1;
minor_t minordev;
int status = 0;
ASSERT(q);
if (q->q_ptr)
return (0);
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
if (sflag == CLONEOPEN) {
minordev = llc1_findminor(&llc1_device_list);
if (minordev == 0) {
rw_exit(&llc1_device_list.llc1_rwlock);
return (ENXIO);
}
*dev = makedevice(getmajor(*dev), minordev);
} else {
minordev = getminor (*dev);
if ((minordev > MAXMIN32) || (minordev == 0)) {
rw_exit(&llc1_device_list.llc1_rwlock);
return (ENXIO);
}
}
llc1 = kmem_zalloc(sizeof (llc1_t), KM_SLEEP);
llc1->llc_qptr = q;
WR(q)->q_ptr = q->q_ptr = (caddr_t)llc1;
llc1->llc_state = DL_UNATTACHED;
llc1->llc_style = DL_STYLE2;
llc1->llc_minor = minordev;
mutex_init(&llc1->llc_lock, NULL, MUTEX_DRIVER, NULL);
llc1insque(llc1, llc1_device_list.llc1_str_prev);
rw_exit(&llc1_device_list.llc1_rwlock);
qprocson(q);
return (status);
}
static int
llc1_close(queue_t *q, int flag, cred_t *cred)
{
llc1_t *llc1;
ASSERT(q);
ASSERT(q->q_ptr);
qprocsoff(q);
llc1 = (llc1_t *)q->q_ptr;
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
q->q_ptr = WR(q)->q_ptr = NULL;
(void) llc1remque(llc1);
rw_exit(&llc1_device_list.llc1_rwlock);
mutex_enter(&llc1->llc_lock);
if (llc1->llc_state == DL_IDLE || llc1->llc_state == DL_UNBOUND) {
llc1->llc_state = DL_UNBOUND;
}
if (llc1->llc_mcast != NULL) {
int i;
for (i = 0; i < llc1_device_list.llc1_multisize; i++) {
llc_mcast_t *mcast;
if ((mcast = llc1->llc_mcast[i]) != NULL) {
if (llc1->llc_mac_info &&
llc1->llc_mac_info->llcp_flags &
LLC1_AVAILABLE)
llc1_send_disable_multi(
llc1->llc_mac_info,
mcast);
llc1->llc_mcast[i] = NULL;
}
}
kmem_free(llc1->llc_mcast,
sizeof (llc_mcast_t *) * llc1->llc_multicnt);
llc1->llc_mcast = NULL;
}
llc1->llc_state = DL_UNATTACHED;
mutex_exit(&llc1->llc_lock);
mutex_destroy(&llc1->llc_lock);
kmem_free(llc1, sizeof (llc1_t));
return (0);
}
static int
llc1_uwput(queue_t *q, mblk_t *mp)
{
llc1_t *ld = (llc1_t *)(q->q_ptr);
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_wput(%x %x): type %d\n", q, mp, DB_TYPE(mp));
#endif
switch (DB_TYPE(mp)) {
case M_IOCTL:
(void) llc1_ioctl(q, mp);
break;
case M_FLUSH:
if (*mp->b_rptr & FLUSHW)
flushq(q, 0);
if (*mp->b_rptr & FLUSHR) {
flushq(RD(q), 0);
*mp->b_rptr &= ~FLUSHW;
qreply(q, mp);
} else
freemsg(mp);
break;
case M_PROTO:
case M_PCPROTO:
(void) putq(q, mp);
break;
case M_DATA:
if ((ld->llc_flags & (LLC_RAW | LLC_FAST)) == 0 ||
ld->llc_state != DL_IDLE) {
(void) merror(q, mp, EPROTO);
break;
}
(void) putq(q, mp);
break;
default:
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1: Unexpected packet type from queue: %d\n",
mp->b_datap->db_type);
#endif
freemsg(mp);
}
return (0);
}
static int
llc1_lrsrv(queue_t *q)
{
mblk_t *mp;
union DL_primitives *prim;
llc_mac_info_t *macinfo = (llc_mac_info_t *)q->q_ptr;
struct iocblk *iocp;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_rsrv(%x)\n", q);
if (llc1_debug & LLCRECV) {
printf("llc1_lrsrv: q=%x macinfo=%x", q, macinfo);
if (macinfo == NULL) {
printf("NULL macinfo");
panic("null macinfo in lrsrv");
}
printf("\n");
}
#endif
while ((mp = getq(q)) != NULL) {
switch (DB_TYPE(mp)) {
case M_PROTO:
case M_PCPROTO:
prim = (union DL_primitives *)mp->b_rptr;
switch (prim->dl_primitive) {
case DL_INFO_ACK:
if (macinfo->llcp_flags & LLC1_LINKED) {
macinfo->llcp_flags &= ~LLC1_LINKED;
macinfo->llcp_flags |= LLC1_AVAILABLE;
macinfo->llcp_maxpkt =
prim->info_ack.dl_max_sdu;
macinfo->llcp_minpkt =
prim->info_ack.dl_min_sdu;
macinfo->llcp_type =
prim->info_ack.dl_mac_type;
if (macinfo->llcp_type == DL_ETHER) {
macinfo->llcp_type = DL_CSMACD;
macinfo->llcp_maxpkt -= 8;
}
macinfo->llcp_addrlen =
prim->info_ack.dl_addr_length -
ABS(prim->info_ack.dl_sap_length);
bcopy(mp->b_rptr +
prim->info_ack.dl_addr_offset,
macinfo->llcp_macaddr,
macinfo->llcp_addrlen);
bcopy(mp->b_rptr +
prim->info_ack.
dl_brdcst_addr_offset,
macinfo->llcp_broadcast,
prim->info_ack.
dl_brdcst_addr_length);
if (prim->info_ack.dl_current_state ==
DL_UNBOUND)
llc1_send_bindreq(macinfo);
freemsg(mp);
switch (macinfo->llcp_type) {
case DL_ETHER:
case DL_CSMACD:
llc1_req_raw(macinfo);
break;
default:
break;
}
} else {
if (prim->info_ack.dl_current_state ==
DL_IDLE)
bcopy(mp->b_rptr +
prim->info_ack.dl_addr_offset,
macinfo->llcp_macaddr,
macinfo->llcp_addrlen);
freemsg(mp);
}
break;
case DL_BIND_ACK:
freemsg(mp);
(void) llc1_req_info(q);
break;
case DL_UNITDATA_IND:
(void) llc1_recv(macinfo, mp);
break;
case DL_ERROR_ACK:
if (prim->error_ack.dl_error_primitive ==
DL_BIND_REQ) {
freemsg(mp);
if (macinfo->llcp_flags & LLC1_BINDING)
llc1_send_bindreq(macinfo);
} else
llc1_find_waiting(macinfo, mp,
prim->error_ack.dl_error_primitive);
break;
case DL_PHYS_ADDR_ACK:
llc1_find_waiting(macinfo, mp,
DL_PHYS_ADDR_REQ);
break;
case DL_OK_ACK:
if (prim->ok_ack.dl_correct_primitive ==
DL_BIND_REQ)
macinfo->llcp_flags &= ~LLC1_BINDING;
default:
freemsg(mp);
}
break;
case M_IOCACK:
iocp = (struct iocblk *)mp->b_rptr;
if ((macinfo->llcp_flags & LLC1_RAW_WAIT) &&
macinfo->llcp_iocid == iocp->ioc_id) {
macinfo->llcp_flags &= ~LLC1_RAW_WAIT;
macinfo->llcp_flags |= LLC1_USING_RAW;
freemsg(mp);
break;
}
freemsg(mp);
break;
case M_IOCNAK:
iocp = (struct iocblk *)mp->b_rptr;
if ((macinfo->llcp_flags & LLC1_RAW_WAIT) &&
macinfo->llcp_iocid == iocp->ioc_id) {
macinfo->llcp_flags &= ~LLC1_RAW_WAIT;
freemsg(mp);
break;
}
freemsg(mp);
break;
case M_DATA:
llc1_recv(macinfo, mp);
break;
}
}
return (0);
}
static int
llc1_uwsrv(queue_t *q)
{
mblk_t *mp;
llc1_t *lld = (llc1_t *)q->q_ptr;
union DL_primitives *prim;
int err;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_wsrv(%x)\n", q);
#endif
while ((mp = getq(q)) != NULL) {
switch (mp->b_datap->db_type) {
case M_PROTO:
case M_PCPROTO:
if ((err = llc1_cmds(q, mp)) != LLCE_OK) {
prim = (union DL_primitives *)mp->b_rptr;
if (err == LLCE_NOBUFFER || err == DL_SYSERR) {
lld->llc_stats->llcs_nobuffer++;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf(
"llc1_cmds: nonfatal err=%d\n",
err);
#endif
(void) putbq(q, mp);
return (0);
} else {
dlerrorack(q, mp,
prim->dl_primitive,
err, 0);
}
}
break;
case M_DATA:
mutex_enter(&lld->llc_lock);
putnext(lld->llc_mac_info->llcp_queue, mp);
mutex_exit(&lld->llc_lock);
freemsg(mp);
break;
default:
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1_wsrv: type(%x) not supported\n",
mp->b_datap->db_type);
#endif
freemsg(mp);
break;
}
}
return (0);
}
int
llc1_multicast(struct ether_addr *addr, llc1_t *lld)
{
int i;
if (lld->llc_mcast)
for (i = 0; i < lld->llc_multicnt; i++)
if (lld->llc_mcast[i] &&
lld->llc_mcast[i]->llcm_refcnt &&
bcmp(lld->llc_mcast[i]->llcm_addr,
addr->ether_addr_octet, ETHERADDRL) == 0)
return (1);
return (0);
}
int llc1_doreqinfo;
static void
llc1_ioctl(queue_t *q, mblk_t *mp)
{
struct iocblk *iocp;
llc1_t *lld;
struct linkblk *link;
llc_mac_info_t *macinfo;
mblk_t *tmp;
int error;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_ioctl(%x %x)\n", q, mp);
#endif
lld = (llc1_t *)q->q_ptr;
iocp = (struct iocblk *)mp->b_rptr;
switch (iocp->ioc_cmd) {
case I_PLINK:
case I_LINK:
link = (struct linkblk *)mp->b_cont->b_rptr;
tmp = allocb(sizeof (llc_mac_info_t), BPRI_MED);
if (tmp == NULL) {
(void) miocnak(q, mp, 0, ENOSR);
return;
}
bzero(tmp->b_rptr, sizeof (llc_mac_info_t));
macinfo = (llc_mac_info_t *)tmp->b_rptr;
macinfo->llcp_mb = tmp;
macinfo->llcp_next = macinfo->llcp_prev = macinfo;
macinfo->llcp_queue = link->l_qbot;
macinfo->llcp_lindex = link->l_index;
macinfo->llcp_ppa = --llc1_device_list.llc1_nextppa;
llc1_device_list.llc1_ndevice++;
macinfo->llcp_flags |= LLC1_LINKED | LLC1_DEF_PPA;
macinfo->llcp_lqtop = q;
macinfo->llcp_data = NULL;
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
llc1insque(macinfo, llc1_device_list.llc1_mac_prev);
macinfo->llcp_queue->q_ptr = RD(macinfo->llcp_queue)->q_ptr =
(caddr_t)macinfo;
llc1_init_kstat(macinfo);
rw_exit(&llc1_device_list.llc1_rwlock);
(void) llc1_req_info(macinfo->llcp_queue);
miocack(q, mp, 0, 0);
return;
case I_PUNLINK:
case I_UNLINK:
link = (struct linkblk *)mp->b_cont->b_rptr;
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
for (macinfo = llc1_device_list.llc1_mac_next;
macinfo != NULL &&
macinfo !=
(llc_mac_info_t *)&llc1_device_list.llc1_mac_next;
macinfo = macinfo->llcp_next) {
if (macinfo->llcp_lindex == link->l_index &&
macinfo->llcp_queue == link->l_qbot) {
ASSERT(macinfo->llcp_next);
llc1_device_list.llc1_ndevice--;
llc1remque(macinfo);
if (macinfo->llcp_mcast != NULL) {
kmem_free(macinfo->llcp_mcast,
sizeof (llc_mcast_t) *
llc1_device_list.llc1_multisize);
macinfo->llcp_mcast = NULL;
}
if (macinfo->llcp_kstatp != NULL)
llc1_uninit_kstat(macinfo);
if (macinfo->llcp_mb != NULL)
freeb(macinfo->llcp_mb);
lld->llc_mac_info = NULL;
miocack(q, mp, 0, 0);
if (llc1_device_list.llc1_ndevice == 0)
llc1_device_list.llc1_nextppa = 0;
rw_exit(&llc1_device_list.llc1_rwlock);
return;
}
}
rw_exit(&llc1_device_list.llc1_rwlock);
miocnak(q, mp, 0, EINVAL);
return;
case L_SETPPA:
error = miocpullup(mp, sizeof (struct ll_snioc));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
if (llc1_setppa((struct ll_snioc *)mp->b_cont->b_rptr) >= 0) {
miocack(q, mp, 0, 0);
return;
}
miocnak(q, mp, 0, EINVAL);
return;
case L_GETPPA:
if (mp->b_cont == NULL) {
mp->b_cont = allocb(sizeof (struct ll_snioc), BPRI_MED);
if (mp->b_cont == NULL) {
miocnak(q, mp, 0, ENOSR);
return;
}
mp->b_cont->b_wptr =
mp->b_cont->b_rptr + sizeof (struct ll_snioc);
} else {
error = miocpullup(mp, sizeof (struct ll_snioc));
if (error != 0) {
miocnak(q, mp, 0, error);
return;
}
}
lld = (llc1_t *)q->q_ptr;
if (llc1_getppa(lld->llc_mac_info,
(struct ll_snioc *)mp->b_cont->b_rptr) >= 0)
miocack(q, mp, 0, 0);
else
miocnak(q, mp, 0, EINVAL);
return;
default:
miocnak(q, mp, 0, EINVAL);
}
}
static int
llc1_setppa(struct ll_snioc *snioc)
{
llc_mac_info_t *macinfo;
for (macinfo = llc1_device_list.llc1_mac_next;
macinfo != (llc_mac_info_t *)&llc1_device_list.llc1_mac_next;
macinfo = macinfo->llcp_next)
if (macinfo->llcp_lindex == snioc->lli_index &&
(macinfo->llcp_flags & LLC1_DEF_PPA)) {
macinfo->llcp_flags &= ~LLC1_DEF_PPA;
macinfo->llcp_ppa = snioc->lli_ppa;
return (0);
}
return (-1);
}
static int
llc1_getppa(llc_mac_info_t *macinfo, struct ll_snioc *snioc)
{
if (macinfo == NULL)
return (-1);
snioc->lli_ppa = macinfo->llcp_ppa;
snioc->lli_index = macinfo->llcp_lindex;
return (0);
}
static int
llc1_cmds(queue_t *q, mblk_t *mp)
{
union DL_primitives *dlp;
llc1_t *llc = (llc1_t *)q->q_ptr;
int result = 0;
llc_mac_info_t *macinfo = llc->llc_mac_info;
dlp = (union DL_primitives *)mp->b_rptr;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_cmds(%x, %x):dlp=%x, dlp->dl_primitive=%d\n",
q, mp, dlp, dlp->dl_primitive);
#endif
mutex_enter(&llc->llc_lock);
rw_enter(&llc1_device_list.llc1_rwlock, RW_READER);
switch (dlp->dl_primitive) {
case DL_BIND_REQ:
result = llc1_bind(q, mp);
break;
case DL_UNBIND_REQ:
result = llc1_unbind(q, mp);
break;
case DL_SUBS_BIND_REQ:
result = llc1_subs_bind(q, mp);
break;
case DL_SUBS_UNBIND_REQ:
result = llc1_subs_unbind();
break;
case DL_UNITDATA_REQ:
result = llc1_unitdata(q, mp);
break;
case DL_INFO_REQ:
result = llc1_inforeq(q, mp);
break;
case DL_ATTACH_REQ:
result = llc1attach(q, mp);
break;
case DL_DETACH_REQ:
result = llc1unattach(q, mp);
break;
case DL_ENABMULTI_REQ:
result = llc1_enable_multi(q, mp);
break;
case DL_DISABMULTI_REQ:
result = llc1_disable_multi(q, mp);
break;
case DL_XID_REQ:
result = llc1_xid_req_res(q, mp, 0);
break;
case DL_XID_RES:
result = llc1_xid_req_res(q, mp, 1);
break;
case DL_TEST_REQ:
result = llc1_test_req_res(q, mp, 0);
break;
case DL_TEST_RES:
result = llc1_test_req_res(q, mp, 1);
break;
case DL_SET_PHYS_ADDR_REQ:
result = DL_NOTSUPPORTED;
break;
case DL_PHYS_ADDR_REQ:
if (llc->llc_state != DL_UNATTACHED && macinfo) {
llc->llc_waiting_for = dlp->dl_primitive;
putnext(WR(macinfo->llcp_queue), mp);
result = LLCE_OK;
} else {
result = DL_OUTSTATE;
}
break;
case DL_PROMISCON_REQ:
case DL_PROMISCOFF_REQ:
result = DL_NOTSUPPORTED;
break;
default:
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1_cmds: Received unknown primitive: %d\n",
dlp->dl_primitive);
#endif
result = DL_BADPRIM;
break;
}
rw_exit(&llc1_device_list.llc1_rwlock);
mutex_exit(&llc->llc_lock);
return (result);
}
static int
llc1_bind(queue_t *q, mblk_t *mp)
{
int sap;
dl_bind_req_t *dlp;
llc1_t *lld = (llc1_t *)q->q_ptr;
ASSERT(lld);
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_bind(%x %x)\n", q, mp);
#endif
dlp = (dl_bind_req_t *)mp->b_rptr;
sap = dlp->dl_sap;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCPROT)
printf("llc1_bind: lsap=%x\n", sap);
#endif
if (lld->llc_mac_info == NULL)
return (DL_OUTSTATE);
if (lld->llc_qptr && lld->llc_state != DL_UNBOUND) {
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1_bind: stream bound/not attached (%d)\n",
lld->llc_state);
#endif
return (DL_OUTSTATE);
}
if (dlp->dl_service_mode != DL_CLDLS || dlp->dl_max_conind != 0) {
return (DL_UNSUPPORTED);
}
if (sap == 0 || (sap <= 0xFF && (sap & 1 && sap != 0xFF)) ||
sap > 0xFFFF) {
return (DL_BADSAP);
}
lld->llc_state = DL_BIND_PENDING;
if (sap == 0xFF) {
if (lld->llc_mac_info->llcp_type == DL_CSMACD)
sap = LLC_NOVELL_SAP;
else
return (DL_BADSAP);
}
lld->llc_sap = sap;
if (sap > 0xFF) {
ushort_t snapsap = htons(sap);
lld->llc_snap[3] = ((uchar_t *)&snapsap)[0];
lld->llc_snap[4] = ((uchar_t *)&snapsap)[1];
lld->llc_flags |= LLC_SNAP;
lld->llc_sap = LLC_SNAP_SAP;
}
#ifdef LLC1_DEBUG
if (llc1_debug & LLCPROT)
printf("llc1_bind: ok - type = %d\n", lld->llc_type);
#endif
if (dlp->dl_xidtest_flg & DL_AUTO_XID)
lld->llc_flags |= LLC1_AUTO_XID;
if (dlp->dl_xidtest_flg & DL_AUTO_TEST)
lld->llc_flags |= LLC1_AUTO_TEST;
dlbindack(q, mp, sap, lld->llc_mac_info->llcp_macaddr, 6, 0, 0);
lld->llc_state = DL_IDLE;
return (LLCE_OK);
}
static int
llc1_unbind(queue_t *q, mblk_t *mp)
{
llc1_t *lld;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_unbind(%x %x)\n", q, mp);
#endif
lld = (llc1_t *)q->q_ptr;
if (lld->llc_mac_info == NULL)
return (DL_OUTSTATE);
if (lld->llc_state != DL_IDLE) {
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1_unbind: wrong state (%d)\n",
lld->llc_state);
#endif
return (DL_OUTSTATE);
}
lld->llc_state = DL_UNBIND_PENDING;
lld->llc_flags &= ~(LLC_SNAP|LLC_SNAP_OID);
dlokack(q, mp, DL_UNBIND_REQ);
lld->llc_state = DL_UNBOUND;
return (LLCE_OK);
}
static int
llc1_inforeq(queue_t *q, mblk_t *mp)
{
llc1_t *lld;
mblk_t *nmp;
dl_info_ack_t *dlp;
int bufsize;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_inforeq(%x %x)\n", q, mp);
#endif
lld = (llc1_t *)q->q_ptr;
ASSERT(lld);
if (lld->llc_mac_info == NULL)
bufsize = sizeof (dl_info_ack_t) + ETHERADDRL;
else
bufsize = sizeof (dl_info_ack_t) +
2 * lld->llc_mac_info->llcp_addrlen + 2;
nmp = mexchange(q, mp, bufsize, M_PCPROTO, DL_INFO_ACK);
if (nmp) {
nmp->b_wptr = nmp->b_rptr + sizeof (dl_info_ack_t);
dlp = (dl_info_ack_t *)nmp->b_rptr;
bzero(dlp, DL_INFO_ACK_SIZE);
dlp->dl_primitive = DL_INFO_ACK;
if (lld->llc_mac_info)
dlp->dl_max_sdu = lld->llc_mac_info->llcp_maxpkt;
dlp->dl_min_sdu = 0;
dlp->dl_mac_type = lld->llc_type;
dlp->dl_service_mode = DL_CLDLS;
dlp->dl_current_state = lld->llc_state;
dlp->dl_provider_style =
(lld->llc_style == 0) ? lld->llc_style : DL_STYLE2;
if (lld->llc_mac_info) {
dlp->dl_addr_length = lld->llc_mac_info->llcp_addrlen;
dlp->dl_addr_offset = DL_INFO_ACK_SIZE;
nmp->b_wptr += dlp->dl_addr_length + 1;
bcopy(lld->llc_mac_info->llcp_macaddr,
((caddr_t)dlp) + dlp->dl_addr_offset,
lld->llc_mac_info->llcp_addrlen);
if (lld->llc_state == DL_IDLE) {
dlp->dl_sap_length = -1;
*(((caddr_t)dlp) + dlp->dl_addr_offset +
dlp->dl_addr_length) = lld->llc_sap;
dlp->dl_addr_length += 1;
}
dlp->dl_brdcst_addr_length =
lld->llc_mac_info->llcp_addrlen;
dlp->dl_brdcst_addr_offset =
dlp->dl_addr_offset + dlp->dl_addr_length;
nmp->b_wptr += dlp->dl_brdcst_addr_length;
bcopy(lld->llc_mac_info->llcp_broadcast,
((caddr_t)dlp) + dlp->dl_brdcst_addr_offset,
lld->llc_mac_info->llcp_addrlen);
} else {
dlp->dl_addr_length = 0;
dlp->dl_addr_offset = 0;
dlp->dl_sap_length = 0;
}
dlp->dl_version = DL_VERSION_2;
qreply(q, nmp);
}
return (LLCE_OK);
}
static int
llc1_unitdata(queue_t *q, mblk_t *mp)
{
llc1_t *lld = (llc1_t *)q->q_ptr;
dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)mp->b_rptr;
struct ether_header *hdr;
struct llcaddr *llcp;
mblk_t *nmp;
long msglen;
struct llchdr *llchdr;
llc_mac_info_t *macinfo;
int xmt_type = 0;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_unitdata(%x %x)\n", q, mp);
#endif
if ((macinfo = lld->llc_mac_info) == NULL)
return (DL_OUTSTATE);
if (lld->llc_state != DL_IDLE) {
#ifdef LLC1_DEBUG
if (llc1_debug & LLCERRS)
printf("llc1_unitdata: wrong state (%d)\n",
lld->llc_state);
#endif
return (DL_OUTSTATE);
}
llcp = (struct llcaddr *)((caddr_t)dlp + dlp->dl_dest_addr_offset);
if (macinfo->llcp_flags & LLC1_USING_RAW) {
nmp = allocb(macinfo->llcp_addrlen * 2 + 2 + 8, BPRI_MED);
hdr = (struct ether_header *)nmp->b_rptr;
msglen = msgdsize(mp);
switch (lld->llc_type) {
case DL_CSMACD:
nmp->b_wptr = nmp->b_rptr + LLC1_CSMACD_HDR_SIZE;
llchdr = (struct llchdr *)nmp->b_wptr;
bcopy(llcp->llca_addr,
hdr->ether_dhost.ether_addr_octet,
ETHERADDRL);
bcopy(macinfo->llcp_macaddr,
hdr->ether_shost.ether_addr_octet,
ETHERADDRL);
if (lld->llc_sap != LLC_NOVELL_SAP) {
hdr->ether_type = ntohs(msglen +
sizeof (struct llchdr));
llchdr->llc_ssap = lld->llc_sap;
llchdr->llc_dsap = llcp->llca_sap;
llchdr->llc_ctl = LLC_UI;
nmp->b_wptr += sizeof (struct llchdr);
if (lld->llc_flags & LLC_SNAP) {
bcopy(lld->llc_snap, nmp->b_wptr, 5);
llchdr->llc_dsap = LLC_SNAP_SAP;
nmp->b_wptr += 5;
}
} else {
hdr->ether_type = ntohs(msglen);
}
if (ismulticast(hdr->ether_dhost.ether_addr_octet)) {
if (bcmp(hdr->ether_dhost.ether_addr_octet,
macinfo->llcp_broadcast, ETHERADDRL) == 0)
xmt_type = 2;
else
xmt_type = 1;
}
break;
default:
break;
}
DB_TYPE(nmp) = M_DATA;
nmp->b_cont = mp->b_cont;
freeb(mp);
mp = nmp;
} else {
nmp = allocb(sizeof (struct llchdr)+sizeof (struct snaphdr),
BPRI_MED);
if (nmp == NULL)
return (DL_UNDELIVERABLE);
llchdr = (struct llchdr *)(nmp->b_rptr);
nmp->b_wptr += sizeof (struct llchdr);
llchdr->llc_dsap = llcp->llca_sap;
llchdr->llc_ssap = lld->llc_sap;
llchdr->llc_ctl = LLC_UI;
if (lld->llc_flags & LLC_SNAP) {
bcopy(lld->llc_snap, nmp->b_wptr, 5);
nmp->b_wptr += 5;
}
nmp->b_cont = mp->b_cont;
mp->b_cont = nmp;
nmp = mp;
if (ismulticast(llcp->llca_addr)) {
if (bcmp(llcp->llca_addr,
macinfo->llcp_broadcast, ETHERADDRL) == 0)
xmt_type = 2;
else
xmt_type = 1;
}
}
if (canput(macinfo->llcp_queue)) {
lld->llc_stats->llcs_bytexmt += msgdsize(mp);
lld->llc_stats->llcs_pktxmt++;
switch (xmt_type) {
case 1:
macinfo->llcp_stats.llcs_multixmt++;
break;
case 2:
macinfo->llcp_stats.llcs_brdcstxmt++;
break;
}
putnext(macinfo->llcp_queue, mp);
return (LLCE_OK);
} else {
lld->llc_stats->llcs_nobuffer++;
}
if (nmp != NULL)
freemsg(nmp);
return (LLCE_OK);
}
static void
llc1_recv(llc_mac_info_t *macinfo, mblk_t *mp)
{
struct ether_addr *addr;
llc1_t *lld;
mblk_t *nmp, *udmp;
int i, nmcast = 0, statcnt_normal = 0, statcnt_brdcst = 0;
int valid, msgsap;
struct llchdr *llchdr;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCTRACE)
printf("llc1_recv(%x, %x)\n", mp, macinfo);
#endif
if (DB_TYPE(mp) == M_PROTO) {
dl_unitdata_ind_t *udata;
nmp = mp;
udata = (dl_unitdata_ind_t *)(nmp->b_rptr);
addr = (struct ether_addr *)(nmp->b_rptr +
udata->dl_dest_addr_offset);
llchdr = (struct llchdr *)(nmp->b_cont->b_rptr);
if (macinfo->llcp_type == DL_CSMACD) {
i = ((struct llcsaddr *)addr)->llca_ssap;
if (i < 60) {
valid = adjmsg(mp->b_cont, i - msgdsize(mp));
}
}
} else {
struct ether_header *hdr;
nmp = NULL;
hdr = (struct ether_header *)mp->b_rptr;
addr = &hdr->ether_dhost;
llchdr = (struct llchdr *)(mp->b_rptr +
sizeof (struct ether_header));
i = (ushort_t)ntohs(hdr->ether_type);
if (i < 60) {
(void) adjmsg(mp, i + sizeof (struct ether_header) -
msgdsize(mp));
}
}
udmp = NULL;
msgsap = llchdr->llc_dsap;
#ifdef LLC1_DEBUG
if (llc1_debug & LLCRECV) {
printf("llc1_recv: machdr=<%s>\n", ether_sprintf(addr));
}
#endif
if (llc1_broadcast(addr, macinfo)) {
valid = 2;
statcnt_brdcst = 1;
} else {
valid = llc1_local(addr, macinfo);
statcnt_normal = msgdsize(mp);
}
if (msgsap == LLC_NULL_SAP) {
if ((llchdr->llc_ctl & ~LLC_P) == LLC_XID)
mp = llc1_xid_reply(macinfo, mp, 0);
else if ((llchdr->llc_ctl & ~LLC_P) == LLC_TEST)
mp = llc1_test_reply(macinfo, mp, 0);
} else
for (lld = llc1_device_list.llc1_str_next;
lld != (llc1_t *)&llc1_device_list.llc1_str_next;
lld = lld->llc_next) {
if (lld->llc_qptr == NULL ||
lld->llc_state != DL_IDLE ||
lld->llc_mac_info != macinfo) {
continue;
}
#ifdef LLC1_DEBUG
if (llc1_debug & LLCRECV)
printf(
"llc1_recv: type=%d, sap=%x, pkt-dsap=%x\n",
lld->llc_type, lld->llc_sap,
msgsap);
#endif
if (!valid && ismulticast(addr->ether_addr_octet) &&
lld->llc_multicnt > 0 &&
llc1_multicast(addr, lld)) {
valid |= 4;
} else if (lld->llc_flags & LLC_PROM)
valid = 1;
if ((lld->llc_flags & LLC_PROM) ||
(valid &&
(lld->llc_sap == msgsap ||
msgsap == LLC_GLOBAL_SAP))) {
if (msgsap == LLC_SNAP_SAP &&
(lld->llc_flags & (LLC_SNAP|LLC_PROM)) ==
LLC_SNAP) {
if (!llc1_snap_match(lld,
(struct snaphdr *)(llchdr+1)))
continue;
}
if (!canputnext(RD(lld->llc_qptr))) {
#ifdef LLC1_DEBUG
if (llc1_debug & LLCRECV)
printf(
"llc1_recv: canput failed\n");
#endif
lld->llc_stats->llcs_blocked++;
continue;
}
if (msgsap == LLC_GLOBAL_SAP &&
lld->llc_sap == LLC_NOVELL_SAP &&
llchdr->llc_ssap == LLC_GLOBAL_SAP) {
nmp = llc1_form_udata(lld, macinfo, mp);
continue;
}
switch (llchdr->llc_ctl) {
case LLC_UI:
nmp = llc1_form_udata(lld, macinfo, mp);
break;
case LLC_XID:
case LLC_XID | LLC_P:
if (lld->llc_flags & LLC1_AUTO_XID) {
nmp = llc1_xid_reply(macinfo,
mp, lld->llc_sap);
} else {
nmp = llc1_xid_ind_con(lld,
macinfo, mp);
}
macinfo->llcp_stats.llcs_xidrcv++;
break;
case LLC_TEST:
case LLC_TEST | LLC_P:
if (lld->llc_flags & LLC1_AUTO_TEST) {
nmp = llc1_test_reply(macinfo,
mp, lld->llc_sap);
} else {
nmp = llc1_test_ind_con(lld,
macinfo, mp);
}
macinfo->llcp_stats.llcs_testrcv++;
break;
default:
nmp = mp;
break;
}
mp = nmp;
}
}
if (mp != NULL)
freemsg(mp);
if (udmp != NULL)
freeb(udmp);
if (nmcast > 0)
macinfo->llcp_stats.llcs_multircv++;
if (statcnt_brdcst) {
macinfo->llcp_stats.llcs_brdcstrcv++;
}
if (statcnt_normal) {
macinfo->llcp_stats.llcs_bytercv += statcnt_normal;
macinfo->llcp_stats.llcs_pktrcv++;
}
}
static int
llc1_local(struct ether_addr *addr, llc_mac_info_t *macinfo)
{
return (bcmp(addr->ether_addr_octet, macinfo->llcp_macaddr,
macinfo->llcp_addrlen) == 0);
}
static int
llc1_broadcast(struct ether_addr *addr, llc_mac_info_t *macinfo)
{
return (bcmp(addr->ether_addr_octet, macinfo->llcp_broadcast,
macinfo->llcp_addrlen) == 0);
}
static int
llc1attach(queue_t *q, mblk_t *mp)
{
dl_attach_req_t *at;
llc_mac_info_t *mac;
llc1_t *llc = (llc1_t *)q->q_ptr;
at = (dl_attach_req_t *)mp->b_rptr;
if (llc->llc_state != DL_UNATTACHED) {
return (DL_OUTSTATE);
}
llc->llc_state = DL_ATTACH_PENDING;
if (rw_tryupgrade(&llc1_device_list.llc1_rwlock) == 0) {
rw_exit(&llc1_device_list.llc1_rwlock);
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
}
for (mac = llc1_device_list.llc1_mac_next;
mac != (llc_mac_info_t *)(&llc1_device_list.llc1_mac_next);
mac = mac->llcp_next) {
ASSERT(mac);
if (mac->llcp_ppa == at->dl_ppa && mac->llcp_lqtop == q) {
if (mac->llcp_flags &
(LLC1_LINKED|LLC1_DEF_PPA)) {
return (DL_INITFAILED);
} else if (!(mac->llcp_flags & LLC1_AVAILABLE)) {
return (DL_BADPPA);
}
mac->llcp_nstreams++;
llc->llc_mac_info = mac;
llc->llc_state = DL_UNBOUND;
llc->llc_stats = &mac->llcp_stats;
dlokack(q, mp, DL_ATTACH_REQ);
return (LLCE_OK);
}
}
llc->llc_state = DL_UNATTACHED;
return (DL_BADPPA);
}
static int
llc1unattach(queue_t *q, mblk_t *mp)
{
llc1_t *llc = (llc1_t *)q->q_ptr;
int state;
int i;
state = llc->llc_state;
if (state != DL_UNBOUND)
return (DL_OUTSTATE);
llc->llc_state = DL_DETACH_PENDING;
if (rw_tryupgrade(&llc1_device_list.llc1_rwlock) == 0) {
rw_exit(&llc1_device_list.llc1_rwlock);
rw_enter(&llc1_device_list.llc1_rwlock, RW_WRITER);
}
if (llc->llc_mcast) {
for (i = 0; i < llc1_device_list.llc1_multisize; i++) {
llc_mcast_t *mcast;
if ((mcast = llc->llc_mcast[i]) != NULL) {
llc1_send_disable_multi(llc->llc_mac_info,
mcast);
llc->llc_mcast[i] = NULL;
}
}
kmem_free(llc->llc_mcast,
sizeof (llc_mcast_t *) * llc->llc_multicnt);
llc->llc_mcast = NULL;
}
if (llc->llc_mac_info)
llc->llc_mac_info->llcp_nstreams--;
llc->llc_sap = 0;
llc->llc_state = DL_UNATTACHED;
if (mp) {
dlokack(q, mp, DL_DETACH_REQ);
}
return (LLCE_OK);
}
static int
llc1_enable_multi(queue_t *q, mblk_t *mp)
{
llc1_t *llc;
llc_mac_info_t *macinfo;
struct ether_addr *maddr;
dl_enabmulti_req_t *multi;
llc_mcast_t *mcast;
int status = DL_BADADDR;
int i;
#if defined(LLC1_DEBUG)
if (llc1_debug & LLCPROT) {
printf("llc1_enable_multi(%x, %x)\n", q, mp);
}
#endif
llc = (llc1_t *)q->q_ptr;
if (llc->llc_state == DL_UNATTACHED)
return (DL_OUTSTATE);
macinfo = llc->llc_mac_info;
multi = (dl_enabmulti_req_t *)mp->b_rptr;
maddr = (struct ether_addr *)(mp->b_rptr + multi->dl_addr_offset);
if (MBLKL(mp) >= sizeof (dl_enabmulti_req_t) &&
MBLKIN(mp, multi->dl_addr_offset, multi->dl_addr_length) &&
multi->dl_addr_length == macinfo->llcp_addrlen &&
ismulticast(maddr->ether_addr_octet)) {
if (llc->llc_mcast == NULL) {
llc->llc_mcast =
GETSTRUCT(llc_mcast_t *,
llc1_device_list.llc1_multisize);
if (llc->llc_mcast == NULL)
return (DL_SYSERR);
llc->llc_multicnt = llc1_device_list.llc1_multisize;
} else {
for (i = 0; i < llc1_device_list.llc1_multisize; i++) {
if (llc->llc_mcast[i] &&
bcmp(llc->llc_mcast[i]->llcm_addr,
maddr->ether_addr_octet, ETHERADDRL)) {
dlokack(q, mp, DL_ENABMULTI_REQ);
return (LLCE_OK);
}
}
}
if (macinfo->llcp_mcast == NULL) {
macinfo->llcp_mcast =
GETSTRUCT(llc_mcast_t,
llc1_device_list.llc1_multisize);
if (macinfo->llcp_mcast == NULL)
return (DL_SYSERR);
}
for (mcast = NULL, i = 0;
i < llc1_device_list.llc1_multisize; i++) {
if (macinfo->llcp_mcast[i].llcm_refcnt &&
bcmp(macinfo->llcp_mcast[i].llcm_addr,
maddr->ether_addr_octet, ETHERADDRL) == 0) {
mcast = &macinfo->llcp_mcast[i];
break;
}
}
if (mcast == NULL) {
mblk_t *nmp;
nmp = dupmsg(mp);
if (nmp) {
nmp->b_cont = NULL;
DB_TYPE(nmp) = M_PROTO;
putnext(WR(macinfo->llcp_queue), nmp);
}
for (mcast = macinfo->llcp_mcast, i = 0;
i < llc1_device_list.llc1_multisize; i++, mcast++) {
if (mcast->llcm_refcnt == 0) {
bcopy(maddr->ether_addr_octet,
mcast->llcm_addr, ETHERADDRL);
break;
}
}
}
if (mcast != NULL) {
for (i = 0; i < llc1_device_list.llc1_multisize; i++) {
if (llc->llc_mcast[i] == NULL) {
llc->llc_mcast[i] = mcast;
mcast->llcm_refcnt++;
dlokack(q, mp, DL_ENABMULTI_REQ);
return (LLCE_OK);
}
}
}
status = DL_TOOMANY;
}
return (status);
}
static int
llc1_disable_multi(queue_t *q, mblk_t *mp)
{
llc1_t *llc;
llc_mac_info_t *macinfo;
struct ether_addr *maddr;
dl_enabmulti_req_t *multi;
int status = DL_BADADDR, i;
llc_mcast_t *mcast;
#if defined(LLC1_DEBUG)
if (llc1_debug & LLCPROT) {
printf("llc1_enable_multi(%x, %x)\n", q, mp);
}
#endif
llc = (llc1_t *)q->q_ptr;
if (llc->llc_state == DL_UNATTACHED)
return (DL_OUTSTATE);
macinfo = llc->llc_mac_info;
multi = (dl_enabmulti_req_t *)mp->b_rptr;
maddr = (struct ether_addr *)(multi + 1);
if (MBLKL(mp) >= sizeof (dl_enabmulti_req_t) &&
MBLKIN(mp, multi->dl_addr_offset, multi->dl_addr_length)) {
if (llc->llc_mcast != NULL) {
for (i = 0; i < llc->llc_multicnt; i++)
if (((mcast = llc->llc_mcast[i]) != NULL) &&
mcast->llcm_refcnt &&
bcmp(mcast->llcm_addr,
maddr->ether_addr_octet, ETHERADDRL) == 0) {
llc1_send_disable_multi(macinfo,
mcast);
llc->llc_mcast[i] = NULL;
dlokack(q, mp, DL_DISABMULTI_REQ);
return (LLCE_OK);
}
status = DL_NOTENAB;
}
}
return (status);
}
static void
llc1_send_disable_multi(llc_mac_info_t *macinfo, llc_mcast_t *mcast)
{
mblk_t *mp;
dl_disabmulti_req_t *dis;
if (mcast == NULL) {
return;
}
if (macinfo == NULL || macinfo->llcp_queue == NULL) {
return;
}
if (--mcast->llcm_refcnt > 0)
return;
mp = allocb(sizeof (dl_disabmulti_req_t) + ETHERADDRL, BPRI_MED);
if (mp) {
dis = (dl_disabmulti_req_t *)mp->b_rptr;
mp->b_wptr =
mp->b_rptr + sizeof (dl_disabmulti_req_t) + ETHERADDRL;
dis->dl_primitive = DL_DISABMULTI_REQ;
dis->dl_addr_offset = sizeof (dl_disabmulti_req_t);
dis->dl_addr_length = ETHERADDRL;
bcopy(mcast->llcm_addr,
(mp->b_rptr + sizeof (dl_disabmulti_req_t)), ETHERADDRL);
DB_TYPE(mp) = M_PROTO;
putnext(WR(macinfo->llcp_queue), mp);
}
}
static minor_t
llc1_findminor(llc1dev_t *device)
{
llc1_t *next;
minor_t minor;
ASSERT(device != NULL);
for (minor = 1; minor <= MAXMIN32; minor++) {
for (next = device->llc1_str_next;
next != NULL && next != (llc1_t *)&device->llc1_str_next;
next = next->llc_next) {
if (minor == next->llc_minor)
goto nextminor;
}
return (minor);
nextminor:
;
}
return (0);
}
static int
llc1_req_info(queue_t *q)
{
dl_info_req_t *info;
mblk_t *mp;
mp = allocb(DL_INFO_REQ_SIZE, BPRI_MED);
if (mp == NULL)
return (-1);
DB_TYPE(mp) = M_PCPROTO;
info = (dl_info_req_t *)mp->b_rptr;
mp->b_wptr = mp->b_rptr + DL_INFO_REQ_SIZE;
info->dl_primitive = DL_INFO_REQ;
putnext(q, mp);
return (0);
}
static void
llc1_req_raw(llc_mac_info_t *macinfo)
{
mblk_t *mp;
mp = mkiocb(DLIOCRAW);
if (mp == NULL)
return;
macinfo->llcp_iocid = ((struct iocblk *)mp->b_rptr)->ioc_id;
putnext(macinfo->llcp_queue, mp);
macinfo->llcp_flags |= LLC1_RAW_WAIT;
}
static void
llc1_send_bindreq(llc_mac_info_t *macinfo)
{
mblk_t *mp;
dl_bind_req_t *bind;
if (macinfo->llcp_sap >= 0xFF) {
macinfo->llcp_sap &= ~(LLC1_BINDING|LLC1_AVAILABLE);
return;
}
mp = allocb(sizeof (dl_bind_req_t), BPRI_MED);
if (mp == NULL)
return;
bind = (dl_bind_req_t *)mp->b_rptr;
mp->b_wptr = mp->b_rptr + sizeof (dl_bind_req_t);
bind->dl_primitive = DL_BIND_REQ;
bind->dl_sap = macinfo->llcp_sap += 2;
macinfo->llcp_flags |= LLC1_BINDING;
bind->dl_max_conind = 0;
bind->dl_service_mode = DL_CLDLS;
bind->dl_conn_mgmt = 0;
bind->dl_xidtest_flg = 0;
putnext(macinfo->llcp_queue, mp);
}
static mblk_t *
llc1_form_udata(llc1_t *lld, llc_mac_info_t *macinfo, mblk_t *mp)
{
mblk_t *udmp, *nmp;
dl_unitdata_ind_t *udata;
struct ether_header *hdr;
struct llchdr *llchdr;
struct snaphdr *snap;
if (macinfo->llcp_flags & LLC1_USING_RAW) {
hdr = (struct ether_header *)mp->b_rptr;
llchdr = (struct llchdr *)(hdr + 1);
udmp = allocb(sizeof (dl_unitdata_ind_t) +
2 * (macinfo->llcp_addrlen + 5), BPRI_MED);
if (udmp == NULL) {
freemsg(mp);
return (NULL);
}
udata = (dl_unitdata_ind_t *)udmp->b_rptr;
udmp->b_wptr += sizeof (dl_unitdata_ind_t);
nmp = dupmsg(mp);
if (lld->llc_sap != LLC_NOVELL_SAP)
mp->b_rptr += sizeof (struct ether_header) +
sizeof (struct llchdr);
else
mp->b_rptr += sizeof (struct ether_header);
if (lld->llc_flags & LLC_SNAP) {
mp->b_rptr += sizeof (struct snaphdr);
snap = (struct snaphdr *)(llchdr + 1);
}
DB_TYPE(udmp) = M_PROTO;
udata->dl_primitive = DL_UNITDATA_IND;
udata->dl_dest_addr_offset = sizeof (dl_unitdata_ind_t);
bcopy(hdr->ether_dhost.ether_addr_octet,
LLCADDR(udata, udata->dl_dest_addr_offset)->llca_addr,
macinfo->llcp_addrlen);
if (lld->llc_flags & LLC_SNAP) {
udata->dl_dest_addr_length = macinfo->llcp_addrlen + 2;
LLCSADDR(udata, udata->dl_dest_addr_offset)->llca_ssap =
ntohs(*(ushort_t *)snap->snap_type);
} else {
udata->dl_dest_addr_length = macinfo->llcp_addrlen + 1;
LLCADDR(udata, udata->dl_dest_addr_offset)->llca_sap =
llchdr->llc_dsap;
}
udmp->b_wptr += udata->dl_dest_addr_length;
udata->dl_src_addr_offset = udata->dl_dest_addr_length +
udata->dl_dest_addr_offset;
bcopy(hdr->ether_shost.ether_addr_octet,
LLCADDR(udata, udata->dl_src_addr_offset)->llca_addr,
macinfo->llcp_addrlen);
if (lld->llc_flags & LLC_SNAP) {
udata->dl_src_addr_length = macinfo->llcp_addrlen + 2;
LLCSADDR(udata, udata->dl_src_addr_offset)->llca_ssap =
ntohs(*(ushort_t *)snap->snap_type);
} else {
udata->dl_src_addr_length = macinfo->llcp_addrlen + 1;
LLCADDR(udata, udata->dl_src_addr_offset)->llca_sap =
llchdr->llc_ssap;
}
udata->dl_group_address = hdr->ether_dhost.ether_addr_octet[0] &
0x1;
udmp->b_wptr += udata->dl_src_addr_length;
udmp->b_cont = mp;
} else {
dl_unitdata_ind_t *ud2;
if (mp->b_cont == NULL) {
return (mp);
}
nmp = dupmsg(mp);
udata = (dl_unitdata_ind_t *)(mp->b_rptr);
udmp = allocb(MBLKL(mp) + 4, BPRI_MED);
bcopy(mp->b_rptr, udmp->b_rptr, sizeof (dl_unitdata_ind_t));
ud2 = (dl_unitdata_ind_t *)(udmp->b_rptr);
udmp->b_wptr += sizeof (dl_unitdata_ind_t);
bcopy((caddr_t)mp->b_rptr + udata->dl_dest_addr_offset,
udmp->b_wptr, macinfo->llcp_addrlen);
ud2->dl_dest_addr_offset = sizeof (dl_unitdata_ind_t);
ud2->dl_dest_addr_length = macinfo->llcp_addrlen + 1;
udmp->b_wptr += ud2->dl_dest_addr_length;
bcopy((caddr_t)udmp->b_rptr + udata->dl_src_addr_offset,
udmp->b_wptr, macinfo->llcp_addrlen);
ud2->dl_src_addr_length = ud2->dl_dest_addr_length;
udmp->b_wptr += ud2->dl_src_addr_length;
udmp->b_cont = mp->b_cont;
if (lld->llc_sap != LLC_NOVELL_SAP)
mp->b_cont->b_rptr += sizeof (struct llchdr);
freeb(mp);
DB_TYPE(udmp) = M_PROTO;
udata = (dl_unitdata_ind_t *)(mp->b_rptr);
llchdr = (struct llchdr *)(mp->b_cont->b_rptr);
LLCADDR(udata, udata->dl_dest_addr_offset)->llca_sap =
llchdr->llc_dsap;
LLCADDR(udata, udata->dl_src_addr_offset)->llca_sap =
llchdr->llc_ssap;
}
#ifdef LLC1_DEBUG
if (llc1_debug & LLCRECV)
printf("llc1_recv: queued message to %x (%d)\n",
lld->llc_qptr, lld->llc_minor);
#endif
putnext(RD(lld->llc_qptr), udmp);
mp = nmp;
return (mp);
}
static mblk_t *
llc1_xid_reply(llc_mac_info_t *macinfo, mblk_t *mp, int sap)
{
mblk_t *nmp, *rmp;
struct ether_header *hdr, *msgether;
struct llchdr *llchdr;
struct llchdr *msgllc;
struct llchdr_xid *xid;
if (DB_TYPE(mp) == M_DATA) {
hdr = (struct ether_header *)mp->b_rptr;
llchdr = (struct llchdr *)(hdr + 1);
} else {
if (mp->b_cont == NULL)
return (mp);
llchdr = (struct llchdr *)(mp->b_cont->b_rptr);
}
if (llchdr->llc_ssap & LLC_RESPONSE)
return (mp);
nmp = allocb(msgdsize(mp) + LLC_XID_INFO_SIZE, BPRI_MED);
if (nmp == NULL) {
return (mp);
}
if (DB_TYPE(mp) == M_DATA) {
msgether = (struct ether_header *)nmp->b_rptr;
nmp->b_wptr += sizeof (struct ether_header);
bcopy(hdr->ether_shost.ether_addr_octet,
msgether->ether_dhost.ether_addr_octet,
macinfo->llcp_addrlen);
bcopy(macinfo->llcp_macaddr,
msgether->ether_shost.ether_addr_octet,
macinfo->llcp_addrlen);
msgether->ether_type = htons(sizeof (struct llchdr_xid) +
sizeof (struct llchdr));
rmp = nmp;
} else {
dl_unitdata_req_t *ud;
dl_unitdata_ind_t *rud;
rud = (dl_unitdata_ind_t *)mp->b_rptr;
rmp = allocb(sizeof (dl_unitdata_req_t) +
macinfo->llcp_addrlen + 5, BPRI_MED);
if (rmp == NULL)
return (mp);
DB_TYPE(rmp) = M_PROTO;
bzero(rmp->b_rptr, sizeof (dl_unitdata_req_t));
ud = (dl_unitdata_req_t *)rmp->b_rptr;
ud->dl_primitive = DL_UNITDATA_REQ;
ud->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
ud->dl_dest_addr_length = macinfo->llcp_addrlen + 1;
rmp->b_wptr += sizeof (dl_unitdata_req_t);
bcopy(LLCADDR(mp->b_rptr, rud->dl_src_addr_offset),
LLCADDR(rmp->b_rptr, ud->dl_dest_addr_offset),
macinfo->llcp_addrlen);
LLCADDR(rmp->b_rptr, ud->dl_dest_addr_offset)->llca_sap =
LLCADDR(mp->b_rptr, rud->dl_src_addr_offset)->llca_sap;
rmp->b_wptr += sizeof (struct llcaddr);
rmp->b_cont = nmp;
}
msgllc = (struct llchdr *)nmp->b_wptr;
xid = (struct llchdr_xid *)(msgllc + 1);
nmp->b_wptr += sizeof (struct llchdr);
msgllc->llc_dsap = llchdr->llc_ssap;
msgllc->llc_ssap = sap | LLC_RESPONSE;
msgllc->llc_ctl = llchdr->llc_ctl;
xid->llcx_format = LLC_XID_FMTID;
xid->llcx_class = LLC_XID_TYPE_1;
xid->llcx_window = 0;
nmp->b_wptr += sizeof (struct llchdr_xid);
macinfo->llcp_stats.llcs_xidxmt++;
putnext(WR(macinfo->llcp_queue), rmp);
return (mp);
}
static mblk_t *
llc1_xid_ind_con(llc1_t *lld, llc_mac_info_t *macinfo, mblk_t *mp)
{
mblk_t *nmp;
dl_xid_ind_t *xid;
struct ether_header *hdr;
struct llchdr *llchdr;
int raw;
nmp = allocb(sizeof (dl_xid_ind_t) + 2 * (macinfo->llcp_addrlen + 1),
BPRI_MED);
if (nmp == NULL)
return (mp);
if ((raw = (DB_TYPE(mp) == M_DATA)) != 0) {
hdr = (struct ether_header *)mp->b_rptr;
llchdr = (struct llchdr *)(hdr + 1);
} else {
if (mp->b_rptr == NULL)
return (mp);
llchdr = (struct llchdr *)mp->b_cont->b_rptr;
}
xid = (dl_xid_ind_t *)nmp->b_rptr;
xid->dl_flag = (llchdr->llc_ctl & LLC_P) ? DL_POLL_FINAL : 0;
xid->dl_dest_addr_offset = sizeof (dl_xid_ind_t);
xid->dl_dest_addr_length = macinfo->llcp_addrlen + 1;
if (raw) {
bcopy(hdr->ether_dhost.ether_addr_octet,
(nmp->b_rptr + xid->dl_dest_addr_offset),
xid->dl_dest_addr_length);
} else {
dl_unitdata_ind_t *ind;
ind = (dl_unitdata_ind_t *)mp->b_rptr;
bcopy(LLCADDR(ind, ind->dl_dest_addr_offset),
(nmp->b_rptr + xid->dl_dest_addr_offset),
xid->dl_dest_addr_length);
}
LLCADDR(xid, xid->dl_dest_addr_offset)->llca_sap =
llchdr->llc_dsap;
xid->dl_src_addr_offset =
xid->dl_dest_addr_offset + xid->dl_dest_addr_length;
xid->dl_src_addr_length = xid->dl_dest_addr_length;
if (raw) {
bcopy(hdr->ether_shost.ether_addr_octet,
(nmp->b_rptr + xid->dl_src_addr_offset),
xid->dl_src_addr_length);
} else {
dl_unitdata_ind_t *ind;
ind = (dl_unitdata_ind_t *)mp->b_rptr;
bcopy(LLCADDR(mp->b_rptr, ind->dl_src_addr_offset),
(nmp->b_rptr + xid->dl_src_addr_offset),
ind->dl_src_addr_length);
}
LLCADDR(nmp->b_rptr, xid->dl_src_addr_offset)->llca_sap =
llchdr->llc_ssap & ~LLC_RESPONSE;
nmp->b_wptr = nmp->b_rptr + sizeof (dl_xid_ind_t) +
2 * xid->dl_dest_addr_length;
if (!(llchdr->llc_ssap & LLC_RESPONSE)) {
xid->dl_primitive = DL_XID_IND;
} else {
xid->dl_primitive = DL_XID_CON;
}
DB_TYPE(nmp) = M_PROTO;
if (raw) {
if (MBLKL(mp) >
(sizeof (struct ether_header) + sizeof (struct llchdr))) {
nmp->b_cont = dupmsg(mp);
if (nmp->b_cont) {
nmp->b_cont->b_rptr +=
sizeof (struct ether_header) +
sizeof (struct llchdr);
}
}
} else if (mp->b_cont != NULL && MBLKL(mp->b_cont) >
sizeof (struct llchdr)) {
nmp->b_cont = dupmsg(mp->b_cont);
(void) adjmsg(nmp->b_cont, sizeof (struct llchdr));
}
putnext(RD(lld->llc_qptr), nmp);
return (mp);
}
static int
llc1_xid_req_res(queue_t *q, mblk_t *mp, int req_or_res)
{
dl_xid_req_t *xid = (dl_xid_req_t *)mp->b_rptr;
llc1_t *llc = (llc1_t *)q->q_ptr;
llc_mac_info_t *macinfo;
mblk_t *nmp, *rmp;
struct ether_header *hdr;
struct llchdr *llchdr;
if (llc == NULL || llc->llc_state == DL_UNATTACHED)
return (DL_OUTSTATE);
if (llc->llc_sap == LLC_NOVELL_SAP)
return (DL_NOTSUPPORTED);
if (llc->llc_flags & DL_AUTO_XID)
return (DL_XIDAUTO);
macinfo = llc->llc_mac_info;
if (MBLKL(mp) < sizeof (dl_xid_req_t) ||
!MBLKIN(mp, xid->dl_dest_addr_offset, xid->dl_dest_addr_length)) {
return (DL_BADPRIM);
}
nmp = allocb(sizeof (struct ether_header) + sizeof (struct llchdr) +
sizeof (struct llchdr_xid), BPRI_MED);
if (nmp == NULL)
return (LLCE_NOBUFFER);
if (macinfo->llcp_flags & LLC1_USING_RAW) {
hdr = (struct ether_header *)nmp->b_rptr;
bcopy(LLCADDR(xid, xid->dl_dest_addr_offset)->llca_addr,
hdr->ether_dhost.ether_addr_octet, ETHERADDRL);
bcopy(macinfo->llcp_macaddr,
hdr->ether_shost.ether_addr_octet, ETHERADDRL);
hdr->ether_type = htons(sizeof (struct llchdr) + msgdsize(mp));
nmp->b_wptr = nmp->b_rptr +
sizeof (struct ether_header) + sizeof (struct llchdr);
llchdr = (struct llchdr *)(hdr + 1);
rmp = nmp;
} else {
dl_unitdata_req_t *ud;
rmp = allocb(sizeof (dl_unitdata_req_t) +
(macinfo->llcp_addrlen + 2), BPRI_MED);
if (rmp == NULL) {
freemsg(nmp);
return (LLCE_NOBUFFER);
}
ud = (dl_unitdata_req_t *)rmp->b_rptr;
DB_TYPE(rmp) = M_PROTO;
ud->dl_primitive = DL_UNITDATA_REQ;
ud->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
ud->dl_dest_addr_length = xid->dl_dest_addr_length;
rmp->b_wptr += sizeof (dl_unitdata_req_t);
bcopy(LLCADDR(xid, xid->dl_dest_addr_offset)->llca_addr,
LLCADDR(ud, ud->dl_dest_addr_offset),
xid->dl_dest_addr_length);
LLCSADDR(ud, ud->dl_dest_addr_offset)->llca_ssap =
msgdsize(mp);
rmp->b_wptr += xid->dl_dest_addr_length;
rmp->b_cont = nmp;
llchdr = (struct llchdr *)nmp->b_rptr;
nmp->b_wptr += sizeof (struct llchdr);
}
llchdr->llc_dsap = LLCADDR(xid, xid->dl_dest_addr_offset)->llca_sap;
llchdr->llc_ssap = llc->llc_sap | (req_or_res ? LLC_RESPONSE : 0);
llchdr->llc_ctl =
LLC_XID | ((xid->dl_flag & DL_POLL_FINAL) ? LLC_P : 0);
nmp->b_cont = mp->b_cont;
mp->b_cont = NULL;
freeb(mp);
macinfo->llcp_stats.llcs_xidxmt++;
putnext(WR(macinfo->llcp_queue), rmp);
return (LLCE_OK);
}
static mblk_t *
llc1_test_reply(llc_mac_info_t *macinfo, mblk_t *mp, int sap)
{
mblk_t *nmp;
struct ether_header *hdr, *msgether;
struct llchdr *llchdr;
struct llchdr *msgllc;
int poll_final;
if (DB_TYPE(mp) == M_PROTO) {
if (mp->b_cont == NULL)
return (mp);
llchdr = (struct llchdr *)mp->b_cont->b_rptr;
hdr = NULL;
} else {
hdr = (struct ether_header *)mp->b_rptr;
llchdr = (struct llchdr *)(hdr + 1);
}
if (llchdr->llc_ssap & LLC_RESPONSE)
return (mp);
nmp = copymsg(mp);
if (nmp == NULL) {
nmp = mp;
mp = NULL;
}
poll_final = llchdr->llc_ctl & LLC_P;
if (DB_TYPE(nmp) == M_PROTO) {
dl_unitdata_req_t *udr = (dl_unitdata_req_t *)nmp->b_rptr;
dl_unitdata_ind_t *udi = (dl_unitdata_ind_t *)nmp->b_rptr;
udr->dl_primitive = DL_UNITDATA_REQ;
udr->dl_dest_addr_offset = udi->dl_src_addr_offset;
udr->dl_dest_addr_length = udi->dl_src_addr_length;
udr->dl_priority.dl_min = udr->dl_priority.dl_max = 0;
msgllc = (struct llchdr *)nmp->b_cont->b_rptr;
} else {
msgether = (struct ether_header *)nmp->b_rptr;
bcopy(hdr->ether_shost.ether_addr_octet,
msgether->ether_dhost.ether_addr_octet,
macinfo->llcp_addrlen);
bcopy(macinfo->llcp_macaddr,
msgether->ether_shost.ether_addr_octet,
macinfo->llcp_addrlen);
msgllc = (struct llchdr *)(msgether+1);
}
msgllc->llc_dsap = llchdr->llc_ssap;
msgllc->llc_ssap = sap | LLC_RESPONSE;
msgllc->llc_ctl = LLC_TEST | poll_final;
macinfo->llcp_stats.llcs_testxmt++;
putnext(WR(macinfo->llcp_queue), nmp);
return (mp);
}
static mblk_t *
llc1_test_ind_con(llc1_t *lld, llc_mac_info_t *macinfo, mblk_t *mp)
{
mblk_t *nmp;
dl_test_ind_t *test;
struct ether_header *hdr;
struct llchdr *llchdr;
int raw;
nmp = allocb(sizeof (dl_test_ind_t) + 2 * (ETHERADDRL + 1), BPRI_MED);
if (nmp == NULL)
return (NULL);
if ((raw = (DB_TYPE(mp) == M_DATA)) != 0) {
hdr = (struct ether_header *)mp->b_rptr;
llchdr = (struct llchdr *)(hdr + 1);
} else {
if (mp->b_rptr == NULL)
return (mp);
llchdr = (struct llchdr *)mp->b_cont->b_rptr;
}
test = (dl_test_ind_t *)nmp->b_rptr;
test->dl_flag = (llchdr->llc_ctl & LLC_P) ? DL_POLL_FINAL : 0;
test->dl_dest_addr_offset = sizeof (dl_test_ind_t);
test->dl_dest_addr_length = macinfo->llcp_addrlen + 1;
if (raw) {
bcopy(hdr->ether_dhost.ether_addr_octet,
LLCADDR(nmp->b_rptr, test->dl_dest_addr_offset)->llca_addr,
test->dl_dest_addr_length);
} else {
dl_unitdata_ind_t *ind;
ind = (dl_unitdata_ind_t *)mp->b_rptr;
bcopy(LLCADDR(ind, ind->dl_dest_addr_offset),
(nmp->b_rptr + test->dl_dest_addr_offset),
test->dl_dest_addr_length);
}
LLCADDR(test, test->dl_dest_addr_offset)->llca_sap =
llchdr->llc_dsap;
test->dl_src_addr_offset = test->dl_dest_addr_offset +
test->dl_dest_addr_length;
test->dl_src_addr_length = test->dl_dest_addr_length;
if (raw) {
bcopy(hdr->ether_shost.ether_addr_octet,
LLCADDR(nmp->b_rptr, test->dl_src_addr_offset)->llca_addr,
test->dl_src_addr_length);
} else {
dl_unitdata_ind_t *ind;
ind = (dl_unitdata_ind_t *)mp->b_rptr;
bcopy(LLCADDR(mp->b_rptr, ind->dl_src_addr_offset),
(nmp->b_rptr + test->dl_src_addr_offset),
ind->dl_src_addr_length);
}
LLCADDR(nmp->b_rptr, test->dl_src_addr_offset)->llca_sap =
llchdr->llc_ssap & ~LLC_RESPONSE;
nmp->b_wptr = nmp->b_rptr + sizeof (dl_test_ind_t) +
2 * test->dl_dest_addr_length;
if (!(llchdr->llc_ssap & LLC_RESPONSE)) {
test->dl_primitive = DL_TEST_IND;
} else {
test->dl_primitive = DL_TEST_CON;
}
DB_TYPE(nmp) = M_PROTO;
if (raw) {
if (MBLKL(mp) >
(sizeof (struct ether_header) + sizeof (struct llchdr))) {
nmp->b_cont = dupmsg(mp);
if (nmp->b_cont) {
nmp->b_cont->b_rptr +=
sizeof (struct ether_header) +
sizeof (struct llchdr);
}
}
} else if (mp->b_cont != NULL && MBLKL(mp->b_cont) >
sizeof (struct llchdr)) {
nmp->b_cont = dupmsg(mp->b_cont);
(void) adjmsg(nmp->b_cont, sizeof (struct llchdr));
}
putnext(RD(lld->llc_qptr), nmp);
return (mp);
}
static int
llc1_test_req_res(queue_t *q, mblk_t *mp, int req_or_res)
{
dl_test_req_t *test = (dl_test_req_t *)mp->b_rptr;
llc1_t *llc = (llc1_t *)q->q_ptr;
llc_mac_info_t *macinfo;
mblk_t *nmp, *rmp;
struct ether_header *hdr;
struct llchdr *llchdr;
if (llc == NULL || llc->llc_state == DL_UNATTACHED)
return (DL_OUTSTATE);
if (llc->llc_sap == LLC_NOVELL_SAP)
return (DL_NOTSUPPORTED);
if (llc->llc_flags & DL_AUTO_TEST)
return (DL_TESTAUTO);
macinfo = llc->llc_mac_info;
if (MBLKL(mp) < sizeof (dl_test_req_t) ||
!MBLKIN(mp, test->dl_dest_addr_offset,
test->dl_dest_addr_length)) {
return (DL_BADPRIM);
}
nmp = allocb(sizeof (struct ether_header) + sizeof (struct llchdr),
BPRI_MED);
if (nmp == NULL)
return (LLCE_NOBUFFER);
if (macinfo->llcp_flags & LLC1_USING_RAW) {
hdr = (struct ether_header *)nmp->b_rptr;
bcopy(LLCADDR(test, test->dl_dest_addr_offset)->llca_addr,
hdr->ether_dhost.ether_addr_octet, ETHERADDRL);
bcopy(macinfo->llcp_macaddr,
hdr->ether_shost.ether_addr_octet, ETHERADDRL);
hdr->ether_type = htons(sizeof (struct llchdr) + msgdsize(mp));
nmp->b_wptr = nmp->b_rptr +
sizeof (struct ether_header) + sizeof (struct llchdr);
llchdr = (struct llchdr *)(hdr + 1);
rmp = nmp;
} else {
dl_unitdata_req_t *ud;
rmp = allocb(sizeof (dl_unitdata_req_t) +
(macinfo->llcp_addrlen + 2), BPRI_MED);
if (rmp == NULL) {
freemsg(nmp);
return (LLCE_NOBUFFER);
}
ud = (dl_unitdata_req_t *)rmp->b_rptr;
DB_TYPE(rmp) = M_PROTO;
ud->dl_primitive = DL_UNITDATA_REQ;
ud->dl_dest_addr_offset = sizeof (dl_unitdata_req_t);
ud->dl_dest_addr_length = test->dl_dest_addr_length;
rmp->b_wptr += sizeof (dl_unitdata_req_t);
bcopy(LLCADDR(test, test->dl_dest_addr_offset)->llca_addr,
LLCADDR(ud, ud->dl_dest_addr_offset),
test->dl_dest_addr_length);
LLCSADDR(ud, ud->dl_dest_addr_offset)->llca_ssap =
msgdsize(mp);
rmp->b_wptr += test->dl_dest_addr_length;
rmp->b_cont = nmp;
llchdr = (struct llchdr *)nmp->b_rptr;
nmp->b_wptr += sizeof (struct llchdr);
}
llchdr->llc_dsap = LLCADDR(test, test->dl_dest_addr_offset)->llca_sap;
llchdr->llc_ssap = llc->llc_sap | (req_or_res ? LLC_RESPONSE : 0);
llchdr->llc_ctl =
LLC_TEST | ((test->dl_flag & DL_POLL_FINAL) ? LLC_P : 0);
nmp->b_cont = mp->b_cont;
mp->b_cont = NULL;
freeb(mp);
macinfo->llcp_stats.llcs_testxmt++;
putnext(WR(macinfo->llcp_queue), rmp);
return (LLCE_OK);
}
static void
llc1_find_waiting(llc_mac_info_t *macinfo, mblk_t *mp, long prim)
{
llc1_t *llc;
for (llc = llc1_device_list.llc1_str_next;
llc != (llc1_t *)&llc1_device_list.llc1_str_next;
llc = llc->llc_next)
if (llc->llc_mac_info == macinfo &&
prim == llc->llc_waiting_for) {
putnext(RD(llc->llc_qptr), mp);
llc->llc_waiting_for = -1;
return;
}
freemsg(mp);
}
static void
llc1insque(void *elem, void *pred)
{
struct qelem *pelem = elem;
struct qelem *ppred = pred;
struct qelem *pnext = ppred->q_forw;
pelem->q_forw = pnext;
pelem->q_back = ppred;
ppred->q_forw = pelem;
pnext->q_back = pelem;
}
static void
llc1remque(void *arg)
{
struct qelem *pelem = arg;
struct qelem *elem = arg;
ASSERT(pelem->q_forw != NULL);
pelem->q_forw->q_back = pelem->q_back;
pelem->q_back->q_forw = pelem->q_forw;
elem->q_back = elem->q_forw = NULL;
}
static void
llc1error(dev_info_t *dip, char *fmt, char *a1, char *a2, char *a3,
char *a4, char *a5, char *a6)
{
static long last;
static char *lastfmt;
time_t now;
now = gethrestime_sec();
if ((last == (now & ~1)) && (lastfmt == fmt))
return;
last = now & ~1;
lastfmt = fmt;
cmn_err(CE_CONT, "%s%d: ",
ddi_get_name(dip), ddi_get_instance(dip));
cmn_err(CE_CONT, fmt, a1, a2, a3, a4, a5, a6);
cmn_err(CE_CONT, "\n");
}
static int
llc1_update_kstat(kstat_t *ksp, int rw)
{
llc_mac_info_t *macinfo;
kstat_named_t *kstat;
struct llc_stats *stats;
if (ksp == NULL)
return (0);
kstat = (kstat_named_t *)(ksp->ks_data);
macinfo = (llc_mac_info_t *)(ksp->ks_private);
stats = &macinfo->llcp_stats;
kstat[LLCS_NOBUFFER].value.ul = stats->llcs_nobuffer;
kstat[LLCS_MULTIXMT].value.ul = stats->llcs_multixmt;
kstat[LLCS_MULTIRCV].value.ul = stats->llcs_multircv;
kstat[LLCS_BRDCSTXMT].value.ul = stats->llcs_brdcstxmt;
kstat[LLCS_BRDCSTRCV].value.ul = stats->llcs_brdcstrcv;
kstat[LLCS_BLOCKED].value.ul = stats->llcs_blocked;
kstat[LLCS_PKTXMT].value.ul = stats->llcs_pktxmt;
kstat[LLCS_PKTRCV].value.ul = stats->llcs_pktrcv;
kstat[LLCS_BYTEXMT].value.ul = stats->llcs_bytexmt;
kstat[LLCS_BYTERCV].value.ul = stats->llcs_bytercv;
kstat[LLCS_XIDXMT].value.ul = stats->llcs_xidxmt;
kstat[LLCS_XIDRCV].value.ul = stats->llcs_xidrcv;
kstat[LLCS_TESTXMT].value.ul = stats->llcs_testxmt;
kstat[LLCS_TESTRCV].value.ul = stats->llcs_testrcv;
kstat[LLCS_IERRORS].value.ul = stats->llcs_ierrors;
kstat[LLCS_OERRORS].value.ul = stats->llcs_oerrors;
return (0);
}
static void
llc1_init_kstat(llc_mac_info_t *macinfo)
{
kstat_named_t *ksp;
macinfo->llcp_kstatp = kstat_create("llc", (-macinfo->llcp_ppa - 1),
NULL, "net", KSTAT_TYPE_NAMED,
sizeof (struct llc_stats) / sizeof (long), 0);
if (macinfo->llcp_kstatp == NULL)
return;
macinfo->llcp_kstatp->ks_update = llc1_update_kstat;
macinfo->llcp_kstatp->ks_private = (void *)macinfo;
ksp = (kstat_named_t *)(macinfo->llcp_kstatp->ks_data);
kstat_named_init(&ksp[LLCS_NOBUFFER], "nobuffer", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_MULTIXMT], "multixmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_MULTIRCV], "multircv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_BRDCSTXMT], "brdcstxmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_BRDCSTRCV], "brdcstrcv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_BLOCKED], "blocked", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_PKTXMT], "pktxmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_PKTRCV], "pktrcv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_BYTEXMT], "bytexmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_BYTERCV], "bytercv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_XIDXMT], "xidxmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_XIDRCV], "xidrcv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_TESTXMT], "testxmt", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_TESTRCV], "testrcv", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_IERRORS], "ierrors", KSTAT_DATA_ULONG);
kstat_named_init(&ksp[LLCS_OERRORS], "oerrors", KSTAT_DATA_ULONG);
kstat_install(macinfo->llcp_kstatp);
}
static void
llc1_uninit_kstat(llc_mac_info_t *macinfo)
{
if (macinfo->llcp_kstatp) {
kstat_delete(macinfo->llcp_kstatp);
macinfo->llcp_kstatp = NULL;
}
}
static int
llc1_subs_bind(queue_t *q, mblk_t *mp)
{
llc1_t *lld = (llc1_t *)q->q_ptr;
dl_subs_bind_req_t *subs = (dl_subs_bind_req_t *)mp->b_rptr;
ushort_t subssap;
uchar_t *sapstr;
int result;
#if defined(LLC1_DEBUG)
if (llc1_debug & (LLCTRACE|LLCPROT)) {
printf("llc1_subs_bind (%x, %x)\n", q, mp);
}
#endif
if (lld == NULL || lld->llc_state != DL_IDLE) {
result = DL_OUTSTATE;
} else if (lld->llc_sap != LLC_SNAP_SAP ||
subs->dl_subs_bind_class != DL_HIERARCHICAL_BIND) {
result = DL_UNSUPPORTED;
} else {
lld->llc_state = DL_SUBS_BIND_PND;
sapstr = (uchar_t *)(mp->b_rptr + subs->dl_subs_sap_offset);
result = LLCE_OK;
switch (subs->dl_subs_sap_length) {
case 2:
if (lld->llc_flags & LLC_SNAP) {
result = DL_BADADDR;
break;
}
((uchar_t *)&subssap)[0] = sapstr[0];
((uchar_t *)&subssap)[1] = sapstr[1];
subssap = htons(subssap);
lld->llc_snap[3] = ((uchar_t *)&subssap)[0];
lld->llc_snap[4] = ((uchar_t *)&subssap)[1];
lld->llc_flags |= LLC_SNAP;
break;
case 3:
if ((lld->llc_flags & (LLC_SNAP|LLC_SNAP_OID)) ==
(LLC_SNAP|LLC_SNAP_OID)) {
result = DL_BADADDR;
break;
}
bcopy(sapstr, lld->llc_snap, 3);
lld->llc_flags |= LLC_SNAP_OID;
break;
case 5:
if (lld->llc_flags & (LLC_SNAP|LLC_SNAP_OID)) {
result = DL_BADADDR;
break;
}
bcopy(sapstr, lld->llc_snap, 5);
lld->llc_flags |= LLC_SNAP|LLC_SNAP_OID;
break;
}
if (result == LLCE_OK) {
mblk_t *nmp = mp;
dl_subs_bind_ack_t *ack;
if (DB_REF(mp) != 1 ||
MBLKL(mp) < (sizeof (dl_subs_bind_ack_t) + 5)) {
freemsg(mp);
nmp = allocb(sizeof (dl_subs_bind_ack_t) + 5,
BPRI_MED);
}
ack = (dl_subs_bind_ack_t *)nmp->b_rptr;
nmp->b_wptr = nmp->b_rptr +
sizeof (dl_subs_bind_ack_t) + 5;
ack->dl_primitive = DL_SUBS_BIND_ACK;
ack->dl_subs_sap_offset = sizeof (dl_subs_bind_ack_t);
ack->dl_subs_sap_length = 5;
bcopy(lld->llc_snap,
(caddr_t)nmp->b_rptr + ack->dl_subs_sap_offset + 5,
5);
DB_TYPE(nmp) = M_PCPROTO;
qreply(q, nmp);
}
lld->llc_state = DL_IDLE;
}
return (result);
}
static int
llc1_subs_unbind(void)
{
return (DL_UNSUPPORTED);
}
char *
snapdmp(uchar_t *bstr)
{
static char buff[32];
(void) sprintf(buff, "%x.%x.%x.%x.%x",
bstr[0],
bstr[1],
bstr[2],
bstr[3],
bstr[4]);
return (buff);
}
static int
llc1_snap_match(llc1_t *lld, struct snaphdr *snap)
{
return (bcmp(snap->snap_oid, lld->llc_snap, 5) == 0);
}