10Sstevel@tonic-gate /* 20Sstevel@tonic-gate * CDDL HEADER START 30Sstevel@tonic-gate * 40Sstevel@tonic-gate * The contents of this file are subject to the terms of the 50Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 60Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 70Sstevel@tonic-gate * with the License. 80Sstevel@tonic-gate * 90Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 100Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 110Sstevel@tonic-gate * See the License for the specific language governing permissions 120Sstevel@tonic-gate * and limitations under the License. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 150Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 160Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 170Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 180Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 190Sstevel@tonic-gate * 200Sstevel@tonic-gate * CDDL HEADER END 210Sstevel@tonic-gate */ 220Sstevel@tonic-gate /* 230Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 240Sstevel@tonic-gate * Use is subject to license terms. 250Sstevel@tonic-gate */ 260Sstevel@tonic-gate 270Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 280Sstevel@tonic-gate 290Sstevel@tonic-gate /* 300Sstevel@tonic-gate * Data-Link Driver 310Sstevel@tonic-gate */ 320Sstevel@tonic-gate 330Sstevel@tonic-gate #include <sys/types.h> 340Sstevel@tonic-gate #include <sys/debug.h> 350Sstevel@tonic-gate #include <sys/sysmacros.h> 360Sstevel@tonic-gate #include <sys/stream.h> 370Sstevel@tonic-gate #include <sys/ddi.h> 380Sstevel@tonic-gate #include <sys/sunddi.h> 390Sstevel@tonic-gate #include <sys/strsun.h> 400Sstevel@tonic-gate #include <sys/dlpi.h> 410Sstevel@tonic-gate #include <netinet/in.h> 420Sstevel@tonic-gate #include <sys/sdt.h> 430Sstevel@tonic-gate #include <sys/strsubr.h> 440Sstevel@tonic-gate #include <sys/vlan.h> 450Sstevel@tonic-gate #include <sys/mac.h> 460Sstevel@tonic-gate #include <sys/dls.h> 470Sstevel@tonic-gate #include <sys/dld.h> 480Sstevel@tonic-gate #include <sys/dld_impl.h> 490Sstevel@tonic-gate 500Sstevel@tonic-gate typedef boolean_t proto_reqfunc_t(dld_str_t *, union DL_primitives *, mblk_t *); 510Sstevel@tonic-gate 520Sstevel@tonic-gate static proto_reqfunc_t proto_info_req, proto_attach_req, proto_detach_req, 530Sstevel@tonic-gate proto_bind_req, proto_unbind_req, proto_promiscon_req, proto_promiscoff_req, 540Sstevel@tonic-gate proto_enabmulti_req, proto_disabmulti_req, proto_physaddr_req, 550Sstevel@tonic-gate proto_setphysaddr_req, proto_udqos_req, proto_req, proto_capability_req, 560Sstevel@tonic-gate proto_notify_req, proto_unitdata_req, proto_passive_req; 570Sstevel@tonic-gate 58*269Sericheng static void proto_poll_disable(dld_str_t *); 59*269Sericheng static boolean_t proto_poll_enable(dld_str_t *, dl_capab_poll_t *); 60*269Sericheng static boolean_t proto_capability_advertise(dld_str_t *, mblk_t *); 610Sstevel@tonic-gate 620Sstevel@tonic-gate #define DL_ACK_PENDING(state) \ 630Sstevel@tonic-gate ((state) == DL_ATTACH_PENDING || \ 640Sstevel@tonic-gate (state) == DL_DETACH_PENDING || \ 650Sstevel@tonic-gate (state) == DL_BIND_PENDING || \ 660Sstevel@tonic-gate (state) == DL_UNBIND_PENDING) 670Sstevel@tonic-gate 680Sstevel@tonic-gate /* 69*269Sericheng * Process a DLPI protocol message. 70*269Sericheng * The primitives DL_BIND_REQ, DL_ENABMULTI_REQ, DL_PROMISCON_REQ, 71*269Sericheng * DL_SET_PHYS_ADDR_REQ put the data link below our dld_str_t into an 72*269Sericheng * 'active' state. The primitive DL_PASSIVE_REQ marks our dld_str_t 73*269Sericheng * as 'passive' and forbids it from being subsequently made 'active' 74*269Sericheng * by the above primitives. 750Sstevel@tonic-gate */ 760Sstevel@tonic-gate void 770Sstevel@tonic-gate dld_proto(dld_str_t *dsp, mblk_t *mp) 780Sstevel@tonic-gate { 790Sstevel@tonic-gate union DL_primitives *udlp; 800Sstevel@tonic-gate t_uscalar_t prim; 810Sstevel@tonic-gate 820Sstevel@tonic-gate if (MBLKL(mp) < sizeof (t_uscalar_t)) { 830Sstevel@tonic-gate freemsg(mp); 840Sstevel@tonic-gate return; 850Sstevel@tonic-gate } 860Sstevel@tonic-gate 870Sstevel@tonic-gate udlp = (union DL_primitives *)mp->b_rptr; 880Sstevel@tonic-gate prim = udlp->dl_primitive; 890Sstevel@tonic-gate 90*269Sericheng switch (prim) { 91*269Sericheng case DL_INFO_REQ: 92*269Sericheng (void) proto_info_req(dsp, udlp, mp); 93*269Sericheng break; 94*269Sericheng case DL_BIND_REQ: 95*269Sericheng (void) proto_bind_req(dsp, udlp, mp); 96*269Sericheng break; 97*269Sericheng case DL_UNBIND_REQ: 98*269Sericheng (void) proto_unbind_req(dsp, udlp, mp); 99*269Sericheng break; 100*269Sericheng case DL_UNITDATA_REQ: 101*269Sericheng (void) proto_unitdata_req(dsp, udlp, mp); 102*269Sericheng break; 103*269Sericheng case DL_UDQOS_REQ: 104*269Sericheng (void) proto_udqos_req(dsp, udlp, mp); 105*269Sericheng break; 106*269Sericheng case DL_ATTACH_REQ: 107*269Sericheng (void) proto_attach_req(dsp, udlp, mp); 108*269Sericheng break; 109*269Sericheng case DL_DETACH_REQ: 110*269Sericheng (void) proto_detach_req(dsp, udlp, mp); 111*269Sericheng break; 112*269Sericheng case DL_ENABMULTI_REQ: 113*269Sericheng (void) proto_enabmulti_req(dsp, udlp, mp); 114*269Sericheng break; 115*269Sericheng case DL_DISABMULTI_REQ: 116*269Sericheng (void) proto_disabmulti_req(dsp, udlp, mp); 117*269Sericheng break; 118*269Sericheng case DL_PROMISCON_REQ: 119*269Sericheng (void) proto_promiscon_req(dsp, udlp, mp); 120*269Sericheng break; 121*269Sericheng case DL_PROMISCOFF_REQ: 122*269Sericheng (void) proto_promiscoff_req(dsp, udlp, mp); 123*269Sericheng break; 124*269Sericheng case DL_PHYS_ADDR_REQ: 125*269Sericheng (void) proto_physaddr_req(dsp, udlp, mp); 126*269Sericheng break; 127*269Sericheng case DL_SET_PHYS_ADDR_REQ: 128*269Sericheng (void) proto_setphysaddr_req(dsp, udlp, mp); 129*269Sericheng break; 130*269Sericheng case DL_NOTIFY_REQ: 131*269Sericheng (void) proto_notify_req(dsp, udlp, mp); 132*269Sericheng break; 133*269Sericheng case DL_CAPABILITY_REQ: 134*269Sericheng (void) proto_capability_req(dsp, udlp, mp); 135*269Sericheng break; 136*269Sericheng case DL_PASSIVE_REQ: 137*269Sericheng (void) proto_passive_req(dsp, udlp, mp); 138*269Sericheng break; 139*269Sericheng default: 140*269Sericheng (void) proto_req(dsp, udlp, mp); 141*269Sericheng break; 1420Sstevel@tonic-gate } 1430Sstevel@tonic-gate } 1440Sstevel@tonic-gate 1450Sstevel@tonic-gate /* 146*269Sericheng * Finish any pending operations. At this moment we are single-threaded, 147*269Sericheng * hence there is no need to hold ds_lock as writer because we're already 148*269Sericheng * exclusive. 1490Sstevel@tonic-gate */ 150*269Sericheng void 151*269Sericheng dld_finish_pending_ops(dld_str_t *dsp) 1520Sstevel@tonic-gate { 153*269Sericheng ASSERT(MUTEX_HELD(&dsp->ds_thr_lock)); 154*269Sericheng ASSERT(dsp->ds_thr == 0); 1550Sstevel@tonic-gate 156*269Sericheng /* Pending DL_DETACH_REQ? */ 157*269Sericheng if (dsp->ds_detach_req != NULL) { 158*269Sericheng mblk_t *mp; 1590Sstevel@tonic-gate 160*269Sericheng ASSERT(dsp->ds_dlstate == DL_DETACH_PENDING); 161*269Sericheng dld_str_detach(dsp); 1620Sstevel@tonic-gate 163*269Sericheng mp = dsp->ds_detach_req; 164*269Sericheng dsp->ds_detach_req = NULL; 1650Sstevel@tonic-gate 166*269Sericheng mutex_exit(&dsp->ds_thr_lock); 167*269Sericheng dlokack(dsp->ds_wq, mp, DL_DETACH_REQ); 168*269Sericheng } else { 169*269Sericheng mutex_exit(&dsp->ds_thr_lock); 1700Sstevel@tonic-gate } 1710Sstevel@tonic-gate } 1720Sstevel@tonic-gate 173*269Sericheng #define NEG(x) -(x) 1740Sstevel@tonic-gate 1750Sstevel@tonic-gate typedef struct dl_info_ack_wrapper { 1760Sstevel@tonic-gate dl_info_ack_t dl_info; 1770Sstevel@tonic-gate uint8_t dl_addr[MAXADDRLEN + sizeof (uint16_t)]; 1780Sstevel@tonic-gate uint8_t dl_brdcst_addr[MAXADDRLEN]; 1790Sstevel@tonic-gate dl_qos_cl_range1_t dl_qos_range1; 1800Sstevel@tonic-gate dl_qos_cl_sel1_t dl_qos_sel1; 1810Sstevel@tonic-gate } dl_info_ack_wrapper_t; 1820Sstevel@tonic-gate 1830Sstevel@tonic-gate /* 184*269Sericheng * DL_INFO_REQ 1850Sstevel@tonic-gate */ 186*269Sericheng /*ARGSUSED*/ 187*269Sericheng static boolean_t 188*269Sericheng proto_info_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1890Sstevel@tonic-gate { 1900Sstevel@tonic-gate dl_info_ack_wrapper_t *dlwp; 1910Sstevel@tonic-gate dl_info_ack_t *dlp; 1920Sstevel@tonic-gate dl_qos_cl_sel1_t *selp; 1930Sstevel@tonic-gate dl_qos_cl_range1_t *rangep; 1940Sstevel@tonic-gate uint8_t *addr; 1950Sstevel@tonic-gate uint8_t *brdcst_addr; 1960Sstevel@tonic-gate uint_t addr_length; 1970Sstevel@tonic-gate uint_t sap_length; 198*269Sericheng mac_info_t minfo; 199*269Sericheng mac_info_t *minfop; 200*269Sericheng queue_t *q = dsp->ds_wq; 2010Sstevel@tonic-gate 2020Sstevel@tonic-gate /* 2030Sstevel@tonic-gate * Swap the request message for one large enough to contain the 2040Sstevel@tonic-gate * wrapper structure defined above. 2050Sstevel@tonic-gate */ 206*269Sericheng if ((mp = mexchange(q, mp, sizeof (dl_info_ack_wrapper_t), 2070Sstevel@tonic-gate M_PCPROTO, 0)) == NULL) 208*269Sericheng return (B_FALSE); 209*269Sericheng 210*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 2110Sstevel@tonic-gate 2120Sstevel@tonic-gate bzero(mp->b_rptr, sizeof (dl_info_ack_wrapper_t)); 2130Sstevel@tonic-gate dlwp = (dl_info_ack_wrapper_t *)mp->b_rptr; 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate dlp = &(dlwp->dl_info); 2160Sstevel@tonic-gate ASSERT(dlp == (dl_info_ack_t *)mp->b_rptr); 2170Sstevel@tonic-gate 2180Sstevel@tonic-gate dlp->dl_primitive = DL_INFO_ACK; 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate /* 2210Sstevel@tonic-gate * Set up the sub-structure pointers. 2220Sstevel@tonic-gate */ 2230Sstevel@tonic-gate addr = dlwp->dl_addr; 2240Sstevel@tonic-gate brdcst_addr = dlwp->dl_brdcst_addr; 2250Sstevel@tonic-gate rangep = &(dlwp->dl_qos_range1); 2260Sstevel@tonic-gate selp = &(dlwp->dl_qos_sel1); 2270Sstevel@tonic-gate 2280Sstevel@tonic-gate /* 2290Sstevel@tonic-gate * This driver supports only version 2 connectionless DLPI provider 2300Sstevel@tonic-gate * nodes. 2310Sstevel@tonic-gate */ 2320Sstevel@tonic-gate dlp->dl_service_mode = DL_CLDLS; 2330Sstevel@tonic-gate dlp->dl_version = DL_VERSION_2; 2340Sstevel@tonic-gate 2350Sstevel@tonic-gate /* 236*269Sericheng * Set the style of the provider 2370Sstevel@tonic-gate */ 238*269Sericheng dlp->dl_provider_style = dsp->ds_style; 2390Sstevel@tonic-gate ASSERT(dlp->dl_provider_style == DL_STYLE1 || 2400Sstevel@tonic-gate dlp->dl_provider_style == DL_STYLE2); 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate /* 2430Sstevel@tonic-gate * Set the current DLPI state. 2440Sstevel@tonic-gate */ 2450Sstevel@tonic-gate dlp->dl_current_state = dsp->ds_dlstate; 2460Sstevel@tonic-gate 2470Sstevel@tonic-gate /* 248*269Sericheng * Gratuitously set the media type. This is to deal with modules 249*269Sericheng * that assume the media type is known prior to DL_ATTACH_REQ 2500Sstevel@tonic-gate * being completed. 2510Sstevel@tonic-gate */ 2520Sstevel@tonic-gate dlp->dl_mac_type = DL_ETHER; 2530Sstevel@tonic-gate 2540Sstevel@tonic-gate /* 255*269Sericheng * If the stream is not at least attached we try to retrieve the 256*269Sericheng * mac_info using mac_info_get() 2570Sstevel@tonic-gate */ 2580Sstevel@tonic-gate if (dsp->ds_dlstate == DL_UNATTACHED || 2590Sstevel@tonic-gate dsp->ds_dlstate == DL_ATTACH_PENDING || 260*269Sericheng dsp->ds_dlstate == DL_DETACH_PENDING) { 261*269Sericheng if (!mac_info_get(ddi_major_to_name(dsp->ds_major), &minfo)) { 262*269Sericheng /* 263*269Sericheng * Cannot find mac_info. giving up. 264*269Sericheng */ 265*269Sericheng goto done; 266*269Sericheng } 267*269Sericheng minfop = &minfo; 268*269Sericheng } else { 269*269Sericheng minfop = (mac_info_t *)dsp->ds_mip; 270*269Sericheng } 2710Sstevel@tonic-gate 2720Sstevel@tonic-gate /* 2730Sstevel@tonic-gate * Set the media type (properly this time). 2740Sstevel@tonic-gate */ 275*269Sericheng dlp->dl_mac_type = minfop->mi_media; 2760Sstevel@tonic-gate 2770Sstevel@tonic-gate /* 2780Sstevel@tonic-gate * Set the DLSAP length. We only support 16 bit values and they 2790Sstevel@tonic-gate * appear after the MAC address portion of DLSAP addresses. 2800Sstevel@tonic-gate */ 2810Sstevel@tonic-gate sap_length = sizeof (uint16_t); 2820Sstevel@tonic-gate dlp->dl_sap_length = NEG(sap_length); 2830Sstevel@tonic-gate 2840Sstevel@tonic-gate /* 2850Sstevel@tonic-gate * Set the minimum and maximum payload sizes. 2860Sstevel@tonic-gate */ 287*269Sericheng dlp->dl_min_sdu = minfop->mi_sdu_min; 288*269Sericheng dlp->dl_max_sdu = minfop->mi_sdu_max; 2890Sstevel@tonic-gate 290*269Sericheng addr_length = minfop->mi_addr_length; 2910Sstevel@tonic-gate ASSERT(addr_length != 0); 2920Sstevel@tonic-gate 2930Sstevel@tonic-gate /* 2940Sstevel@tonic-gate * Copy in the media broadcast address. 2950Sstevel@tonic-gate */ 2960Sstevel@tonic-gate dlp->dl_brdcst_addr_offset = (uintptr_t)brdcst_addr - (uintptr_t)dlp; 297*269Sericheng bcopy(minfop->mi_brdcst_addr, brdcst_addr, addr_length); 2980Sstevel@tonic-gate dlp->dl_brdcst_addr_length = addr_length; 2990Sstevel@tonic-gate 3000Sstevel@tonic-gate /* 3010Sstevel@tonic-gate * We only support QoS information for VLAN interfaces. 3020Sstevel@tonic-gate */ 3030Sstevel@tonic-gate if (dsp->ds_vid != VLAN_ID_NONE) { 3040Sstevel@tonic-gate dlp->dl_qos_range_offset = (uintptr_t)rangep - (uintptr_t)dlp; 3050Sstevel@tonic-gate dlp->dl_qos_range_length = sizeof (dl_qos_cl_range1_t); 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate rangep->dl_qos_type = DL_QOS_CL_RANGE1; 3080Sstevel@tonic-gate rangep->dl_trans_delay.dl_target_value = DL_UNKNOWN; 3090Sstevel@tonic-gate rangep->dl_trans_delay.dl_accept_value = DL_UNKNOWN; 3100Sstevel@tonic-gate rangep->dl_protection.dl_min = DL_UNKNOWN; 3110Sstevel@tonic-gate rangep->dl_protection.dl_max = DL_UNKNOWN; 3120Sstevel@tonic-gate rangep->dl_residual_error = DL_UNKNOWN; 3130Sstevel@tonic-gate 3140Sstevel@tonic-gate /* 3150Sstevel@tonic-gate * Specify the supported range of priorities. 3160Sstevel@tonic-gate */ 3170Sstevel@tonic-gate rangep->dl_priority.dl_min = 0; 3180Sstevel@tonic-gate rangep->dl_priority.dl_max = (1 << VLAN_PRI_SIZE) - 1; 3190Sstevel@tonic-gate 3200Sstevel@tonic-gate dlp->dl_qos_offset = (uintptr_t)selp - (uintptr_t)dlp; 3210Sstevel@tonic-gate dlp->dl_qos_length = sizeof (dl_qos_cl_sel1_t); 3220Sstevel@tonic-gate 3230Sstevel@tonic-gate selp->dl_qos_type = DL_QOS_CL_SEL1; 3240Sstevel@tonic-gate selp->dl_trans_delay = DL_UNKNOWN; 3250Sstevel@tonic-gate selp->dl_protection = DL_UNKNOWN; 3260Sstevel@tonic-gate selp->dl_residual_error = DL_UNKNOWN; 3270Sstevel@tonic-gate 3280Sstevel@tonic-gate /* 3290Sstevel@tonic-gate * Specify the current priority (which can be changed by 3300Sstevel@tonic-gate * the DL_UDQOS_REQ primitive). 3310Sstevel@tonic-gate */ 3320Sstevel@tonic-gate selp->dl_priority = dsp->ds_pri; 3330Sstevel@tonic-gate } else { 3340Sstevel@tonic-gate /* 3350Sstevel@tonic-gate * Shorten the buffer to lose the unused QoS information 336*269Sericheng * structures. 3370Sstevel@tonic-gate */ 3380Sstevel@tonic-gate mp->b_wptr = (uint8_t *)rangep; 3390Sstevel@tonic-gate } 3400Sstevel@tonic-gate 3410Sstevel@tonic-gate dlp->dl_addr_length = addr_length + sizeof (uint16_t); 3420Sstevel@tonic-gate if (dsp->ds_dlstate == DL_IDLE) { 3430Sstevel@tonic-gate /* 3440Sstevel@tonic-gate * The stream is bound. Therefore we can formulate a valid 3450Sstevel@tonic-gate * DLSAP address. 3460Sstevel@tonic-gate */ 3470Sstevel@tonic-gate dlp->dl_addr_offset = (uintptr_t)addr - (uintptr_t)dlp; 3480Sstevel@tonic-gate bcopy(dsp->ds_curr_addr, addr, addr_length); 3490Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 3500Sstevel@tonic-gate } 3510Sstevel@tonic-gate 3520Sstevel@tonic-gate done: 3530Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_offset != 0, dlp->dl_qos_length != 0)); 3540Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_qos_range_offset != 0, 3550Sstevel@tonic-gate dlp->dl_qos_range_length != 0)); 3560Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_addr_offset != 0, dlp->dl_addr_length != 0)); 3570Sstevel@tonic-gate ASSERT(IMPLY(dlp->dl_brdcst_addr_offset != 0, 3580Sstevel@tonic-gate dlp->dl_brdcst_addr_length != 0)); 3590Sstevel@tonic-gate 360*269Sericheng rw_exit(&dsp->ds_lock); 361*269Sericheng 362*269Sericheng qreply(q, mp); 363*269Sericheng return (B_TRUE); 364*269Sericheng } 365*269Sericheng 366*269Sericheng /* 367*269Sericheng * DL_ATTACH_REQ 368*269Sericheng */ 369*269Sericheng static boolean_t 370*269Sericheng proto_attach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 371*269Sericheng { 372*269Sericheng dl_attach_req_t *dlp = (dl_attach_req_t *)udlp; 373*269Sericheng int err = 0; 374*269Sericheng t_uscalar_t dl_err; 375*269Sericheng queue_t *q = dsp->ds_wq; 376*269Sericheng 377*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 378*269Sericheng 379*269Sericheng if (MBLKL(mp) < sizeof (dl_attach_req_t) || 380*269Sericheng dlp->dl_ppa < 0 || dsp->ds_style == DL_STYLE1) { 381*269Sericheng dl_err = DL_BADPRIM; 382*269Sericheng goto failed; 383*269Sericheng } 384*269Sericheng 385*269Sericheng if (dsp->ds_dlstate != DL_UNATTACHED) { 386*269Sericheng dl_err = DL_OUTSTATE; 387*269Sericheng goto failed; 388*269Sericheng } 389*269Sericheng 390*269Sericheng dsp->ds_dlstate = DL_ATTACH_PENDING; 391*269Sericheng 392*269Sericheng err = dld_str_attach(dsp, dlp->dl_ppa); 393*269Sericheng if (err != 0) { 394*269Sericheng switch (err) { 395*269Sericheng case ENOENT: 396*269Sericheng dl_err = DL_BADPPA; 397*269Sericheng err = 0; 398*269Sericheng break; 399*269Sericheng default: 400*269Sericheng dl_err = DL_SYSERR; 401*269Sericheng break; 402*269Sericheng } 403*269Sericheng dsp->ds_dlstate = DL_UNATTACHED; 404*269Sericheng goto failed; 405*269Sericheng } 406*269Sericheng ASSERT(dsp->ds_dlstate == DL_UNBOUND); 407*269Sericheng rw_exit(&dsp->ds_lock); 408*269Sericheng 409*269Sericheng dlokack(q, mp, DL_ATTACH_REQ); 410*269Sericheng return (B_TRUE); 411*269Sericheng failed: 412*269Sericheng rw_exit(&dsp->ds_lock); 413*269Sericheng dlerrorack(q, mp, DL_ATTACH_REQ, dl_err, (t_uscalar_t)err); 414*269Sericheng return (B_FALSE); 4150Sstevel@tonic-gate } 4160Sstevel@tonic-gate 4170Sstevel@tonic-gate /* 418*269Sericheng * DL_DETACH_REQ 4190Sstevel@tonic-gate */ 420*269Sericheng /*ARGSUSED*/ 421*269Sericheng static boolean_t 422*269Sericheng proto_detach_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4230Sstevel@tonic-gate { 424*269Sericheng queue_t *q = dsp->ds_wq; 425*269Sericheng t_uscalar_t dl_err; 4260Sstevel@tonic-gate 427*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 4280Sstevel@tonic-gate 429*269Sericheng if (MBLKL(mp) < sizeof (dl_detach_req_t)) { 430*269Sericheng dl_err = DL_BADPRIM; 431*269Sericheng goto failed; 432*269Sericheng } 4330Sstevel@tonic-gate 434*269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 435*269Sericheng dl_err = DL_OUTSTATE; 436*269Sericheng goto failed; 4370Sstevel@tonic-gate } 4380Sstevel@tonic-gate 439*269Sericheng if (dsp->ds_style == DL_STYLE1) { 440*269Sericheng dl_err = DL_BADPRIM; 441*269Sericheng goto failed; 442*269Sericheng } 443*269Sericheng 444*269Sericheng dsp->ds_dlstate = DL_DETACH_PENDING; 445*269Sericheng 446*269Sericheng /* 447*269Sericheng * Complete the detach when the driver is single-threaded. 448*269Sericheng */ 449*269Sericheng mutex_enter(&dsp->ds_thr_lock); 450*269Sericheng ASSERT(dsp->ds_detach_req == NULL); 451*269Sericheng dsp->ds_detach_req = mp; 452*269Sericheng mutex_exit(&dsp->ds_thr_lock); 453*269Sericheng rw_exit(&dsp->ds_lock); 454*269Sericheng 455*269Sericheng return (B_TRUE); 456*269Sericheng failed: 457*269Sericheng rw_exit(&dsp->ds_lock); 458*269Sericheng dlerrorack(q, mp, DL_DETACH_REQ, dl_err, 0); 459*269Sericheng return (B_FALSE); 4600Sstevel@tonic-gate } 4610Sstevel@tonic-gate 4620Sstevel@tonic-gate /* 463*269Sericheng * DL_BIND_REQ 4640Sstevel@tonic-gate */ 465*269Sericheng static boolean_t 466*269Sericheng proto_bind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 4670Sstevel@tonic-gate { 468*269Sericheng dl_bind_req_t *dlp = (dl_bind_req_t *)udlp; 469*269Sericheng int err = 0; 470*269Sericheng uint8_t addr[MAXADDRLEN]; 471*269Sericheng uint_t addr_length; 472*269Sericheng t_uscalar_t dl_err; 473*269Sericheng t_scalar_t sap; 474*269Sericheng queue_t *q = dsp->ds_wq; 475*269Sericheng 476*269Sericheng if (MBLKL(mp) < sizeof (dl_bind_req_t)) { 477*269Sericheng dl_err = DL_BADPRIM; 478*269Sericheng goto failed; 479*269Sericheng } 480*269Sericheng 481*269Sericheng if (dlp->dl_xidtest_flg != 0) { 482*269Sericheng dl_err = DL_NOAUTO; 483*269Sericheng goto failed; 484*269Sericheng } 485*269Sericheng 486*269Sericheng if (dlp->dl_service_mode != DL_CLDLS) { 487*269Sericheng dl_err = DL_UNSUPPORTED; 488*269Sericheng goto failed; 489*269Sericheng } 490*269Sericheng 491*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 492*269Sericheng 493*269Sericheng if (dsp->ds_dlstate != DL_UNBOUND) { 494*269Sericheng dl_err = DL_OUTSTATE; 495*269Sericheng goto failed; 496*269Sericheng } 4970Sstevel@tonic-gate 498*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 499*269Sericheng !dls_active_set(dsp->ds_dc)) { 500*269Sericheng dl_err = DL_SYSERR; 501*269Sericheng err = EBUSY; 502*269Sericheng goto failed; 503*269Sericheng } 504*269Sericheng 505*269Sericheng dsp->ds_dlstate = DL_BIND_PENDING; 506*269Sericheng /* 507*269Sericheng * Set the receive callback. 508*269Sericheng */ 509*269Sericheng dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_RAW) ? 510*269Sericheng dld_str_rx_raw : dld_str_rx_unitdata, dsp); 5110Sstevel@tonic-gate 512*269Sericheng /* 513*269Sericheng * Bind the channel such that it can receive packets. 514*269Sericheng */ 515*269Sericheng sap = dsp->ds_sap = dlp->dl_sap; 516*269Sericheng err = dls_bind(dsp->ds_dc, dlp->dl_sap); 517*269Sericheng if (err != 0) { 518*269Sericheng switch (err) { 519*269Sericheng case EINVAL: 520*269Sericheng dl_err = DL_BADADDR; 521*269Sericheng err = 0; 522*269Sericheng break; 523*269Sericheng default: 524*269Sericheng dl_err = DL_SYSERR; 525*269Sericheng break; 526*269Sericheng } 527*269Sericheng dsp->ds_dlstate = DL_UNBOUND; 528*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 529*269Sericheng dls_active_clear(dsp->ds_dc); 530*269Sericheng 5310Sstevel@tonic-gate goto failed; 532*269Sericheng } 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate /* 5350Sstevel@tonic-gate * Copy in MAC address. 5360Sstevel@tonic-gate */ 5370Sstevel@tonic-gate addr_length = dsp->ds_mip->mi_addr_length; 5380Sstevel@tonic-gate bcopy(dsp->ds_curr_addr, addr, addr_length); 5390Sstevel@tonic-gate 5400Sstevel@tonic-gate /* 5410Sstevel@tonic-gate * Copy in the DLSAP. 5420Sstevel@tonic-gate */ 5430Sstevel@tonic-gate *(uint16_t *)(addr + addr_length) = dsp->ds_sap; 5440Sstevel@tonic-gate addr_length += sizeof (uint16_t); 5450Sstevel@tonic-gate 5460Sstevel@tonic-gate dsp->ds_dlstate = DL_IDLE; 547*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 548*269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 5490Sstevel@tonic-gate 550*269Sericheng rw_exit(&dsp->ds_lock); 551*269Sericheng 552*269Sericheng dlbindack(q, mp, sap, (void *)addr, addr_length, 0, 0); 553*269Sericheng return (B_TRUE); 5540Sstevel@tonic-gate failed: 555*269Sericheng rw_exit(&dsp->ds_lock); 556*269Sericheng dlerrorack(q, mp, DL_BIND_REQ, dl_err, (t_uscalar_t)err); 557*269Sericheng return (B_FALSE); 5580Sstevel@tonic-gate } 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate /* 561*269Sericheng * DL_UNBIND_REQ 5620Sstevel@tonic-gate */ 563*269Sericheng /*ARGSUSED*/ 564*269Sericheng static boolean_t 565*269Sericheng proto_unbind_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 5660Sstevel@tonic-gate { 567*269Sericheng queue_t *q = dsp->ds_wq; 568*269Sericheng t_uscalar_t dl_err; 569*269Sericheng 570*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 571*269Sericheng 572*269Sericheng if (MBLKL(mp) < sizeof (dl_unbind_req_t)) { 573*269Sericheng dl_err = DL_BADPRIM; 574*269Sericheng goto failed; 575*269Sericheng } 576*269Sericheng 577*269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 578*269Sericheng dl_err = DL_OUTSTATE; 579*269Sericheng goto failed; 580*269Sericheng } 581*269Sericheng 582*269Sericheng dsp->ds_dlstate = DL_UNBIND_PENDING; 583*269Sericheng 584*269Sericheng /* 585*269Sericheng * Flush any remaining packets scheduled for transmission. 586*269Sericheng */ 587*269Sericheng dld_tx_flush(dsp); 588*269Sericheng 589*269Sericheng /* 590*269Sericheng * Unbind the channel to stop packets being received. 591*269Sericheng */ 592*269Sericheng dls_unbind(dsp->ds_dc); 593*269Sericheng 594*269Sericheng /* 595*269Sericheng * Disable polling mode, if it is enabled. 596*269Sericheng */ 597*269Sericheng proto_poll_disable(dsp); 598*269Sericheng 599*269Sericheng /* 600*269Sericheng * Clear the receive callback. 601*269Sericheng */ 602*269Sericheng dls_rx_set(dsp->ds_dc, NULL, NULL); 603*269Sericheng 604*269Sericheng /* 605*269Sericheng * Set the mode back to the default (unitdata). 606*269Sericheng */ 607*269Sericheng dsp->ds_mode = DLD_UNITDATA; 608*269Sericheng 6090Sstevel@tonic-gate dsp->ds_dlstate = DL_UNBOUND; 610*269Sericheng rw_exit(&dsp->ds_lock); 611*269Sericheng 612*269Sericheng dlokack(q, mp, DL_UNBIND_REQ); 613*269Sericheng return (B_TRUE); 614*269Sericheng failed: 615*269Sericheng rw_exit(&dsp->ds_lock); 616*269Sericheng dlerrorack(q, mp, DL_UNBIND_REQ, dl_err, 0); 617*269Sericheng return (B_FALSE); 6180Sstevel@tonic-gate } 6190Sstevel@tonic-gate 6200Sstevel@tonic-gate /* 621*269Sericheng * DL_PROMISCON_REQ 6220Sstevel@tonic-gate */ 623*269Sericheng static boolean_t 624*269Sericheng proto_promiscon_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 6250Sstevel@tonic-gate { 626*269Sericheng dl_promiscon_req_t *dlp = (dl_promiscon_req_t *)udlp; 627*269Sericheng int err = 0; 628*269Sericheng t_uscalar_t dl_err; 629*269Sericheng uint32_t promisc_saved; 630*269Sericheng queue_t *q = dsp->ds_wq; 631*269Sericheng 632*269Sericheng if (MBLKL(mp) < sizeof (dl_promiscon_req_t)) { 633*269Sericheng dl_err = DL_BADPRIM; 634*269Sericheng goto failed; 635*269Sericheng } 636*269Sericheng 637*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 638*269Sericheng 639*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 640*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 641*269Sericheng dl_err = DL_OUTSTATE; 6420Sstevel@tonic-gate goto failed; 643*269Sericheng } 6440Sstevel@tonic-gate 645*269Sericheng promisc_saved = dsp->ds_promisc; 646*269Sericheng switch (dlp->dl_level) { 647*269Sericheng case DL_PROMISC_SAP: 648*269Sericheng dsp->ds_promisc |= DLS_PROMISC_SAP; 649*269Sericheng break; 650*269Sericheng 651*269Sericheng case DL_PROMISC_MULTI: 652*269Sericheng dsp->ds_promisc |= DLS_PROMISC_MULTI; 653*269Sericheng break; 654*269Sericheng 655*269Sericheng case DL_PROMISC_PHYS: 656*269Sericheng dsp->ds_promisc |= DLS_PROMISC_PHYS; 657*269Sericheng break; 658*269Sericheng 659*269Sericheng default: 660*269Sericheng dl_err = DL_NOTSUPPORTED; 661*269Sericheng goto failed; 662*269Sericheng } 6630Sstevel@tonic-gate 664*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 665*269Sericheng !dls_active_set(dsp->ds_dc)) { 666*269Sericheng dsp->ds_promisc = promisc_saved; 667*269Sericheng dl_err = DL_SYSERR; 668*269Sericheng err = EBUSY; 669*269Sericheng goto failed; 670*269Sericheng } 671*269Sericheng 672*269Sericheng /* 673*269Sericheng * Adjust channel promiscuity. 674*269Sericheng */ 675*269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 676*269Sericheng if (err != 0) { 677*269Sericheng dl_err = DL_SYSERR; 678*269Sericheng dsp->ds_promisc = promisc_saved; 679*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 680*269Sericheng dls_active_clear(dsp->ds_dc); 681*269Sericheng 682*269Sericheng goto failed; 683*269Sericheng } 684*269Sericheng 685*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 686*269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 687*269Sericheng 688*269Sericheng rw_exit(&dsp->ds_lock); 689*269Sericheng dlokack(q, mp, DL_PROMISCON_REQ); 690*269Sericheng return (B_TRUE); 6910Sstevel@tonic-gate failed: 692*269Sericheng rw_exit(&dsp->ds_lock); 693*269Sericheng dlerrorack(q, mp, DL_PROMISCON_REQ, dl_err, (t_uscalar_t)err); 694*269Sericheng return (B_FALSE); 6950Sstevel@tonic-gate } 6960Sstevel@tonic-gate 6970Sstevel@tonic-gate /* 698*269Sericheng * DL_PROMISCOFF_REQ 6990Sstevel@tonic-gate */ 700*269Sericheng static boolean_t 701*269Sericheng proto_promiscoff_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 7020Sstevel@tonic-gate { 703*269Sericheng dl_promiscoff_req_t *dlp = (dl_promiscoff_req_t *)udlp; 704*269Sericheng int err = 0; 705*269Sericheng t_uscalar_t dl_err; 706*269Sericheng uint32_t promisc_saved; 707*269Sericheng queue_t *q = dsp->ds_wq; 708*269Sericheng 709*269Sericheng 710*269Sericheng if (MBLKL(mp) < sizeof (dl_promiscoff_req_t)) { 711*269Sericheng dl_err = DL_BADPRIM; 7120Sstevel@tonic-gate goto failed; 713*269Sericheng } 7140Sstevel@tonic-gate 715*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 7160Sstevel@tonic-gate 717*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 718*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 719*269Sericheng dl_err = DL_OUTSTATE; 7200Sstevel@tonic-gate goto failed; 721*269Sericheng } 7220Sstevel@tonic-gate 723*269Sericheng promisc_saved = dsp->ds_promisc; 724*269Sericheng switch (dlp->dl_level) { 725*269Sericheng case DL_PROMISC_SAP: 726*269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_SAP)) { 727*269Sericheng dl_err = DL_NOTENAB; 728*269Sericheng goto failed; 729*269Sericheng } 730*269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_SAP; 7310Sstevel@tonic-gate break; 7320Sstevel@tonic-gate 733*269Sericheng case DL_PROMISC_MULTI: 734*269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_MULTI)) { 735*269Sericheng dl_err = DL_NOTENAB; 736*269Sericheng goto failed; 737*269Sericheng } 738*269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_MULTI; 739*269Sericheng break; 740*269Sericheng 741*269Sericheng case DL_PROMISC_PHYS: 742*269Sericheng if (!(dsp->ds_promisc & DLS_PROMISC_PHYS)) { 743*269Sericheng dl_err = DL_NOTENAB; 744*269Sericheng goto failed; 745*269Sericheng } 746*269Sericheng dsp->ds_promisc &= ~DLS_PROMISC_PHYS; 7470Sstevel@tonic-gate break; 7480Sstevel@tonic-gate 7490Sstevel@tonic-gate default: 750*269Sericheng dl_err = DL_NOTSUPPORTED; 751*269Sericheng goto failed; 752*269Sericheng } 753*269Sericheng 754*269Sericheng /* 755*269Sericheng * Adjust channel promiscuity. 756*269Sericheng */ 757*269Sericheng err = dls_promisc(dsp->ds_dc, dsp->ds_promisc); 758*269Sericheng if (err != 0) { 759*269Sericheng dsp->ds_promisc = promisc_saved; 7600Sstevel@tonic-gate dl_err = DL_SYSERR; 761*269Sericheng goto failed; 762*269Sericheng } 763*269Sericheng 764*269Sericheng rw_exit(&dsp->ds_lock); 765*269Sericheng dlokack(q, mp, DL_PROMISCOFF_REQ); 766*269Sericheng return (B_TRUE); 767*269Sericheng failed: 768*269Sericheng rw_exit(&dsp->ds_lock); 769*269Sericheng dlerrorack(q, mp, DL_PROMISCOFF_REQ, dl_err, (t_uscalar_t)err); 770*269Sericheng return (B_FALSE); 771*269Sericheng } 772*269Sericheng 773*269Sericheng /* 774*269Sericheng * DL_ENABMULTI_REQ 775*269Sericheng */ 776*269Sericheng static boolean_t 777*269Sericheng proto_enabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 778*269Sericheng { 779*269Sericheng dl_enabmulti_req_t *dlp = (dl_enabmulti_req_t *)udlp; 780*269Sericheng int err = 0; 781*269Sericheng t_uscalar_t dl_err; 782*269Sericheng queue_t *q = dsp->ds_wq; 783*269Sericheng 784*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 785*269Sericheng 786*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 787*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 788*269Sericheng dl_err = DL_OUTSTATE; 789*269Sericheng goto failed; 790*269Sericheng } 791*269Sericheng 792*269Sericheng if (MBLKL(mp) < sizeof (dl_enabmulti_req_t) || 793*269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 794*269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 795*269Sericheng dl_err = DL_BADPRIM; 796*269Sericheng goto failed; 797*269Sericheng } 798*269Sericheng 799*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 800*269Sericheng !dls_active_set(dsp->ds_dc)) { 801*269Sericheng dl_err = DL_SYSERR; 802*269Sericheng err = EBUSY; 803*269Sericheng goto failed; 8040Sstevel@tonic-gate } 8050Sstevel@tonic-gate 806*269Sericheng err = dls_multicst_add(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 807*269Sericheng if (err != 0) { 808*269Sericheng switch (err) { 809*269Sericheng case EINVAL: 810*269Sericheng dl_err = DL_BADADDR; 811*269Sericheng err = 0; 812*269Sericheng break; 813*269Sericheng case ENOSPC: 814*269Sericheng dl_err = DL_TOOMANY; 815*269Sericheng err = 0; 816*269Sericheng break; 817*269Sericheng default: 818*269Sericheng dl_err = DL_SYSERR; 819*269Sericheng break; 820*269Sericheng } 821*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 822*269Sericheng dls_active_clear(dsp->ds_dc); 823*269Sericheng 824*269Sericheng goto failed; 825*269Sericheng } 826*269Sericheng 827*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 828*269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 829*269Sericheng 830*269Sericheng rw_exit(&dsp->ds_lock); 831*269Sericheng dlokack(q, mp, DL_ENABMULTI_REQ); 832*269Sericheng return (B_TRUE); 833*269Sericheng failed: 834*269Sericheng rw_exit(&dsp->ds_lock); 835*269Sericheng dlerrorack(q, mp, DL_ENABMULTI_REQ, dl_err, (t_uscalar_t)err); 836*269Sericheng return (B_FALSE); 837*269Sericheng } 838*269Sericheng 839*269Sericheng /* 840*269Sericheng * DL_DISABMULTI_REQ 841*269Sericheng */ 842*269Sericheng static boolean_t 843*269Sericheng proto_disabmulti_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 844*269Sericheng { 845*269Sericheng dl_disabmulti_req_t *dlp = (dl_disabmulti_req_t *)udlp; 846*269Sericheng int err = 0; 847*269Sericheng t_uscalar_t dl_err; 848*269Sericheng queue_t *q = dsp->ds_wq; 849*269Sericheng 850*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 851*269Sericheng 852*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 853*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 854*269Sericheng dl_err = DL_OUTSTATE; 855*269Sericheng goto failed; 856*269Sericheng } 857*269Sericheng 858*269Sericheng if (MBLKL(mp) < sizeof (dl_disabmulti_req_t) || 859*269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 860*269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 861*269Sericheng dl_err = DL_BADPRIM; 862*269Sericheng goto failed; 863*269Sericheng } 864*269Sericheng 865*269Sericheng err = dls_multicst_remove(dsp->ds_dc, mp->b_rptr + dlp->dl_addr_offset); 866*269Sericheng if (err != 0) { 867*269Sericheng switch (err) { 868*269Sericheng case EINVAL: 869*269Sericheng dl_err = DL_BADADDR; 870*269Sericheng err = 0; 871*269Sericheng break; 872*269Sericheng 873*269Sericheng case ENOENT: 874*269Sericheng dl_err = DL_NOTENAB; 875*269Sericheng err = 0; 876*269Sericheng break; 877*269Sericheng 878*269Sericheng default: 879*269Sericheng dl_err = DL_SYSERR; 880*269Sericheng break; 881*269Sericheng } 882*269Sericheng goto failed; 883*269Sericheng } 884*269Sericheng 885*269Sericheng rw_exit(&dsp->ds_lock); 886*269Sericheng dlokack(q, mp, DL_DISABMULTI_REQ); 887*269Sericheng return (B_TRUE); 888*269Sericheng failed: 889*269Sericheng rw_exit(&dsp->ds_lock); 890*269Sericheng dlerrorack(q, mp, DL_DISABMULTI_REQ, dl_err, (t_uscalar_t)err); 891*269Sericheng return (B_FALSE); 8920Sstevel@tonic-gate } 8930Sstevel@tonic-gate 8940Sstevel@tonic-gate /* 895*269Sericheng * DL_PHYS_ADDR_REQ 8960Sstevel@tonic-gate */ 897*269Sericheng static boolean_t 898*269Sericheng proto_physaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 8990Sstevel@tonic-gate { 900*269Sericheng dl_phys_addr_req_t *dlp = (dl_phys_addr_req_t *)udlp; 901*269Sericheng queue_t *q = dsp->ds_wq; 902*269Sericheng t_uscalar_t dl_err; 903*269Sericheng char *addr; 904*269Sericheng uint_t addr_length; 905*269Sericheng 906*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 907*269Sericheng 908*269Sericheng if (MBLKL(mp) < sizeof (dl_phys_addr_req_t)) { 909*269Sericheng dl_err = DL_BADPRIM; 910*269Sericheng goto failed; 911*269Sericheng } 912*269Sericheng 913*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 914*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 915*269Sericheng dl_err = DL_OUTSTATE; 916*269Sericheng goto failed; 917*269Sericheng } 9180Sstevel@tonic-gate 919*269Sericheng if (dlp->dl_addr_type != DL_CURR_PHYS_ADDR && 920*269Sericheng dlp->dl_addr_type != DL_FACT_PHYS_ADDR) { 921*269Sericheng dl_err = DL_UNSUPPORTED; 9220Sstevel@tonic-gate goto failed; 923*269Sericheng } 9240Sstevel@tonic-gate 925*269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 926*269Sericheng addr = kmem_alloc(addr_length, KM_NOSLEEP); 927*269Sericheng if (addr == NULL) { 928*269Sericheng rw_exit(&dsp->ds_lock); 929*269Sericheng merror(q, mp, ENOSR); 930*269Sericheng return (B_FALSE); 931*269Sericheng } 9320Sstevel@tonic-gate 933*269Sericheng /* 934*269Sericheng * Copy out the address before we drop the lock; we don't 935*269Sericheng * want to call dlphysaddrack() while holding ds_lock. 936*269Sericheng */ 937*269Sericheng bcopy((dlp->dl_addr_type == DL_CURR_PHYS_ADDR) ? 938*269Sericheng dsp->ds_curr_addr : dsp->ds_fact_addr, addr, addr_length); 939*269Sericheng 940*269Sericheng rw_exit(&dsp->ds_lock); 941*269Sericheng dlphysaddrack(q, mp, addr, (t_uscalar_t)addr_length); 942*269Sericheng kmem_free(addr, addr_length); 943*269Sericheng return (B_TRUE); 9440Sstevel@tonic-gate failed: 945*269Sericheng rw_exit(&dsp->ds_lock); 946*269Sericheng dlerrorack(q, mp, DL_PHYS_ADDR_REQ, dl_err, 0); 947*269Sericheng return (B_FALSE); 948*269Sericheng } 9490Sstevel@tonic-gate 950*269Sericheng /* 951*269Sericheng * DL_SET_PHYS_ADDR_REQ 952*269Sericheng */ 953*269Sericheng static boolean_t 954*269Sericheng proto_setphysaddr_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 955*269Sericheng { 956*269Sericheng dl_set_phys_addr_req_t *dlp = (dl_set_phys_addr_req_t *)udlp; 957*269Sericheng int err = 0; 958*269Sericheng t_uscalar_t dl_err; 959*269Sericheng queue_t *q = dsp->ds_wq; 9600Sstevel@tonic-gate 961*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 962*269Sericheng 963*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 964*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 965*269Sericheng dl_err = DL_OUTSTATE; 966*269Sericheng goto failed; 967*269Sericheng } 968*269Sericheng 969*269Sericheng if (MBLKL(mp) < sizeof (dl_set_phys_addr_req_t) || 970*269Sericheng !MBLKIN(mp, dlp->dl_addr_offset, dlp->dl_addr_length) || 971*269Sericheng dlp->dl_addr_length != dsp->ds_mip->mi_addr_length) { 972*269Sericheng dl_err = DL_BADPRIM; 973*269Sericheng goto failed; 9740Sstevel@tonic-gate } 9750Sstevel@tonic-gate 976*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED && 977*269Sericheng !dls_active_set(dsp->ds_dc)) { 978*269Sericheng dl_err = DL_SYSERR; 979*269Sericheng err = EBUSY; 980*269Sericheng goto failed; 981*269Sericheng } 982*269Sericheng 983*269Sericheng err = mac_unicst_set(dsp->ds_mh, mp->b_rptr + dlp->dl_addr_offset); 984*269Sericheng if (err != 0) { 985*269Sericheng switch (err) { 986*269Sericheng case EINVAL: 987*269Sericheng dl_err = DL_BADADDR; 988*269Sericheng err = 0; 989*269Sericheng break; 990*269Sericheng 991*269Sericheng default: 992*269Sericheng dl_err = DL_SYSERR; 993*269Sericheng break; 994*269Sericheng } 995*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 996*269Sericheng dls_active_clear(dsp->ds_dc); 997*269Sericheng 998*269Sericheng goto failed; 999*269Sericheng } 1000*269Sericheng if (dsp->ds_passivestate == DLD_UNINITIALIZED) 1001*269Sericheng dsp->ds_passivestate = DLD_ACTIVE; 1002*269Sericheng 1003*269Sericheng rw_exit(&dsp->ds_lock); 1004*269Sericheng dlokack(q, mp, DL_SET_PHYS_ADDR_REQ); 1005*269Sericheng return (B_TRUE); 1006*269Sericheng failed: 1007*269Sericheng rw_exit(&dsp->ds_lock); 1008*269Sericheng dlerrorack(q, mp, DL_SET_PHYS_ADDR_REQ, dl_err, (t_uscalar_t)err); 1009*269Sericheng return (B_FALSE); 1010*269Sericheng } 1011*269Sericheng 1012*269Sericheng /* 1013*269Sericheng * DL_UDQOS_REQ 1014*269Sericheng */ 1015*269Sericheng static boolean_t 1016*269Sericheng proto_udqos_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1017*269Sericheng { 1018*269Sericheng dl_udqos_req_t *dlp = (dl_udqos_req_t *)udlp; 1019*269Sericheng dl_qos_cl_sel1_t *selp; 1020*269Sericheng int off, len; 1021*269Sericheng t_uscalar_t dl_err; 1022*269Sericheng queue_t *q = dsp->ds_wq; 1023*269Sericheng 1024*269Sericheng off = dlp->dl_qos_offset; 1025*269Sericheng len = dlp->dl_qos_length; 1026*269Sericheng 1027*269Sericheng if (MBLKL(mp) < sizeof (dl_udqos_req_t) || !MBLKIN(mp, off, len)) { 1028*269Sericheng dl_err = DL_BADPRIM; 1029*269Sericheng goto failed; 1030*269Sericheng } 1031*269Sericheng 1032*269Sericheng selp = (dl_qos_cl_sel1_t *)(mp->b_rptr + off); 1033*269Sericheng if (selp->dl_qos_type != DL_QOS_CL_SEL1) { 1034*269Sericheng dl_err = DL_BADQOSTYPE; 1035*269Sericheng goto failed; 1036*269Sericheng } 1037*269Sericheng 1038*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1039*269Sericheng 1040*269Sericheng if (dsp->ds_vid == VLAN_ID_NONE || 1041*269Sericheng selp->dl_priority > (1 << VLAN_PRI_SIZE) - 1 || 1042*269Sericheng selp->dl_priority < 0) { 1043*269Sericheng dl_err = DL_BADQOSPARAM; 1044*269Sericheng goto failed; 1045*269Sericheng } 1046*269Sericheng 1047*269Sericheng dsp->ds_pri = selp->dl_priority; 1048*269Sericheng 1049*269Sericheng rw_exit(&dsp->ds_lock); 1050*269Sericheng dlokack(q, mp, DL_UDQOS_REQ); 1051*269Sericheng return (B_TRUE); 1052*269Sericheng failed: 1053*269Sericheng rw_exit(&dsp->ds_lock); 1054*269Sericheng dlerrorack(q, mp, DL_UDQOS_REQ, dl_err, 0); 1055*269Sericheng return (B_FALSE); 10560Sstevel@tonic-gate } 10570Sstevel@tonic-gate 10580Sstevel@tonic-gate /* 1059*269Sericheng * DL_CAPABILITY_REQ 10600Sstevel@tonic-gate */ 1061*269Sericheng /*ARGSUSED*/ 1062*269Sericheng static boolean_t 1063*269Sericheng proto_capability_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 10640Sstevel@tonic-gate { 1065*269Sericheng dl_capability_req_t *dlp = (dl_capability_req_t *)udlp; 1066*269Sericheng dl_capability_sub_t *sp; 1067*269Sericheng size_t size, len; 1068*269Sericheng offset_t off, end; 1069*269Sericheng t_uscalar_t dl_err; 1070*269Sericheng queue_t *q = dsp->ds_wq; 1071*269Sericheng boolean_t upgraded; 1072*269Sericheng 1073*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1074*269Sericheng 1075*269Sericheng if (MBLKL(mp) < sizeof (dl_capability_req_t)) { 1076*269Sericheng dl_err = DL_BADPRIM; 1077*269Sericheng goto failed; 1078*269Sericheng } 1079*269Sericheng 1080*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1081*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1082*269Sericheng dl_err = DL_OUTSTATE; 1083*269Sericheng goto failed; 1084*269Sericheng } 1085*269Sericheng 1086*269Sericheng /* 1087*269Sericheng * This request is overloaded. If there are no requested capabilities 1088*269Sericheng * then we just want to acknowledge with all the capabilities we 1089*269Sericheng * support. Otherwise we enable the set of capabilities requested. 1090*269Sericheng */ 1091*269Sericheng if (dlp->dl_sub_length == 0) { 1092*269Sericheng /* callee drops lock */ 1093*269Sericheng return (proto_capability_advertise(dsp, mp)); 1094*269Sericheng } 1095*269Sericheng 1096*269Sericheng if (!MBLKIN(mp, dlp->dl_sub_offset, dlp->dl_sub_length)) { 1097*269Sericheng dl_err = DL_BADPRIM; 1098*269Sericheng goto failed; 1099*269Sericheng } 1100*269Sericheng 1101*269Sericheng dlp->dl_primitive = DL_CAPABILITY_ACK; 1102*269Sericheng 1103*269Sericheng off = dlp->dl_sub_offset; 1104*269Sericheng len = dlp->dl_sub_length; 11050Sstevel@tonic-gate 11060Sstevel@tonic-gate /* 1107*269Sericheng * Walk the list of capabilities to be enabled. 11080Sstevel@tonic-gate */ 1109*269Sericheng upgraded = B_FALSE; 1110*269Sericheng for (end = off + len; off < end; ) { 1111*269Sericheng sp = (dl_capability_sub_t *)(mp->b_rptr + off); 1112*269Sericheng size = sizeof (dl_capability_sub_t) + sp->dl_length; 1113*269Sericheng 1114*269Sericheng if (off + size > end || 1115*269Sericheng !IS_P2ALIGNED(off, sizeof (uint32_t))) { 1116*269Sericheng dl_err = DL_BADPRIM; 1117*269Sericheng goto failed; 1118*269Sericheng } 1119*269Sericheng 1120*269Sericheng switch (sp->dl_cap) { 1121*269Sericheng /* 1122*269Sericheng * TCP/IP checksum offload to hardware. 1123*269Sericheng */ 1124*269Sericheng case DL_CAPAB_HCKSUM: { 1125*269Sericheng dl_capab_hcksum_t *hcksump; 1126*269Sericheng dl_capab_hcksum_t hcksum; 1127*269Sericheng 1128*269Sericheng ASSERT(dsp->ds_mip->mi_cksum != 0); 1129*269Sericheng 1130*269Sericheng hcksump = (dl_capab_hcksum_t *)&sp[1]; 1131*269Sericheng /* 1132*269Sericheng * Copy for alignment. 1133*269Sericheng */ 1134*269Sericheng bcopy(hcksump, &hcksum, sizeof (dl_capab_hcksum_t)); 1135*269Sericheng dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 1136*269Sericheng bcopy(&hcksum, hcksump, sizeof (dl_capab_hcksum_t)); 1137*269Sericheng break; 1138*269Sericheng } 1139*269Sericheng 1140*269Sericheng /* 1141*269Sericheng * IP polling interface. 1142*269Sericheng */ 1143*269Sericheng case DL_CAPAB_POLL: { 1144*269Sericheng dl_capab_poll_t *pollp; 1145*269Sericheng dl_capab_poll_t poll; 1146*269Sericheng 1147*269Sericheng pollp = (dl_capab_poll_t *)&sp[1]; 1148*269Sericheng /* 1149*269Sericheng * Copy for alignment. 1150*269Sericheng */ 1151*269Sericheng bcopy(pollp, &poll, sizeof (dl_capab_poll_t)); 1152*269Sericheng 1153*269Sericheng /* 1154*269Sericheng * We need to become writer before enabling and/or 1155*269Sericheng * disabling the polling interface. If we couldn' 1156*269Sericheng * upgrade, check state again after re-acquiring the 1157*269Sericheng * lock to make sure we can proceed. 1158*269Sericheng */ 1159*269Sericheng if (!upgraded && !rw_tryupgrade(&dsp->ds_lock)) { 1160*269Sericheng rw_exit(&dsp->ds_lock); 1161*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1162*269Sericheng 1163*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1164*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1165*269Sericheng dl_err = DL_OUTSTATE; 1166*269Sericheng goto failed; 1167*269Sericheng } 1168*269Sericheng } 1169*269Sericheng upgraded = B_TRUE; 1170*269Sericheng 1171*269Sericheng switch (poll.poll_flags) { 1172*269Sericheng default: 1173*269Sericheng /*FALLTHRU*/ 1174*269Sericheng case POLL_DISABLE: 1175*269Sericheng proto_poll_disable(dsp); 1176*269Sericheng break; 1177*269Sericheng 1178*269Sericheng case POLL_ENABLE: 1179*269Sericheng ASSERT(!(dld_opt & DLD_OPT_NO_POLL)); 1180*269Sericheng 1181*269Sericheng /* 1182*269Sericheng * Make sure polling is disabled. 1183*269Sericheng */ 1184*269Sericheng proto_poll_disable(dsp); 1185*269Sericheng 1186*269Sericheng /* 1187*269Sericheng * Now attempt enable it. 1188*269Sericheng */ 1189*269Sericheng if (!proto_poll_enable(dsp, &poll)) 1190*269Sericheng break; 1191*269Sericheng 1192*269Sericheng bzero(&poll, sizeof (dl_capab_poll_t)); 1193*269Sericheng poll.poll_flags = POLL_ENABLE; 1194*269Sericheng break; 1195*269Sericheng } 1196*269Sericheng 1197*269Sericheng dlcapabsetqid(&(poll.poll_mid), dsp->ds_rq); 1198*269Sericheng bcopy(&poll, pollp, sizeof (dl_capab_poll_t)); 1199*269Sericheng break; 1200*269Sericheng } 1201*269Sericheng default: 1202*269Sericheng break; 1203*269Sericheng } 1204*269Sericheng 1205*269Sericheng off += size; 1206*269Sericheng } 1207*269Sericheng rw_exit(&dsp->ds_lock); 1208*269Sericheng qreply(q, mp); 1209*269Sericheng return (B_TRUE); 1210*269Sericheng failed: 1211*269Sericheng rw_exit(&dsp->ds_lock); 1212*269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, dl_err, 0); 1213*269Sericheng return (B_FALSE); 12140Sstevel@tonic-gate } 12150Sstevel@tonic-gate 12160Sstevel@tonic-gate /* 1217*269Sericheng * DL_NOTIFY_REQ 12180Sstevel@tonic-gate */ 1219*269Sericheng static boolean_t 1220*269Sericheng proto_notify_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 12210Sstevel@tonic-gate { 1222*269Sericheng dl_notify_req_t *dlp = (dl_notify_req_t *)udlp; 1223*269Sericheng t_uscalar_t dl_err; 1224*269Sericheng queue_t *q = dsp->ds_wq; 1225*269Sericheng uint_t note = 1226*269Sericheng DL_NOTE_PROMISC_ON_PHYS | 1227*269Sericheng DL_NOTE_PROMISC_OFF_PHYS | 1228*269Sericheng DL_NOTE_PHYS_ADDR | 1229*269Sericheng DL_NOTE_LINK_UP | 1230*269Sericheng DL_NOTE_LINK_DOWN | 1231*269Sericheng DL_NOTE_CAPAB_RENEG; 12320Sstevel@tonic-gate 1233*269Sericheng if (MBLKL(mp) < sizeof (dl_notify_req_t)) { 1234*269Sericheng dl_err = DL_BADPRIM; 1235*269Sericheng goto failed; 1236*269Sericheng } 12370Sstevel@tonic-gate 1238*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1239*269Sericheng if (dsp->ds_dlstate == DL_UNATTACHED || 1240*269Sericheng DL_ACK_PENDING(dsp->ds_dlstate)) { 1241*269Sericheng dl_err = DL_OUTSTATE; 1242*269Sericheng goto failed; 12430Sstevel@tonic-gate } 12440Sstevel@tonic-gate 1245*269Sericheng if (dsp->ds_mip->mi_stat[MAC_STAT_IFSPEED]) 1246*269Sericheng note |= DL_NOTE_SPEED; 1247*269Sericheng 1248*269Sericheng /* 1249*269Sericheng * Cache the notifications that are being enabled. 1250*269Sericheng */ 1251*269Sericheng dsp->ds_notifications = dlp->dl_notifications & note; 1252*269Sericheng rw_exit(&dsp->ds_lock); 1253*269Sericheng /* 1254*269Sericheng * The ACK carries all notifications regardless of which set is 1255*269Sericheng * being enabled. 1256*269Sericheng */ 1257*269Sericheng dlnotifyack(q, mp, note); 1258*269Sericheng 1259*269Sericheng /* 1260*269Sericheng * Solicit DL_NOTIFY_IND messages for each enabled notification. 1261*269Sericheng */ 1262*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1263*269Sericheng if (dsp->ds_notifications != 0) { 1264*269Sericheng rw_exit(&dsp->ds_lock); 1265*269Sericheng dld_str_notify_ind(dsp); 1266*269Sericheng } else { 1267*269Sericheng rw_exit(&dsp->ds_lock); 1268*269Sericheng } 1269*269Sericheng return (B_TRUE); 1270*269Sericheng failed: 1271*269Sericheng rw_exit(&dsp->ds_lock); 1272*269Sericheng dlerrorack(q, mp, DL_NOTIFY_REQ, dl_err, 0); 1273*269Sericheng return (B_FALSE); 12740Sstevel@tonic-gate } 12750Sstevel@tonic-gate 12760Sstevel@tonic-gate /* 1277*269Sericheng * DL_UINTDATA_REQ 12780Sstevel@tonic-gate */ 1279*269Sericheng static boolean_t 1280*269Sericheng proto_unitdata_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 12810Sstevel@tonic-gate { 1282*269Sericheng queue_t *q = dsp->ds_wq; 1283*269Sericheng dl_unitdata_req_t *dlp = (dl_unitdata_req_t *)udlp; 1284*269Sericheng off_t off; 1285*269Sericheng size_t len, size; 1286*269Sericheng const uint8_t *addr; 1287*269Sericheng uint16_t sap; 1288*269Sericheng uint_t addr_length; 1289*269Sericheng mblk_t *bp, *cont; 1290*269Sericheng uint32_t start, stuff, end, value, flags; 1291*269Sericheng t_uscalar_t dl_err; 1292*269Sericheng 1293*269Sericheng rw_enter(&dsp->ds_lock, RW_READER); 1294*269Sericheng 1295*269Sericheng if (MBLKL(mp) < sizeof (dl_unitdata_req_t) || mp->b_cont == NULL) { 1296*269Sericheng dl_err = DL_BADPRIM; 1297*269Sericheng goto failed; 1298*269Sericheng } 1299*269Sericheng 1300*269Sericheng if (dsp->ds_dlstate != DL_IDLE) { 1301*269Sericheng dl_err = DL_OUTSTATE; 1302*269Sericheng goto failed; 1303*269Sericheng } 1304*269Sericheng addr_length = dsp->ds_mip->mi_addr_length; 1305*269Sericheng 1306*269Sericheng off = dlp->dl_dest_addr_offset; 1307*269Sericheng len = dlp->dl_dest_addr_length; 1308*269Sericheng 1309*269Sericheng if (!MBLKIN(mp, off, len) || !IS_P2ALIGNED(off, sizeof (uint16_t))) { 1310*269Sericheng dl_err = DL_BADPRIM; 1311*269Sericheng goto failed; 1312*269Sericheng } 1313*269Sericheng 1314*269Sericheng if (len != addr_length + sizeof (uint16_t)) { 1315*269Sericheng dl_err = DL_BADADDR; 1316*269Sericheng goto failed; 1317*269Sericheng } 1318*269Sericheng 1319*269Sericheng addr = mp->b_rptr + off; 1320*269Sericheng sap = *(uint16_t *)(mp->b_rptr + off + addr_length); 1321*269Sericheng 1322*269Sericheng /* 1323*269Sericheng * Check the length of the packet and the block types. 1324*269Sericheng */ 1325*269Sericheng size = 0; 1326*269Sericheng cont = mp->b_cont; 1327*269Sericheng for (bp = cont; bp != NULL; bp = bp->b_cont) { 1328*269Sericheng if (DB_TYPE(bp) != M_DATA) 1329*269Sericheng goto baddata; 1330*269Sericheng 1331*269Sericheng size += MBLKL(bp); 1332*269Sericheng } 1333*269Sericheng 1334*269Sericheng if (size > dsp->ds_mip->mi_sdu_max) 1335*269Sericheng goto baddata; 1336*269Sericheng 1337*269Sericheng /* 1338*269Sericheng * Build a packet header. 1339*269Sericheng */ 1340*269Sericheng if ((bp = dls_header(dsp->ds_dc, addr, sap, dsp->ds_pri)) == NULL) { 1341*269Sericheng dl_err = DL_BADADDR; 1342*269Sericheng goto failed; 1343*269Sericheng } 1344*269Sericheng 1345*269Sericheng /* 1346*269Sericheng * We no longer need the M_PROTO header, so free it. 1347*269Sericheng */ 1348*269Sericheng freeb(mp); 1349*269Sericheng 1350*269Sericheng /* 1351*269Sericheng * Transfer the checksum offload information if it is present. 1352*269Sericheng */ 1353*269Sericheng hcksum_retrieve(cont, NULL, NULL, &start, &stuff, &end, &value, 1354*269Sericheng &flags); 1355*269Sericheng (void) hcksum_assoc(bp, NULL, NULL, start, stuff, end, value, flags, 1356*269Sericheng 0); 1357*269Sericheng 1358*269Sericheng /* 1359*269Sericheng * Link the payload onto the new header. 1360*269Sericheng */ 1361*269Sericheng ASSERT(bp->b_cont == NULL); 1362*269Sericheng bp->b_cont = cont; 1363*269Sericheng 1364*269Sericheng str_mdata_fastpath_put(dsp, bp); 1365*269Sericheng rw_exit(&dsp->ds_lock); 1366*269Sericheng return (B_TRUE); 1367*269Sericheng failed: 1368*269Sericheng rw_exit(&dsp->ds_lock); 1369*269Sericheng dlerrorack(q, mp, DL_UNITDATA_REQ, dl_err, 0); 1370*269Sericheng return (B_FALSE); 1371*269Sericheng 1372*269Sericheng baddata: 1373*269Sericheng rw_exit(&dsp->ds_lock); 1374*269Sericheng dluderrorind(q, mp, (void *)addr, len, DL_BADDATA, 0); 1375*269Sericheng return (B_FALSE); 1376*269Sericheng } 1377*269Sericheng 1378*269Sericheng /* 1379*269Sericheng * DL_PASSIVE_REQ 1380*269Sericheng */ 1381*269Sericheng /* ARGSUSED */ 1382*269Sericheng static boolean_t 1383*269Sericheng proto_passive_req(dld_str_t *dsp, union DL_primitives *udlp, mblk_t *mp) 1384*269Sericheng { 1385*269Sericheng t_uscalar_t dl_err; 1386*269Sericheng 1387*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1388*269Sericheng /* 1389*269Sericheng * If we've already become active by issuing an active primitive, 1390*269Sericheng * then it's too late to try to become passive. 1391*269Sericheng */ 1392*269Sericheng if (dsp->ds_passivestate == DLD_ACTIVE) { 1393*269Sericheng dl_err = DL_OUTSTATE; 1394*269Sericheng goto failed; 1395*269Sericheng } 1396*269Sericheng 1397*269Sericheng if (MBLKL(mp) < sizeof (dl_passive_req_t)) { 1398*269Sericheng dl_err = DL_BADPRIM; 1399*269Sericheng goto failed; 1400*269Sericheng } 1401*269Sericheng 1402*269Sericheng dsp->ds_passivestate = DLD_PASSIVE; 1403*269Sericheng rw_exit(&dsp->ds_lock); 1404*269Sericheng dlokack(dsp->ds_wq, mp, DL_PASSIVE_REQ); 1405*269Sericheng return (B_TRUE); 1406*269Sericheng failed: 1407*269Sericheng rw_exit(&dsp->ds_lock); 1408*269Sericheng dlerrorack(dsp->ds_wq, mp, DL_PASSIVE_REQ, dl_err, 0); 1409*269Sericheng return (B_FALSE); 1410*269Sericheng } 1411*269Sericheng 1412*269Sericheng 1413*269Sericheng /* 1414*269Sericheng * Catch-all handler. 1415*269Sericheng */ 1416*269Sericheng static boolean_t 1417*269Sericheng proto_req(dld_str_t *dsp, union DL_primitives *dlp, mblk_t *mp) 1418*269Sericheng { 1419*269Sericheng dlerrorack(dsp->ds_wq, mp, dlp->dl_primitive, DL_UNSUPPORTED, 0); 1420*269Sericheng return (B_FALSE); 14210Sstevel@tonic-gate } 14220Sstevel@tonic-gate 14230Sstevel@tonic-gate static void 14240Sstevel@tonic-gate proto_poll_disable(dld_str_t *dsp) 14250Sstevel@tonic-gate { 14260Sstevel@tonic-gate mac_handle_t mh; 14270Sstevel@tonic-gate 1428*269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 1429*269Sericheng 14300Sstevel@tonic-gate if (!dsp->ds_polling) 14310Sstevel@tonic-gate return; 14320Sstevel@tonic-gate 14330Sstevel@tonic-gate /* 14340Sstevel@tonic-gate * It should be impossible to enable raw mode if polling is turned on. 14350Sstevel@tonic-gate */ 14360Sstevel@tonic-gate ASSERT(dsp->ds_mode != DLD_RAW); 14370Sstevel@tonic-gate 14380Sstevel@tonic-gate /* 14390Sstevel@tonic-gate * Reset the resource_add callback. 14400Sstevel@tonic-gate */ 14410Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 14420Sstevel@tonic-gate mac_resource_set(mh, NULL, NULL); 14430Sstevel@tonic-gate 14440Sstevel@tonic-gate /* 14450Sstevel@tonic-gate * Set receive function back to default. 14460Sstevel@tonic-gate */ 14470Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dsp->ds_mode == DLD_FASTPATH) ? 14480Sstevel@tonic-gate dld_str_rx_fastpath : dld_str_rx_unitdata, (void *)dsp); 14490Sstevel@tonic-gate 14500Sstevel@tonic-gate /* 14510Sstevel@tonic-gate * Note that polling is disabled. 14520Sstevel@tonic-gate */ 14530Sstevel@tonic-gate dsp->ds_polling = B_FALSE; 14540Sstevel@tonic-gate } 14550Sstevel@tonic-gate 14560Sstevel@tonic-gate static boolean_t 14570Sstevel@tonic-gate proto_poll_enable(dld_str_t *dsp, dl_capab_poll_t *pollp) 14580Sstevel@tonic-gate { 14590Sstevel@tonic-gate mac_handle_t mh; 14600Sstevel@tonic-gate 1461*269Sericheng ASSERT(RW_WRITE_HELD(&dsp->ds_lock)); 14620Sstevel@tonic-gate ASSERT(!dsp->ds_polling); 14630Sstevel@tonic-gate 14640Sstevel@tonic-gate /* 14650Sstevel@tonic-gate * We cannot enable polling if raw mode 14660Sstevel@tonic-gate * has been enabled. 14670Sstevel@tonic-gate */ 14680Sstevel@tonic-gate if (dsp->ds_mode == DLD_RAW) 14690Sstevel@tonic-gate return (B_FALSE); 14700Sstevel@tonic-gate 14710Sstevel@tonic-gate mh = dls_mac(dsp->ds_dc); 14720Sstevel@tonic-gate 14730Sstevel@tonic-gate /* 14740Sstevel@tonic-gate * Register resources. 14750Sstevel@tonic-gate */ 14760Sstevel@tonic-gate mac_resource_set(mh, (mac_resource_add_t)pollp->poll_ring_add, 14770Sstevel@tonic-gate (void *)pollp->poll_rx_handle); 14780Sstevel@tonic-gate mac_resources(mh); 14790Sstevel@tonic-gate 14800Sstevel@tonic-gate /* 14810Sstevel@tonic-gate * Set the receive function. 14820Sstevel@tonic-gate */ 14830Sstevel@tonic-gate dls_rx_set(dsp->ds_dc, (dls_rx_t)pollp->poll_rx, 14840Sstevel@tonic-gate (void *)pollp->poll_rx_handle); 14850Sstevel@tonic-gate 14860Sstevel@tonic-gate /* 14870Sstevel@tonic-gate * Note that polling is enabled. This prevents further DLIOCHDRINFO 14880Sstevel@tonic-gate * ioctls from overwriting the receive function pointer. 14890Sstevel@tonic-gate */ 14900Sstevel@tonic-gate dsp->ds_polling = B_TRUE; 14910Sstevel@tonic-gate return (B_TRUE); 14920Sstevel@tonic-gate } 14930Sstevel@tonic-gate 14940Sstevel@tonic-gate /* 14950Sstevel@tonic-gate * DL_CAPABILITY_ACK/DL_ERROR_ACK 14960Sstevel@tonic-gate */ 1497*269Sericheng static boolean_t 1498*269Sericheng proto_capability_advertise(dld_str_t *dsp, mblk_t *mp) 14990Sstevel@tonic-gate { 15000Sstevel@tonic-gate dl_capability_ack_t *dlap; 15010Sstevel@tonic-gate dl_capability_sub_t *dlsp; 15020Sstevel@tonic-gate size_t subsize; 15030Sstevel@tonic-gate dl_capab_poll_t poll; 15040Sstevel@tonic-gate dl_capab_hcksum_t hcksum; 15050Sstevel@tonic-gate dl_capab_zerocopy_t zcopy; 15060Sstevel@tonic-gate uint8_t *ptr; 15070Sstevel@tonic-gate uint32_t cksum; 15080Sstevel@tonic-gate boolean_t poll_cap; 1509*269Sericheng queue_t *q = dsp->ds_wq; 1510*269Sericheng mblk_t *mp1; 1511*269Sericheng 1512*269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 15130Sstevel@tonic-gate 15140Sstevel@tonic-gate /* 15150Sstevel@tonic-gate * Initially assume no capabilities. 15160Sstevel@tonic-gate */ 15170Sstevel@tonic-gate subsize = 0; 15180Sstevel@tonic-gate 15190Sstevel@tonic-gate /* 15200Sstevel@tonic-gate * Check if polling can be enabled on this interface. 15210Sstevel@tonic-gate * If advertising DL_CAPAB_POLL has not been explicitly disabled 15220Sstevel@tonic-gate * then reserve space for that capability. 15230Sstevel@tonic-gate */ 15240Sstevel@tonic-gate poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) && 15250Sstevel@tonic-gate !(dld_opt & DLD_OPT_NO_POLL) && (dsp->ds_vid == VLAN_ID_NONE)); 15260Sstevel@tonic-gate if (poll_cap) { 15270Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15280Sstevel@tonic-gate sizeof (dl_capab_poll_t); 15290Sstevel@tonic-gate } 15300Sstevel@tonic-gate 15310Sstevel@tonic-gate /* 15320Sstevel@tonic-gate * If the MAC interface supports checksum offload then reserve 15330Sstevel@tonic-gate * space for the DL_CAPAB_HCKSUM capability. 15340Sstevel@tonic-gate */ 15350Sstevel@tonic-gate if ((cksum = dsp->ds_mip->mi_cksum) != 0) { 15360Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15370Sstevel@tonic-gate sizeof (dl_capab_hcksum_t); 15380Sstevel@tonic-gate } 15390Sstevel@tonic-gate 15400Sstevel@tonic-gate /* 15410Sstevel@tonic-gate * If DL_CAPAB_ZEROCOPY has not be explicitly disabled then 15420Sstevel@tonic-gate * reserve space for it. 15430Sstevel@tonic-gate */ 15440Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 15450Sstevel@tonic-gate subsize += sizeof (dl_capability_sub_t) + 15460Sstevel@tonic-gate sizeof (dl_capab_zerocopy_t); 15470Sstevel@tonic-gate } 15480Sstevel@tonic-gate 15490Sstevel@tonic-gate /* 1550*269Sericheng * If there are no capabilities to advertise or if we 1551*269Sericheng * can't allocate a response, send a DL_ERROR_ACK. 15520Sstevel@tonic-gate */ 1553*269Sericheng if (subsize == 0 || (mp1 = reallocb(mp, 1554*269Sericheng sizeof (dl_capability_ack_t) + subsize, 0)) == NULL) { 1555*269Sericheng rw_exit(&dsp->ds_lock); 1556*269Sericheng dlerrorack(q, mp, DL_CAPABILITY_REQ, DL_NOTSUPPORTED, 0); 1557*269Sericheng return (B_FALSE); 15580Sstevel@tonic-gate } 15590Sstevel@tonic-gate 1560*269Sericheng mp = mp1; 1561*269Sericheng DB_TYPE(mp) = M_PROTO; 1562*269Sericheng mp->b_wptr = mp->b_rptr + sizeof (dl_capability_ack_t) + subsize; 1563*269Sericheng bzero(mp->b_rptr, MBLKL(mp)); 15640Sstevel@tonic-gate dlap = (dl_capability_ack_t *)mp->b_rptr; 15650Sstevel@tonic-gate dlap->dl_primitive = DL_CAPABILITY_ACK; 15660Sstevel@tonic-gate dlap->dl_sub_offset = sizeof (dl_capability_ack_t); 15670Sstevel@tonic-gate dlap->dl_sub_length = subsize; 15680Sstevel@tonic-gate ptr = (uint8_t *)&dlap[1]; 15690Sstevel@tonic-gate 15700Sstevel@tonic-gate /* 15710Sstevel@tonic-gate * IP polling interface. 15720Sstevel@tonic-gate */ 15730Sstevel@tonic-gate if (poll_cap) { 15740Sstevel@tonic-gate /* 1575*269Sericheng * Attempt to disable just in case this is a re-negotiation; 1576*269Sericheng * we need to become writer before doing so. 15770Sstevel@tonic-gate */ 1578*269Sericheng if (!rw_tryupgrade(&dsp->ds_lock)) { 1579*269Sericheng rw_exit(&dsp->ds_lock); 1580*269Sericheng rw_enter(&dsp->ds_lock, RW_WRITER); 1581*269Sericheng } 15820Sstevel@tonic-gate 1583*269Sericheng /* 1584*269Sericheng * Check if polling state has changed after we re-acquired 1585*269Sericheng * the lock above, so that we don't mis-advertise it. 1586*269Sericheng */ 1587*269Sericheng poll_cap = ((dsp->ds_mip->mi_poll & DL_CAPAB_POLL) && 1588*269Sericheng !(dld_opt & DLD_OPT_NO_POLL) && 1589*269Sericheng (dsp->ds_vid == VLAN_ID_NONE)); 15900Sstevel@tonic-gate 1591*269Sericheng if (!poll_cap) { 1592*269Sericheng int poll_capab_size; 1593*269Sericheng 1594*269Sericheng rw_downgrade(&dsp->ds_lock); 1595*269Sericheng 1596*269Sericheng poll_capab_size = sizeof (dl_capability_sub_t) + 1597*269Sericheng sizeof (dl_capab_poll_t); 15980Sstevel@tonic-gate 1599*269Sericheng mp->b_wptr -= poll_capab_size; 1600*269Sericheng subsize -= poll_capab_size; 1601*269Sericheng dlap->dl_sub_length = subsize; 1602*269Sericheng } else { 1603*269Sericheng proto_poll_disable(dsp); 1604*269Sericheng 1605*269Sericheng rw_downgrade(&dsp->ds_lock); 1606*269Sericheng 1607*269Sericheng dlsp = (dl_capability_sub_t *)ptr; 1608*269Sericheng 1609*269Sericheng dlsp->dl_cap = DL_CAPAB_POLL; 1610*269Sericheng dlsp->dl_length = sizeof (dl_capab_poll_t); 1611*269Sericheng ptr += sizeof (dl_capability_sub_t); 16120Sstevel@tonic-gate 1613*269Sericheng bzero(&poll, sizeof (dl_capab_poll_t)); 1614*269Sericheng poll.poll_version = POLL_VERSION_1; 1615*269Sericheng poll.poll_flags = POLL_CAPABLE; 1616*269Sericheng poll.poll_tx_handle = (uintptr_t)dsp; 1617*269Sericheng poll.poll_tx = (uintptr_t)str_mdata_fastpath_put; 1618*269Sericheng 1619*269Sericheng dlcapabsetqid(&(poll.poll_mid), dsp->ds_rq); 1620*269Sericheng bcopy(&poll, ptr, sizeof (dl_capab_poll_t)); 1621*269Sericheng ptr += sizeof (dl_capab_poll_t); 1622*269Sericheng } 16230Sstevel@tonic-gate } 16240Sstevel@tonic-gate 1625*269Sericheng ASSERT(RW_READ_HELD(&dsp->ds_lock)); 1626*269Sericheng 16270Sstevel@tonic-gate /* 16280Sstevel@tonic-gate * TCP/IP checksum offload. 16290Sstevel@tonic-gate */ 16300Sstevel@tonic-gate if (cksum != 0) { 16310Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16320Sstevel@tonic-gate 16330Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_HCKSUM; 16340Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_hcksum_t); 16350Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16360Sstevel@tonic-gate 16370Sstevel@tonic-gate bzero(&hcksum, sizeof (dl_capab_hcksum_t)); 16380Sstevel@tonic-gate hcksum.hcksum_version = HCKSUM_VERSION_1; 16390Sstevel@tonic-gate hcksum.hcksum_txflags = cksum; 16400Sstevel@tonic-gate 16410Sstevel@tonic-gate dlcapabsetqid(&(hcksum.hcksum_mid), dsp->ds_rq); 16420Sstevel@tonic-gate bcopy(&hcksum, ptr, sizeof (dl_capab_hcksum_t)); 16430Sstevel@tonic-gate ptr += sizeof (dl_capab_hcksum_t); 16440Sstevel@tonic-gate } 16450Sstevel@tonic-gate 16460Sstevel@tonic-gate /* 16470Sstevel@tonic-gate * Zero copy 16480Sstevel@tonic-gate */ 16490Sstevel@tonic-gate if (!(dld_opt & DLD_OPT_NO_ZEROCOPY)) { 16500Sstevel@tonic-gate dlsp = (dl_capability_sub_t *)ptr; 16510Sstevel@tonic-gate 16520Sstevel@tonic-gate dlsp->dl_cap = DL_CAPAB_ZEROCOPY; 16530Sstevel@tonic-gate dlsp->dl_length = sizeof (dl_capab_zerocopy_t); 16540Sstevel@tonic-gate ptr += sizeof (dl_capability_sub_t); 16550Sstevel@tonic-gate 16560Sstevel@tonic-gate bzero(&zcopy, sizeof (dl_capab_zerocopy_t)); 16570Sstevel@tonic-gate zcopy.zerocopy_version = ZEROCOPY_VERSION_1; 16580Sstevel@tonic-gate zcopy.zerocopy_flags = DL_CAPAB_VMSAFE_MEM; 16590Sstevel@tonic-gate 16600Sstevel@tonic-gate dlcapabsetqid(&(zcopy.zerocopy_mid), dsp->ds_rq); 16610Sstevel@tonic-gate bcopy(&zcopy, ptr, sizeof (dl_capab_zerocopy_t)); 16620Sstevel@tonic-gate ptr += sizeof (dl_capab_zerocopy_t); 16630Sstevel@tonic-gate } 16640Sstevel@tonic-gate 16650Sstevel@tonic-gate ASSERT(ptr == mp->b_rptr + sizeof (dl_capability_ack_t) + subsize); 1666*269Sericheng 1667*269Sericheng rw_exit(&dsp->ds_lock); 1668*269Sericheng qreply(q, mp); 1669*269Sericheng return (B_TRUE); 16700Sstevel@tonic-gate } 1671