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/stropts.h> 29 #include <sys/softmac_impl.h> 30 31 int 32 softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications) 33 { 34 mblk_t *reqmp; 35 36 /* 37 * create notify req message and send it down 38 */ 39 reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO, 40 DL_NOTIFY_REQ); 41 if (reqmp == NULL) 42 return (ENOMEM); 43 44 ((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications; 45 46 return (softmac_proto_tx(slp, reqmp, NULL)); 47 } 48 49 int 50 softmac_send_bind_req(softmac_lower_t *slp, uint_t sap) 51 { 52 dl_bind_req_t *bind; 53 mblk_t *reqmp; 54 55 /* 56 * create bind req message and send it down 57 */ 58 reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ); 59 if (reqmp == NULL) 60 return (ENOMEM); 61 62 bind = (dl_bind_req_t *)reqmp->b_rptr; 63 bind->dl_sap = sap; 64 bind->dl_conn_mgmt = 0; 65 bind->dl_max_conind = 0; 66 bind->dl_xidtest_flg = 0; 67 bind->dl_service_mode = DL_CLDLS; 68 69 return (softmac_proto_tx(slp, reqmp, NULL)); 70 } 71 72 int 73 softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on) 74 { 75 mblk_t *reqmp; 76 size_t size; 77 t_uscalar_t dl_prim; 78 79 /* 80 * create promisc message and send it down 81 */ 82 if (on) { 83 dl_prim = DL_PROMISCON_REQ; 84 size = DL_PROMISCON_REQ_SIZE; 85 } else { 86 dl_prim = DL_PROMISCOFF_REQ; 87 size = DL_PROMISCOFF_REQ_SIZE; 88 } 89 90 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); 91 if (reqmp == NULL) 92 return (ENOMEM); 93 94 if (on) 95 ((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level; 96 else 97 ((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level; 98 99 return (softmac_proto_tx(slp, reqmp, NULL)); 100 } 101 102 int 103 softmac_m_promisc(void *arg, boolean_t on) 104 { 105 softmac_t *softmac = arg; 106 softmac_lower_t *slp = softmac->smac_lower; 107 108 ASSERT(slp != NULL); 109 return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on)); 110 } 111 112 int 113 softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca) 114 { 115 softmac_t *softmac = arg; 116 softmac_lower_t *slp; 117 dl_enabmulti_req_t *enabmulti; 118 dl_disabmulti_req_t *disabmulti; 119 mblk_t *reqmp; 120 t_uscalar_t dl_prim; 121 uint32_t size, addr_length; 122 123 /* 124 * create multicst message and send it down 125 */ 126 addr_length = softmac->smac_addrlen; 127 if (add) { 128 size = sizeof (dl_enabmulti_req_t) + addr_length; 129 dl_prim = DL_ENABMULTI_REQ; 130 } else { 131 size = sizeof (dl_disabmulti_req_t) + addr_length; 132 dl_prim = DL_DISABMULTI_REQ; 133 } 134 135 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim); 136 if (reqmp == NULL) 137 return (ENOMEM); 138 139 if (add) { 140 enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr; 141 enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t); 142 enabmulti->dl_addr_length = addr_length; 143 (void) memcpy(&enabmulti[1], mca, addr_length); 144 } else { 145 disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr; 146 disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t); 147 disabmulti->dl_addr_length = addr_length; 148 (void) memcpy(&disabmulti[1], mca, addr_length); 149 } 150 151 slp = softmac->smac_lower; 152 ASSERT(slp != NULL); 153 return (softmac_proto_tx(slp, reqmp, NULL)); 154 } 155 156 int 157 softmac_m_unicst(void *arg, const uint8_t *macaddr) 158 { 159 softmac_t *softmac = arg; 160 softmac_lower_t *slp; 161 dl_set_phys_addr_req_t *phyaddr; 162 mblk_t *reqmp; 163 size_t size; 164 165 /* 166 * create set_phys_addr message and send it down 167 */ 168 size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen; 169 reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ); 170 if (reqmp == NULL) 171 return (ENOMEM); 172 173 phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr; 174 phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t); 175 phyaddr->dl_addr_length = softmac->smac_addrlen; 176 (void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen); 177 178 slp = softmac->smac_lower; 179 ASSERT(slp != NULL); 180 return (softmac_proto_tx(slp, reqmp, NULL)); 181 } 182 183 void 184 softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp) 185 { 186 softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower; 187 mblk_t *ackmp; 188 189 ASSERT(slp != NULL); 190 softmac_ioctl_tx(slp, mp, &ackmp); 191 qreply(wq, ackmp); 192 } 193 194 static void 195 softmac_process_notify_ind(queue_t *rq, mblk_t *mp) 196 { 197 softmac_lower_t *slp = rq->q_ptr; 198 dl_notify_ind_t *dlnip = (dl_notify_ind_t *)mp->b_rptr; 199 softmac_t *softmac = slp->sl_softmac; 200 uint_t addroff, addrlen; 201 202 ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND); 203 204 switch (dlnip->dl_notification) { 205 case DL_NOTE_PHYS_ADDR: 206 if (dlnip->dl_data != DL_CURR_PHYS_ADDR) 207 break; 208 209 addroff = dlnip->dl_addr_offset; 210 addrlen = dlnip->dl_addr_length - softmac->smac_saplen; 211 if (addroff == 0 || addrlen != softmac->smac_addrlen || 212 !MBLKIN(mp, addroff, addrlen)) { 213 cmn_err(CE_NOTE, "softmac: got malformed " 214 "DL_NOTIFY_IND; length/offset %d/%d", 215 addrlen, addroff); 216 break; 217 } 218 219 mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff); 220 break; 221 222 case DL_NOTE_LINK_UP: 223 mac_link_update(softmac->smac_mh, LINK_STATE_UP); 224 break; 225 226 case DL_NOTE_LINK_DOWN: 227 mac_link_update(softmac->smac_mh, LINK_STATE_DOWN); 228 break; 229 } 230 231 freemsg(mp); 232 } 233 234 static void 235 softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen, 236 t_uscalar_t reqprim) 237 { 238 const char *ackname; 239 240 ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive); 241 242 if (MBLKL(mp) < minlen) { 243 cmn_err(CE_WARN, "softmac: got short %s", ackname); 244 freemsg(mp); 245 return; 246 } 247 248 mutex_enter(&slp->sl_mutex); 249 if (slp->sl_pending_prim != reqprim) { 250 cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname); 251 mutex_exit(&slp->sl_mutex); 252 freemsg(mp); 253 return; 254 } 255 256 slp->sl_pending_prim = DL_PRIM_INVAL; 257 slp->sl_ack_mp = mp; 258 cv_signal(&slp->sl_cv); 259 mutex_exit(&slp->sl_mutex); 260 } 261 262 void 263 softmac_rput_process_proto(queue_t *rq, mblk_t *mp) 264 { 265 softmac_lower_t *slp = rq->q_ptr; 266 union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr; 267 ssize_t len = MBLKL(mp); 268 const char *primstr; 269 270 if (len < sizeof (t_uscalar_t)) { 271 cmn_err(CE_WARN, "softmac: got runt DLPI message"); 272 goto exit; 273 } 274 275 primstr = dl_primstr(dlp->dl_primitive); 276 277 switch (dlp->dl_primitive) { 278 case DL_OK_ACK: 279 if (len < DL_OK_ACK_SIZE) 280 goto runt; 281 282 softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE, 283 dlp->ok_ack.dl_correct_primitive); 284 return; 285 286 case DL_ERROR_ACK: 287 if (len < DL_ERROR_ACK_SIZE) 288 goto runt; 289 290 cmn_err(CE_NOTE, "softmac: received DL_ERROR_ACK for " 291 "%s errno/unix_errno 0x%x/%d", 292 dl_primstr(dlp->error_ack.dl_error_primitive), 293 dlp->error_ack.dl_errno, dlp->error_ack.dl_unix_errno); 294 295 softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE, 296 dlp->error_ack.dl_error_primitive); 297 return; 298 299 case DL_NOTIFY_IND: 300 if (len < DL_NOTIFY_IND_SIZE) 301 goto runt; 302 303 softmac_process_notify_ind(rq, mp); 304 return; 305 306 case DL_NOTIFY_ACK: 307 softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE, 308 DL_NOTIFY_REQ); 309 return; 310 311 case DL_CAPABILITY_ACK: 312 softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE, 313 DL_CAPABILITY_REQ); 314 return; 315 316 case DL_BIND_ACK: 317 softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ); 318 return; 319 320 case DL_CONTROL_ACK: 321 softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE, 322 DL_CONTROL_REQ); 323 return; 324 325 case DL_UNITDATA_IND: 326 case DL_PHYS_ADDR_ACK: 327 /* 328 * a. Because the stream is in DLIOCRAW mode, 329 * DL_UNITDATA_IND messages are not expected. 330 * b. The lower stream should not receive DL_PHYS_ADDR_REQ, 331 * so DL_PHYS_ADDR_ACK messages are also unexpected. 332 */ 333 default: 334 cmn_err(CE_WARN, "softmac: got unexpected %s", primstr); 335 break; 336 } 337 exit: 338 freemsg(mp); 339 return; 340 runt: 341 cmn_err(CE_WARN, "softmac: got runt %s", primstr); 342 freemsg(mp); 343 } 344 345 void 346 softmac_rput_process_notdata(queue_t *rq, mblk_t *mp) 347 { 348 softmac_lower_t *slp = rq->q_ptr; 349 350 switch (DB_TYPE(mp)) { 351 case M_PROTO: 352 case M_PCPROTO: 353 softmac_rput_process_proto(rq, mp); 354 break; 355 356 case M_FLUSH: 357 if (*mp->b_rptr & FLUSHR) 358 flushq(rq, FLUSHDATA); 359 if (*mp->b_rptr & FLUSHW) 360 flushq(OTHERQ(rq), FLUSHDATA); 361 putnext(rq, mp); 362 break; 363 364 case M_IOCACK: 365 case M_IOCNAK: 366 case M_COPYIN: 367 case M_COPYOUT: 368 mutex_enter(&slp->sl_mutex); 369 if (!slp->sl_pending_ioctl) { 370 mutex_exit(&slp->sl_mutex); 371 cmn_err(CE_NOTE, "softmac: got unexpected mblk " 372 "type 0x%x", DB_TYPE(mp)); 373 freemsg(mp); 374 break; 375 } 376 377 slp->sl_pending_ioctl = B_FALSE; 378 slp->sl_ack_mp = mp; 379 cv_broadcast(&slp->sl_cv); 380 mutex_exit(&slp->sl_mutex); 381 return; 382 383 default: 384 cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x", 385 DB_TYPE(mp)); 386 freemsg(mp); 387 break; 388 } 389 } 390