/* * 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" /* * Common Sun DLPI routines. */ #include <sys/types.h> #include <sys/sysmacros.h> #include <sys/byteorder.h> #include <sys/systm.h> #include <sys/stream.h> #include <sys/strsun.h> #include <sys/dlpi.h> #include <sys/ddi.h> #include <sys/sunddi.h> #include <sys/sunldi.h> #include <sys/cmn_err.h> #define DLADDRL (80) void dlbindack( queue_t *wq, mblk_t *mp, t_scalar_t sap, void *addrp, t_uscalar_t addrlen, t_uscalar_t maxconind, t_uscalar_t xidtest) { union DL_primitives *dlp; size_t size; size = sizeof (dl_bind_ack_t) + addrlen; if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_BIND_ACK)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->bind_ack.dl_sap = sap; dlp->bind_ack.dl_addr_length = addrlen; dlp->bind_ack.dl_addr_offset = sizeof (dl_bind_ack_t); dlp->bind_ack.dl_max_conind = maxconind; dlp->bind_ack.dl_xidtest_flg = xidtest; if (addrlen != 0) bcopy(addrp, mp->b_rptr + sizeof (dl_bind_ack_t), addrlen); qreply(wq, mp); } void dlokack( queue_t *wq, mblk_t *mp, t_uscalar_t correct_primitive) { union DL_primitives *dlp; if ((mp = mexchange(wq, mp, sizeof (dl_ok_ack_t), M_PCPROTO, DL_OK_ACK)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->ok_ack.dl_correct_primitive = correct_primitive; qreply(wq, mp); } void dlerrorack( queue_t *wq, mblk_t *mp, t_uscalar_t error_primitive, t_uscalar_t error, t_uscalar_t unix_errno) { union DL_primitives *dlp; if ((mp = mexchange(wq, mp, sizeof (dl_error_ack_t), M_PCPROTO, DL_ERROR_ACK)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->error_ack.dl_error_primitive = error_primitive; dlp->error_ack.dl_errno = error; dlp->error_ack.dl_unix_errno = unix_errno; qreply(wq, mp); } void dluderrorind( queue_t *wq, mblk_t *mp, void *addrp, t_uscalar_t addrlen, t_uscalar_t error, t_uscalar_t unix_errno) { union DL_primitives *dlp; char buf[DLADDRL]; size_t size; if (addrlen > DLADDRL) addrlen = DLADDRL; bcopy(addrp, buf, addrlen); size = sizeof (dl_uderror_ind_t) + addrlen; if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_UDERROR_IND)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->uderror_ind.dl_dest_addr_length = addrlen; dlp->uderror_ind.dl_dest_addr_offset = sizeof (dl_uderror_ind_t); dlp->uderror_ind.dl_unix_errno = unix_errno; dlp->uderror_ind.dl_errno = error; bcopy((caddr_t)buf, (caddr_t)(mp->b_rptr + sizeof (dl_uderror_ind_t)), addrlen); qreply(wq, mp); } void dlphysaddrack( queue_t *wq, mblk_t *mp, void *addrp, t_uscalar_t len) { union DL_primitives *dlp; size_t size; size = sizeof (dl_phys_addr_ack_t) + len; if ((mp = mexchange(wq, mp, size, M_PCPROTO, DL_PHYS_ADDR_ACK)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->physaddr_ack.dl_addr_length = len; dlp->physaddr_ack.dl_addr_offset = sizeof (dl_phys_addr_ack_t); if (len != 0) bcopy(addrp, mp->b_rptr + sizeof (dl_phys_addr_ack_t), len); qreply(wq, mp); } void dlcapabsetqid(dl_mid_t *idp, const queue_t *q) { #ifndef _LP64 idp->mid[0] = (t_uscalar_t)q; #else idp->mid[0] = (t_uscalar_t)BMASK_32((uint64_t)q); idp->mid[1] = (t_uscalar_t)BMASK_32(((uint64_t)q) >> 32); #endif } boolean_t dlcapabcheckqid(const dl_mid_t *idp, const queue_t *q) { #ifndef _LP64 return ((queue_t *)(idp->mid[0]) == q); #else return ((queue_t *) ((uint64_t)idp->mid[0] | ((uint64_t)idp->mid[1] << 32)) == q); #endif } void dlnotifyack( queue_t *wq, mblk_t *mp, uint32_t notifications) { union DL_primitives *dlp; if ((mp = mexchange(wq, mp, sizeof (dl_notify_ack_t), M_PROTO, DL_NOTIFY_ACK)) == NULL) return; dlp = (union DL_primitives *)mp->b_rptr; dlp->notify_ack.dl_notifications = notifications; qreply(wq, mp); } static int dl_op(ldi_handle_t lh, mblk_t **mpp, t_uscalar_t expprim, size_t minlen, dl_error_ack_t *dleap, timestruc_t *tvp) { int err; size_t len; mblk_t *mp = *mpp; t_uscalar_t reqprim, ackprim, ackreqprim; union DL_primitives *dlp; reqprim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; (void) ldi_putmsg(lh, mp); switch (err = ldi_getmsg(lh, &mp, tvp)) { case 0: break; case ETIME: cmn_err(CE_NOTE, "!dl_op: timed out waiting for %s to %s", dl_primstr(reqprim), dl_primstr(expprim)); return (ETIME); default: cmn_err(CE_NOTE, "!dl_op: ldi_getmsg() for %s failed: %d", dl_primstr(expprim), err); return (err); } len = MBLKL(mp); if (len < sizeof (t_uscalar_t)) { cmn_err(CE_NOTE, "!dl_op: received runt DLPI message"); freemsg(mp); return (EBADMSG); } dlp = (union DL_primitives *)mp->b_rptr; ackprim = dlp->dl_primitive; if (ackprim == expprim) { if (len < minlen) goto runt; if (ackprim == DL_OK_ACK) { if (dlp->ok_ack.dl_correct_primitive != reqprim) { ackreqprim = dlp->ok_ack.dl_correct_primitive; goto mixup; } } *mpp = mp; return (0); } if (ackprim == DL_ERROR_ACK) { if (len < DL_ERROR_ACK_SIZE) goto runt; if (dlp->error_ack.dl_error_primitive != reqprim) { ackreqprim = dlp->error_ack.dl_error_primitive; goto mixup; } /* * Return a special error code (ENOTSUP) indicating that the * caller has returned DL_ERROR_ACK. Callers that want more * details an pass a non-NULL dleap. */ if (dleap != NULL) *dleap = dlp->error_ack; freemsg(mp); return (ENOTSUP); } cmn_err(CE_NOTE, "!dl_op: expected %s but received %s", dl_primstr(expprim), dl_primstr(ackprim)); freemsg(mp); return (EBADMSG); runt: cmn_err(CE_NOTE, "!dl_op: received runt %s", dl_primstr(ackprim)); freemsg(mp); return (EBADMSG); mixup: cmn_err(CE_NOTE, "!dl_op: received %s for %s instead of %s", dl_primstr(ackprim), dl_primstr(ackreqprim), dl_primstr(reqprim)); freemsg(mp); return (EBADMSG); } /* * Send a DL_ATTACH_REQ for `ppa' over `lh' and wait for the response. * * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the * caller can get the contents by passing a non-NULL `dleap'). */ int dl_attach(ldi_handle_t lh, int ppa, dl_error_ack_t *dleap) { mblk_t *mp; int err; mp = mexchange(NULL, NULL, DL_ATTACH_REQ_SIZE, M_PROTO, DL_ATTACH_REQ); if (mp == NULL) return (ENOMEM); ((dl_attach_req_t *)mp->b_rptr)->dl_ppa = ppa; err = dl_op(lh, &mp, DL_OK_ACK, DL_OK_ACK_SIZE, dleap, NULL); if (err == 0) freemsg(mp); return (err); } /* * Send a DL_BIND_REQ for `sap' over `lh' and wait for the response. * * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the * caller can get the contents by passing a non-NULL `dleap'). */ int dl_bind(ldi_handle_t lh, uint_t sap, dl_error_ack_t *dleap) { dl_bind_req_t *dlbrp; dl_bind_ack_t *dlbap; mblk_t *mp; int err; mp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); if (mp == NULL) return (ENOMEM); dlbrp = (dl_bind_req_t *)mp->b_rptr; dlbrp->dl_sap = sap; dlbrp->dl_conn_mgmt = 0; dlbrp->dl_max_conind = 0; dlbrp->dl_xidtest_flg = 0; dlbrp->dl_service_mode = DL_CLDLS; err = dl_op(lh, &mp, DL_BIND_ACK, DL_BIND_ACK_SIZE, dleap, NULL); if (err == 0) { dlbap = (dl_bind_ack_t *)mp->b_rptr; if (dlbap->dl_sap != sap) { cmn_err(CE_NOTE, "!dl_bind: DL_BIND_ACK: bad sap %u", dlbap->dl_sap); err = EPROTO; } freemsg(mp); } return (err); } /* * Send a DL_PHYS_ADDR_REQ over `lh' and wait for the response. The caller * must set `*physlenp' to the size of `physaddr' (both of which must be * non-NULL); upon success they will be updated to contain the actual physical * address and length. * * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the * caller can get the contents by passing a non-NULL `dleap'). */ int dl_phys_addr(ldi_handle_t lh, uchar_t *physaddr, size_t *physlenp, dl_error_ack_t *dleap) { dl_phys_addr_ack_t *dlpap; mblk_t *mp; int err; t_uscalar_t paddrlen, paddroff; timestruc_t tv; mp = mexchange(NULL, NULL, DL_PHYS_ADDR_REQ_SIZE, M_PROTO, DL_PHYS_ADDR_REQ); if (mp == NULL) return (ENOMEM); ((dl_phys_addr_req_t *)mp->b_rptr)->dl_addr_type = DL_CURR_PHYS_ADDR; /* * In case some provider doesn't implement or NAK the * request, just wait for 15 seconds. */ tv.tv_sec = 15; tv.tv_nsec = 0; err = dl_op(lh, &mp, DL_PHYS_ADDR_ACK, DL_PHYS_ADDR_ACK_SIZE, dleap, &tv); if (err == 0) { dlpap = (dl_phys_addr_ack_t *)mp->b_rptr; paddrlen = dlpap->dl_addr_length; paddroff = dlpap->dl_addr_offset; if (paddroff == 0 || paddrlen == 0 || paddrlen > *physlenp || !MBLKIN(mp, paddroff, paddrlen)) { cmn_err(CE_NOTE, "!dl_phys_addr: DL_PHYS_ADDR_ACK: " "bad length/offset %d/%d", paddrlen, paddroff); err = EBADMSG; } else { bcopy(mp->b_rptr + paddroff, physaddr, paddrlen); *physlenp = paddrlen; } freemsg(mp); } return (err); } /* * Send a DL_INFO_REQ over `lh' and wait for the response. The caller must * pass a non-NULL `dliap', which upon success will contain the dl_info_ack_t * from the provider. The caller may optionally get the provider's physical * address by passing a non-NULL `physaddr' and setting `*physlenp' to its * size; upon success they will be updated to contain the actual physical * address and its length. * * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the * caller can get the contents by passing a non-NULL `dleap'). */ int dl_info(ldi_handle_t lh, dl_info_ack_t *dliap, uchar_t *physaddr, size_t *physlenp, dl_error_ack_t *dleap) { mblk_t *mp; int err; int addrlen, addroff; mp = mexchange(NULL, NULL, DL_INFO_REQ_SIZE, M_PCPROTO, DL_INFO_REQ); if (mp == NULL) return (ENOMEM); err = dl_op(lh, &mp, DL_INFO_ACK, DL_INFO_ACK_SIZE, dleap, NULL); if (err != 0) return (err); *dliap = *(dl_info_ack_t *)mp->b_rptr; if (physaddr != NULL) { addrlen = dliap->dl_addr_length - ABS(dliap->dl_sap_length); addroff = dliap->dl_addr_offset; if (addroff == 0 || addrlen <= 0 || addrlen > *physlenp || !MBLKIN(mp, addroff, dliap->dl_addr_length)) { cmn_err(CE_NOTE, "!dl_info: DL_INFO_ACK: " "bad length/offset %d/%d", addrlen, addroff); freemsg(mp); return (EBADMSG); } if (dliap->dl_sap_length > 0) addroff += dliap->dl_sap_length; bcopy(mp->b_rptr + addroff, physaddr, addrlen); *physlenp = addrlen; } freemsg(mp); return (err); } /* * Send a DL_NOTIFY_REQ over `lh' and wait for the response. The caller * should set `notesp' to the set of notifications they wish to enable; * upon success it will contain the notifications enabled by the provider. * * Returns an errno; ENOTSUP indicates a DL_ERROR_ACK response (and the * caller can get the contents by passing a non-NULL `dleap'). */ int dl_notify(ldi_handle_t lh, uint32_t *notesp, dl_error_ack_t *dleap) { mblk_t *mp; int err; mp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, DL_NOTIFY_REQ); if (mp == NULL) return (ENOMEM); ((dl_notify_req_t *)mp->b_rptr)->dl_notifications = *notesp; err = dl_op(lh, &mp, DL_NOTIFY_ACK, DL_NOTIFY_ACK_SIZE, dleap, NULL); if (err == 0) { *notesp = ((dl_notify_ack_t *)mp->b_rptr)->dl_notifications; freemsg(mp); } return (err); } const char * dl_primstr(t_uscalar_t prim) { switch (prim) { case DL_INFO_REQ: return ("DL_INFO_REQ"); case DL_INFO_ACK: return ("DL_INFO_ACK"); case DL_ATTACH_REQ: return ("DL_ATTACH_REQ"); case DL_DETACH_REQ: return ("DL_DETACH_REQ"); case DL_BIND_REQ: return ("DL_BIND_REQ"); case DL_BIND_ACK: return ("DL_BIND_ACK"); case DL_UNBIND_REQ: return ("DL_UNBIND_REQ"); case DL_OK_ACK: return ("DL_OK_ACK"); case DL_ERROR_ACK: return ("DL_ERROR_ACK"); case DL_ENABMULTI_REQ: return ("DL_ENABMULTI_REQ"); case DL_DISABMULTI_REQ: return ("DL_DISABMULTI_REQ"); case DL_PROMISCON_REQ: return ("DL_PROMISCON_REQ"); case DL_PROMISCOFF_REQ: return ("DL_PROMISCOFF_REQ"); case DL_UNITDATA_REQ: return ("DL_UNITDATA_REQ"); case DL_UNITDATA_IND: return ("DL_UNITDATA_IND"); case DL_UDERROR_IND: return ("DL_UDERROR_IND"); case DL_PHYS_ADDR_REQ: return ("DL_PHYS_ADDR_REQ"); case DL_PHYS_ADDR_ACK: return ("DL_PHYS_ADDR_ACK"); case DL_SET_PHYS_ADDR_REQ: return ("DL_SET_PHYS_ADDR_REQ"); case DL_NOTIFY_REQ: return ("DL_NOTIFY_REQ"); case DL_NOTIFY_ACK: return ("DL_NOTIFY_ACK"); case DL_NOTIFY_IND: return ("DL_NOTIFY_IND"); case DL_CAPABILITY_REQ: return ("DL_CAPABILITY_REQ"); case DL_CAPABILITY_ACK: return ("DL_CAPABILITY_ACK"); case DL_CONTROL_REQ: return ("DL_CONTROL_REQ"); case DL_CONTROL_ACK: return ("DL_CONTROL_ACK"); case DL_PASSIVE_REQ: return ("DL_PASSIVE_REQ"); case DL_INTR_MODE_REQ: return ("DL_INTR_MODE_REQ"); case DL_UDQOS_REQ: return ("DL_UDQOS_REQ"); default: return ("<unknown primitive>"); } } const char * dl_errstr(t_uscalar_t err) { switch (err) { case DL_ACCESS: return ("DL_ACCESS"); case DL_BADADDR: return ("DL_BADADDR"); case DL_BADCORR: return ("DL_BADCORR"); case DL_BADDATA: return ("DL_BADDATA"); case DL_BADPPA: return ("DL_BADPPA"); case DL_BADPRIM: return ("DL_BADPRIM"); case DL_BADQOSPARAM: return ("DL_BADQOSPARAM"); case DL_BADQOSTYPE: return ("DL_BADQOSTYPE"); case DL_BADSAP: return ("DL_BADSAP"); case DL_BADTOKEN: return ("DL_BADTOKEN"); case DL_BOUND: return ("DL_BOUND"); case DL_INITFAILED: return ("DL_INITFAILED"); case DL_NOADDR: return ("DL_NOADDR"); case DL_NOTINIT: return ("DL_NOTINIT"); case DL_OUTSTATE: return ("DL_OUTSTATE"); case DL_SYSERR: return ("DL_SYSERR"); case DL_UNSUPPORTED: return ("DL_UNSUPPORTED"); case DL_UNDELIVERABLE: return ("DL_UNDELIVERABLE"); case DL_NOTSUPPORTED: return ("DL_NOTSUPPORTED "); case DL_TOOMANY: return ("DL_TOOMANY"); case DL_NOTENAB: return ("DL_NOTENAB"); case DL_BUSY: return ("DL_BUSY"); case DL_NOAUTO: return ("DL_NOAUTO"); case DL_NOXIDAUTO: return ("DL_NOXIDAUTO"); case DL_NOTESTAUTO: return ("DL_NOTESTAUTO"); case DL_XIDAUTO: return ("DL_XIDAUTO"); case DL_TESTAUTO: return ("DL_TESTAUTO"); case DL_PENDING: return ("DL_PENDING"); default: return ("<unknown error>"); } } const char * dl_mactypestr(t_uscalar_t mactype) { switch (mactype) { case DL_CSMACD: return ("CSMA/CD"); case DL_TPB: return ("Token Bus"); case DL_TPR: return ("Token Ring"); case DL_METRO: return ("Metro Net"); case DL_ETHER: return ("Ethernet"); case DL_HDLC: return ("HDLC"); case DL_CHAR: return ("Sync Character"); case DL_CTCA: return ("CTCA"); case DL_FDDI: return ("FDDI"); case DL_FRAME: return ("Frame Relay (LAPF)"); case DL_MPFRAME: return ("MP Frame Relay"); case DL_ASYNC: return ("Async Character"); case DL_IPX25: return ("X.25 (Classic IP)"); case DL_LOOP: return ("Software Loopback"); case DL_FC: return ("Fiber Channel"); case DL_ATM: return ("ATM"); case DL_IPATM: return ("ATM (Classic IP)"); case DL_X25: return ("X.25 (LAPB)"); case DL_ISDN: return ("ISDN"); case DL_HIPPI: return ("HIPPI"); case DL_100VG: return ("100BaseVG Ethernet"); case DL_100VGTPR: return ("100BaseVG Token Ring"); case DL_ETH_CSMA: return ("Ethernet/IEEE 802.3"); case DL_100BT: return ("100BaseT"); case DL_IB: return ("Infiniband"); case DL_IPV4: return ("IPv4 Tunnel"); case DL_IPV6: return ("IPv6 Tunnel"); case DL_WIFI: return ("IEEE 802.11"); default: return ("<unknown mactype>"); } }