Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / softmac / softmac_ctl.c
blobd4c8afa8ceaa60dc76dff21ef520d1fc826d3012
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 #include <sys/stropts.h>
27 #include <sys/strsubr.h>
28 #include <sys/callb.h>
29 #include <sys/softmac_impl.h>
31 int
32 softmac_send_notify_req(softmac_lower_t *slp, uint32_t notifications)
34 mblk_t *reqmp;
37 * create notify req message and send it down
39 reqmp = mexchange(NULL, NULL, DL_NOTIFY_REQ_SIZE, M_PROTO,
40 DL_NOTIFY_REQ);
41 if (reqmp == NULL)
42 return (ENOMEM);
44 ((dl_notify_req_t *)reqmp->b_rptr)->dl_notifications = notifications;
46 return (softmac_proto_tx(slp, reqmp, NULL));
49 int
50 softmac_send_bind_req(softmac_lower_t *slp, uint_t sap)
52 dl_bind_req_t *bind;
53 mblk_t *reqmp;
56 * create bind req message and send it down
58 reqmp = mexchange(NULL, NULL, DL_BIND_REQ_SIZE, M_PROTO, DL_BIND_REQ);
59 if (reqmp == NULL)
60 return (ENOMEM);
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;
69 return (softmac_proto_tx(slp, reqmp, NULL));
72 int
73 softmac_send_unbind_req(softmac_lower_t *slp)
75 mblk_t *reqmp;
78 * create unbind req message and send it down
80 reqmp = mexchange(NULL, NULL, DL_UNBIND_REQ_SIZE, M_PROTO,
81 DL_UNBIND_REQ);
82 if (reqmp == NULL)
83 return (ENOMEM);
85 return (softmac_proto_tx(slp, reqmp, NULL));
88 int
89 softmac_send_promisc_req(softmac_lower_t *slp, t_uscalar_t level, boolean_t on)
91 mblk_t *reqmp;
92 size_t size;
93 t_uscalar_t dl_prim;
96 * create promisc message and send it down
98 if (on) {
99 dl_prim = DL_PROMISCON_REQ;
100 size = DL_PROMISCON_REQ_SIZE;
101 } else {
102 dl_prim = DL_PROMISCOFF_REQ;
103 size = DL_PROMISCOFF_REQ_SIZE;
106 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
107 if (reqmp == NULL)
108 return (ENOMEM);
110 if (on)
111 ((dl_promiscon_req_t *)reqmp->b_rptr)->dl_level = level;
112 else
113 ((dl_promiscoff_req_t *)reqmp->b_rptr)->dl_level = level;
115 return (softmac_proto_tx(slp, reqmp, NULL));
119 softmac_m_promisc(void *arg, boolean_t on)
121 softmac_t *softmac = arg;
122 softmac_lower_t *slp = softmac->smac_lower;
124 ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
125 ASSERT(slp != NULL);
126 return (softmac_send_promisc_req(slp, DL_PROMISC_PHYS, on));
130 softmac_m_multicst(void *arg, boolean_t add, const uint8_t *mca)
132 softmac_t *softmac = arg;
133 softmac_lower_t *slp;
134 dl_enabmulti_req_t *enabmulti;
135 dl_disabmulti_req_t *disabmulti;
136 mblk_t *reqmp;
137 t_uscalar_t dl_prim;
138 uint32_t size, addr_length;
140 ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
142 * create multicst message and send it down
144 addr_length = softmac->smac_addrlen;
145 if (add) {
146 size = sizeof (dl_enabmulti_req_t) + addr_length;
147 dl_prim = DL_ENABMULTI_REQ;
148 } else {
149 size = sizeof (dl_disabmulti_req_t) + addr_length;
150 dl_prim = DL_DISABMULTI_REQ;
153 reqmp = mexchange(NULL, NULL, size, M_PROTO, dl_prim);
154 if (reqmp == NULL)
155 return (ENOMEM);
157 if (add) {
158 enabmulti = (dl_enabmulti_req_t *)reqmp->b_rptr;
159 enabmulti->dl_addr_offset = sizeof (dl_enabmulti_req_t);
160 enabmulti->dl_addr_length = addr_length;
161 (void) memcpy(&enabmulti[1], mca, addr_length);
162 } else {
163 disabmulti = (dl_disabmulti_req_t *)reqmp->b_rptr;
164 disabmulti->dl_addr_offset = sizeof (dl_disabmulti_req_t);
165 disabmulti->dl_addr_length = addr_length;
166 (void) memcpy(&disabmulti[1], mca, addr_length);
169 slp = softmac->smac_lower;
170 ASSERT(slp != NULL);
171 return (softmac_proto_tx(slp, reqmp, NULL));
175 softmac_m_unicst(void *arg, const uint8_t *macaddr)
177 softmac_t *softmac = arg;
178 softmac_lower_t *slp;
179 dl_set_phys_addr_req_t *phyaddr;
180 mblk_t *reqmp;
181 size_t size;
183 ASSERT(MAC_PERIM_HELD(softmac->smac_mh));
185 * create set_phys_addr message and send it down
187 size = DL_SET_PHYS_ADDR_REQ_SIZE + softmac->smac_addrlen;
188 reqmp = mexchange(NULL, NULL, size, M_PROTO, DL_SET_PHYS_ADDR_REQ);
189 if (reqmp == NULL)
190 return (ENOMEM);
192 phyaddr = (dl_set_phys_addr_req_t *)reqmp->b_rptr;
193 phyaddr->dl_addr_offset = sizeof (dl_set_phys_addr_req_t);
194 phyaddr->dl_addr_length = softmac->smac_addrlen;
195 (void) memcpy(&phyaddr[1], macaddr, softmac->smac_addrlen);
197 slp = softmac->smac_lower;
198 ASSERT(slp != NULL);
199 return (softmac_proto_tx(slp, reqmp, NULL));
202 void
203 softmac_m_ioctl(void *arg, queue_t *wq, mblk_t *mp)
205 softmac_lower_t *slp = ((softmac_t *)arg)->smac_lower;
206 mblk_t *ackmp;
208 ASSERT(slp != NULL);
209 softmac_ioctl_tx(slp, mp, &ackmp);
210 qreply(wq, ackmp);
213 static void
214 softmac_process_notify_ind(softmac_t *softmac, mblk_t *mp)
216 dl_notify_ind_t *dlnip = (dl_notify_ind_t *)mp->b_rptr;
217 uint_t addroff, addrlen;
219 ASSERT(dlnip->dl_primitive == DL_NOTIFY_IND);
221 switch (dlnip->dl_notification) {
222 case DL_NOTE_PHYS_ADDR:
223 if (dlnip->dl_data != DL_CURR_PHYS_ADDR)
224 break;
226 addroff = dlnip->dl_addr_offset;
227 addrlen = dlnip->dl_addr_length - softmac->smac_saplen;
228 if (addroff == 0 || addrlen != softmac->smac_addrlen ||
229 !MBLKIN(mp, addroff, addrlen)) {
230 cmn_err(CE_NOTE, "softmac: got malformed "
231 "DL_NOTIFY_IND; length/offset %d/%d",
232 addrlen, addroff);
233 break;
236 mac_unicst_update(softmac->smac_mh, mp->b_rptr + addroff);
237 break;
239 case DL_NOTE_LINK_UP:
240 mac_link_update(softmac->smac_mh, LINK_STATE_UP);
241 break;
243 case DL_NOTE_LINK_DOWN:
244 mac_link_update(softmac->smac_mh, LINK_STATE_DOWN);
245 break;
248 freemsg(mp);
251 void
252 softmac_notify_thread(void *arg)
254 softmac_t *softmac = arg;
255 callb_cpr_t cprinfo;
257 CALLB_CPR_INIT(&cprinfo, &softmac->smac_mutex, callb_generic_cpr,
258 "softmac_notify_thread");
260 mutex_enter(&softmac->smac_mutex);
263 * Quit the thread if smac_mh is unregistered.
265 while (softmac->smac_mh != NULL &&
266 !(softmac->smac_flags & SOFTMAC_NOTIFY_QUIT)) {
267 mblk_t *mp, *nextmp;
269 if ((mp = softmac->smac_notify_head) == NULL) {
270 CALLB_CPR_SAFE_BEGIN(&cprinfo);
271 cv_wait(&softmac->smac_cv, &softmac->smac_mutex);
272 CALLB_CPR_SAFE_END(&cprinfo, &softmac->smac_mutex);
273 continue;
276 softmac->smac_notify_head = softmac->smac_notify_tail = NULL;
277 mutex_exit(&softmac->smac_mutex);
279 while (mp != NULL) {
280 nextmp = mp->b_next;
281 mp->b_next = NULL;
282 softmac_process_notify_ind(softmac, mp);
283 mp = nextmp;
285 mutex_enter(&softmac->smac_mutex);
289 * The softmac is being destroyed, simply free all of the DL_NOTIFY_IND
290 * messages left in the queue which did not have the chance to be
291 * processed.
293 freemsgchain(softmac->smac_notify_head);
294 softmac->smac_notify_head = softmac->smac_notify_tail = NULL;
295 softmac->smac_notify_thread = NULL;
296 cv_broadcast(&softmac->smac_cv);
297 CALLB_CPR_EXIT(&cprinfo);
298 thread_exit();
301 static void
302 softmac_enqueue_notify_ind(queue_t *rq, mblk_t *mp)
304 softmac_lower_t *slp = rq->q_ptr;
305 softmac_t *softmac = slp->sl_softmac;
307 mutex_enter(&softmac->smac_mutex);
308 if (softmac->smac_notify_tail == NULL) {
309 softmac->smac_notify_head = softmac->smac_notify_tail = mp;
310 } else {
311 softmac->smac_notify_tail->b_next = mp;
312 softmac->smac_notify_tail = mp;
314 cv_broadcast(&softmac->smac_cv);
315 mutex_exit(&softmac->smac_mutex);
318 static void
319 softmac_process_dlpi(softmac_lower_t *slp, mblk_t *mp, uint_t minlen,
320 t_uscalar_t reqprim)
322 const char *ackname;
324 ackname = dl_primstr(((union DL_primitives *)mp->b_rptr)->dl_primitive);
326 if (MBLKL(mp) < minlen) {
327 cmn_err(CE_WARN, "softmac: got short %s", ackname);
328 freemsg(mp);
329 return;
332 mutex_enter(&slp->sl_mutex);
333 if (slp->sl_pending_prim != reqprim) {
334 cmn_err(CE_NOTE, "softmac: got unexpected %s", ackname);
335 mutex_exit(&slp->sl_mutex);
336 freemsg(mp);
337 return;
340 slp->sl_pending_prim = DL_PRIM_INVAL;
341 slp->sl_ack_mp = mp;
342 cv_signal(&slp->sl_cv);
343 mutex_exit(&slp->sl_mutex);
346 void
347 softmac_rput_process_proto(queue_t *rq, mblk_t *mp)
349 softmac_lower_t *slp = rq->q_ptr;
350 union DL_primitives *dlp = (union DL_primitives *)mp->b_rptr;
351 ssize_t len = MBLKL(mp);
352 const char *primstr;
354 if (len < sizeof (t_uscalar_t)) {
355 cmn_err(CE_WARN, "softmac: got runt DLPI message");
356 goto exit;
359 primstr = dl_primstr(dlp->dl_primitive);
361 switch (dlp->dl_primitive) {
362 case DL_OK_ACK:
363 if (len < DL_OK_ACK_SIZE)
364 goto runt;
366 softmac_process_dlpi(slp, mp, DL_OK_ACK_SIZE,
367 dlp->ok_ack.dl_correct_primitive);
368 return;
370 case DL_ERROR_ACK:
371 if (len < DL_ERROR_ACK_SIZE)
372 goto runt;
374 softmac_process_dlpi(slp, mp, DL_ERROR_ACK_SIZE,
375 dlp->error_ack.dl_error_primitive);
376 return;
378 case DL_NOTIFY_IND:
379 if (len < DL_NOTIFY_IND_SIZE)
380 goto runt;
383 * Enqueue all the DL_NOTIFY_IND messages and process them
384 * in another separate thread to avoid deadlock. Here is an
385 * example of the deadlock scenario:
387 * Thread A: mac_promisc_set()->softmac_m_promisc()
389 * The softmac driver waits for the ACK of the
390 * DL_PROMISC_PHYS request with the MAC perimeter;
392 * Thread B:
394 * The driver handles the DL_PROMISC_PHYS request. Before
395 * it sends back the ACK, it could first send a
396 * DL_NOTE_PROMISC_ON_PHYS notification.
398 * Since DL_NOTIFY_IND could eventually cause softmac to call
399 * mac_xxx_update(), which requires MAC perimeter, this would
400 * cause deadlock between the two threads. Enqueuing the
401 * DL_NOTIFY_IND message and defer its processing would
402 * avoid the potential deadlock.
404 softmac_enqueue_notify_ind(rq, mp);
405 return;
407 case DL_NOTIFY_ACK:
408 softmac_process_dlpi(slp, mp, DL_NOTIFY_ACK_SIZE,
409 DL_NOTIFY_REQ);
410 return;
412 case DL_CAPABILITY_ACK:
413 softmac_process_dlpi(slp, mp, DL_CAPABILITY_ACK_SIZE,
414 DL_CAPABILITY_REQ);
415 return;
417 case DL_BIND_ACK:
418 softmac_process_dlpi(slp, mp, DL_BIND_ACK_SIZE, DL_BIND_REQ);
419 return;
421 case DL_CONTROL_ACK:
422 softmac_process_dlpi(slp, mp, DL_CONTROL_ACK_SIZE,
423 DL_CONTROL_REQ);
424 return;
426 case DL_UNITDATA_IND:
427 case DL_PHYS_ADDR_ACK:
429 * a. Because the stream is in DLIOCRAW mode,
430 * DL_UNITDATA_IND messages are not expected.
431 * b. The lower stream should not receive DL_PHYS_ADDR_REQ,
432 * so DL_PHYS_ADDR_ACK messages are also unexpected.
434 default:
435 cmn_err(CE_WARN, "softmac: got unexpected %s", primstr);
436 break;
438 exit:
439 freemsg(mp);
440 return;
441 runt:
442 cmn_err(CE_WARN, "softmac: got runt %s", primstr);
443 freemsg(mp);
446 void
447 softmac_rput_process_notdata(queue_t *rq, softmac_upper_t *sup, mblk_t *mp)
449 softmac_lower_t *slp = rq->q_ptr;
450 union DL_primitives *dlp;
451 ssize_t len = MBLKL(mp);
453 switch (DB_TYPE(mp)) {
454 case M_PROTO:
455 case M_PCPROTO:
457 * If this is a shared-lower-stream, pass it to softmac to
458 * process.
460 if (sup == NULL) {
461 softmac_rput_process_proto(rq, mp);
462 break;
466 * Dedicated-lower-stream.
468 dlp = (union DL_primitives *)mp->b_rptr;
469 ASSERT(len >= sizeof (dlp->dl_primitive));
470 switch (dlp->dl_primitive) {
471 case DL_OK_ACK:
472 if (len < DL_OK_ACK_SIZE)
473 goto runt;
476 * If this is a DL_OK_ACK for a DL_UNBIND_REQ, pass it
477 * to softmac to process, otherwise directly pass it to
478 * the upper stream.
480 if (dlp->ok_ack.dl_correct_primitive == DL_UNBIND_REQ) {
481 softmac_rput_process_proto(rq, mp);
482 break;
485 putnext(sup->su_rq, mp);
486 break;
487 case DL_ERROR_ACK:
488 if (len < DL_ERROR_ACK_SIZE)
489 goto runt;
492 * If this is a DL_ERROR_ACK for a DL_UNBIND_REQ, pass
493 * it to softmac to process, otherwise directly pass it
494 * to the upper stream.
496 if (dlp->error_ack.dl_error_primitive ==
497 DL_UNBIND_REQ) {
498 softmac_rput_process_proto(rq, mp);
499 break;
502 putnext(sup->su_rq, mp);
503 break;
504 case DL_BIND_ACK:
505 case DL_CAPABILITY_ACK:
506 softmac_rput_process_proto(rq, mp);
507 break;
508 default:
509 putnext(sup->su_rq, mp);
510 break;
512 break;
513 case M_FLUSH:
514 if (*mp->b_rptr & FLUSHR)
515 flushq(rq, FLUSHDATA);
516 if (*mp->b_rptr & FLUSHW)
517 flushq(OTHERQ(rq), FLUSHDATA);
518 putnext(rq, mp);
519 break;
521 case M_IOCACK:
522 case M_IOCNAK:
523 case M_COPYIN:
524 case M_COPYOUT:
525 if (sup != NULL) {
526 putnext(sup->su_rq, mp);
527 break;
530 mutex_enter(&slp->sl_mutex);
531 if (!slp->sl_pending_ioctl) {
532 mutex_exit(&slp->sl_mutex);
533 cmn_err(CE_NOTE, "softmac: got unexpected mblk "
534 "type 0x%x", DB_TYPE(mp));
535 freemsg(mp);
536 break;
539 slp->sl_pending_ioctl = B_FALSE;
540 slp->sl_ack_mp = mp;
541 cv_broadcast(&slp->sl_cv);
542 mutex_exit(&slp->sl_mutex);
543 break;
545 default:
546 cmn_err(CE_NOTE, "softmac: got unsupported mblk type 0x%x",
547 DB_TYPE(mp));
548 freemsg(mp);
549 break;
551 return;
552 runt:
553 cmn_err(CE_WARN, "softmac: got runt %s", dl_primstr(dlp->dl_primitive));
554 freemsg(mp);