1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2008 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <sys/strsubr.h> 29 #include <inet/led.h> 30 #include <sys/softmac_impl.h> 31 32 /* 33 * Macro to check whether the write-queue of the lower stream is full. 34 * 35 * Because softmac is pushed right above the underlying device and 36 * _I_INSERT/_I_REMOVE is not processed in the lower stream, it is 37 * safe to directly access the q_next pointer. 38 */ 39 #define CANPUTNEXT(q) \ 40 (!((q)->q_next->q_nfsrv->q_flag & QFULL) || canput((q)->q_next)) 41 42 mblk_t * 43 softmac_m_tx(void *arg, mblk_t *mp) 44 { 45 queue_t *wq = ((softmac_t *)arg)->smac_lower->sl_wq; 46 47 /* 48 * Optimize for the most common case. 49 */ 50 if (mp->b_cont == NULL) { 51 if (!CANPUTNEXT(wq)) 52 return (mp); 53 54 mp->b_flag |= MSGNOLOOP; 55 putnext(wq, mp); 56 return (NULL); 57 } 58 59 while (mp != NULL) { 60 mblk_t *next = mp->b_next; 61 62 if (!CANPUTNEXT(wq)) 63 break; 64 mp->b_next = NULL; 65 mp->b_flag |= MSGNOLOOP; 66 putnext(wq, mp); 67 mp = next; 68 } 69 return (mp); 70 } 71 72 /*ARGSUSED*/ 73 static void 74 softmac_blank(void *arg, time_t ticks, uint_t count) 75 { 76 } 77 78 void 79 softmac_m_resources(void *arg) 80 { 81 softmac_t *softmac = arg; 82 softmac_lower_t *slp = softmac->smac_lower; 83 mac_rx_fifo_t mrf; 84 85 ASSERT((softmac->smac_state == SOFTMAC_READY) && (slp != NULL)); 86 87 /* 88 * Register rx resources and save resource handle for future reference. 89 * Note that the mac_resources() function must be called when the lower 90 * stream is plumbed. 91 */ 92 93 mutex_enter(&slp->sl_mutex); 94 95 mrf.mrf_type = MAC_RX_FIFO; 96 mrf.mrf_blank = softmac_blank; 97 mrf.mrf_arg = slp; 98 mrf.mrf_normal_blank_time = SOFTMAC_BLANK_TICKS; 99 mrf.mrf_normal_pkt_count = SOFTMAC_BLANK_PKT_COUNT; 100 101 slp->sl_handle = 102 mac_resource_add(softmac->smac_mh, (mac_resource_t *)&mrf); 103 104 mutex_exit(&slp->sl_mutex); 105 } 106 107 void 108 softmac_rput_process_data(softmac_lower_t *slp, mblk_t *mp) 109 { 110 /* 111 * When packets arrive, the softmac might not be fully started. 112 */ 113 ASSERT((slp->sl_softmac != NULL)); 114 ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL)); 115 116 if (DB_REF(mp) > 1) { 117 mblk_t *tmp; 118 119 if ((tmp = copymsg(mp)) == NULL) { 120 cmn_err(CE_WARN, "softmac_rput_process_data: " 121 "copymsg failed"); 122 goto failed; 123 } 124 freemsg(mp); 125 mp = tmp; 126 } 127 128 mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp); 129 return; 130 131 failed: 132 freemsg(mp); 133 } 134 135 #define ACKTIMEOUT (10 * hz) 136 137 /* 138 * Serialize control message processing. 139 */ 140 static void 141 softmac_serialize_enter(softmac_lower_t *slp) 142 { 143 mutex_enter(&slp->sl_ctl_mutex); 144 while (slp->sl_ctl_inprogress) 145 cv_wait(&slp->sl_ctl_cv, &slp->sl_ctl_mutex); 146 147 ASSERT(!slp->sl_ctl_inprogress); 148 ASSERT(!slp->sl_pending_ioctl); 149 ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); 150 151 slp->sl_ctl_inprogress = B_TRUE; 152 mutex_exit(&slp->sl_ctl_mutex); 153 } 154 155 static void 156 softmac_serialize_exit(softmac_lower_t *slp) 157 { 158 mutex_enter(&slp->sl_ctl_mutex); 159 160 ASSERT(slp->sl_ctl_inprogress); 161 ASSERT(!slp->sl_pending_ioctl); 162 ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL); 163 164 slp->sl_ctl_inprogress = B_FALSE; 165 cv_broadcast(&slp->sl_ctl_cv); 166 mutex_exit(&slp->sl_ctl_mutex); 167 } 168 169 static int 170 dlpi_get_errno(t_uscalar_t error, t_uscalar_t unix_errno) 171 { 172 return (error == DL_SYSERR ? unix_errno : EINVAL); 173 } 174 175 static int 176 softmac_output(softmac_lower_t *slp, mblk_t *mp, t_uscalar_t dl_prim, 177 t_uscalar_t ack, mblk_t **mpp) 178 { 179 union DL_primitives *dlp; 180 int err = 0; 181 182 softmac_serialize_enter(slp); 183 184 /* 185 * Record the pending DLPI primitive. 186 */ 187 mutex_enter(&slp->sl_mutex); 188 slp->sl_pending_prim = dl_prim; 189 mutex_exit(&slp->sl_mutex); 190 191 putnext(slp->sl_wq, mp); 192 193 mutex_enter(&slp->sl_mutex); 194 while (slp->sl_pending_prim != DL_PRIM_INVAL) { 195 if (cv_timedwait(&slp->sl_cv, &slp->sl_mutex, 196 lbolt + ACKTIMEOUT) == -1) 197 break; 198 } 199 200 mp = slp->sl_ack_mp; 201 slp->sl_ack_mp = NULL; 202 203 /* 204 * If we timed out, sl_ack_mp will still be NULL, but sl_pending_prim 205 * won't be set to DL_PRIM_INVAL. 206 */ 207 ASSERT(mp != NULL || slp->sl_pending_prim != DL_PRIM_INVAL); 208 209 slp->sl_pending_prim = DL_PRIM_INVAL; 210 mutex_exit(&slp->sl_mutex); 211 212 if (mp != NULL) { 213 dlp = (union DL_primitives *)mp->b_rptr; 214 215 if (dlp->dl_primitive == DL_ERROR_ACK) { 216 err = dlpi_get_errno(dlp->error_ack.dl_errno, 217 dlp->error_ack.dl_unix_errno); 218 } else { 219 ASSERT(dlp->dl_primitive == ack); 220 } 221 } else { 222 err = ENOMSG; 223 } 224 225 if (mpp != NULL) 226 *mpp = mp; 227 else 228 freemsg(mp); 229 230 softmac_serialize_exit(slp); 231 return (err); 232 } 233 234 void 235 softmac_ioctl_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) 236 { 237 softmac_serialize_enter(slp); 238 239 /* 240 * Record that ioctl processing is currently in progress. 241 */ 242 mutex_enter(&slp->sl_mutex); 243 slp->sl_pending_ioctl = B_TRUE; 244 mutex_exit(&slp->sl_mutex); 245 246 putnext(slp->sl_wq, mp); 247 248 mutex_enter(&slp->sl_mutex); 249 while (slp->sl_pending_ioctl) 250 cv_wait(&slp->sl_cv, &slp->sl_mutex); 251 mp = slp->sl_ack_mp; 252 slp->sl_ack_mp = NULL; 253 mutex_exit(&slp->sl_mutex); 254 255 ASSERT(mpp != NULL && mp != NULL); 256 *mpp = mp; 257 258 softmac_serialize_exit(slp); 259 } 260 261 static int 262 softmac_mexchange_error_ack(mblk_t **mpp, t_uscalar_t error_primitive, 263 t_uscalar_t error, t_uscalar_t unix_errno) 264 { 265 union DL_primitives *dlp; 266 267 if ((*mpp = mexchange(NULL, *mpp, sizeof (dl_error_ack_t), M_PCPROTO, 268 DL_ERROR_ACK)) == NULL) 269 return (ENOMEM); 270 271 dlp = (union DL_primitives *)(*mpp)->b_rptr; 272 dlp->error_ack.dl_error_primitive = error_primitive; 273 dlp->error_ack.dl_errno = error; 274 dlp->error_ack.dl_unix_errno = unix_errno; 275 276 return (0); 277 } 278 279 int 280 softmac_proto_tx(softmac_lower_t *slp, mblk_t *mp, mblk_t **mpp) 281 { 282 int err = 0; 283 t_uscalar_t dl_prim; 284 285 dl_prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive; 286 287 ASSERT(slp->sl_softmac != NULL); 288 289 switch (dl_prim) { 290 case DL_ENABMULTI_REQ: 291 case DL_DISABMULTI_REQ: 292 case DL_SET_PHYS_ADDR_REQ: 293 case DL_UNBIND_REQ: 294 case DL_UDQOS_REQ: 295 case DL_PROMISCON_REQ: 296 case DL_PROMISCOFF_REQ: 297 err = softmac_output(slp, mp, dl_prim, DL_OK_ACK, mpp); 298 break; 299 case DL_BIND_REQ: 300 err = softmac_output(slp, mp, dl_prim, DL_BIND_ACK, mpp); 301 break; 302 case DL_NOTIFY_REQ: 303 err = softmac_output(slp, mp, dl_prim, DL_NOTIFY_ACK, mpp); 304 break; 305 case DL_CONTROL_REQ: 306 err = softmac_output(slp, mp, dl_prim, DL_CONTROL_ACK, mpp); 307 break; 308 case DL_CAPABILITY_REQ: 309 err = softmac_output(slp, mp, dl_prim, DL_CAPABILITY_ACK, mpp); 310 break; 311 default: 312 if (mpp != NULL) { 313 *mpp = mp; 314 err = softmac_mexchange_error_ack(mpp, dl_prim, 315 DL_UNSUPPORTED, 0); 316 } 317 break; 318 } 319 return (err); 320 } 321