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]
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>
32 softmac_send_notify_req(softmac_lower_t
*slp
, uint32_t notifications
)
37 * create notify req message and send it down
39 reqmp
= mexchange(NULL
, NULL
, DL_NOTIFY_REQ_SIZE
, M_PROTO
,
44 ((dl_notify_req_t
*)reqmp
->b_rptr
)->dl_notifications
= notifications
;
46 return (softmac_proto_tx(slp
, reqmp
, NULL
));
50 softmac_send_bind_req(softmac_lower_t
*slp
, uint_t sap
)
56 * create bind req message and send it down
58 reqmp
= mexchange(NULL
, NULL
, DL_BIND_REQ_SIZE
, M_PROTO
, DL_BIND_REQ
);
62 bind
= (dl_bind_req_t
*)reqmp
->b_rptr
;
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
));
73 softmac_send_unbind_req(softmac_lower_t
*slp
)
78 * create unbind req message and send it down
80 reqmp
= mexchange(NULL
, NULL
, DL_UNBIND_REQ_SIZE
, M_PROTO
,
85 return (softmac_proto_tx(slp
, reqmp
, NULL
));
89 softmac_send_promisc_req(softmac_lower_t
*slp
, t_uscalar_t level
, boolean_t on
)
96 * create promisc message and send it down
99 dl_prim
= DL_PROMISCON_REQ
;
100 size
= DL_PROMISCON_REQ_SIZE
;
102 dl_prim
= DL_PROMISCOFF_REQ
;
103 size
= DL_PROMISCOFF_REQ_SIZE
;
106 reqmp
= mexchange(NULL
, NULL
, size
, M_PROTO
, dl_prim
);
111 ((dl_promiscon_req_t
*)reqmp
->b_rptr
)->dl_level
= level
;
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
));
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
;
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
;
146 size
= sizeof (dl_enabmulti_req_t
) + addr_length
;
147 dl_prim
= DL_ENABMULTI_REQ
;
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
);
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
);
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
;
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
;
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
);
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
;
199 return (softmac_proto_tx(slp
, reqmp
, NULL
));
203 softmac_m_ioctl(void *arg
, queue_t
*wq
, mblk_t
*mp
)
205 softmac_lower_t
*slp
= ((softmac_t
*)arg
)->smac_lower
;
209 softmac_ioctl_tx(slp
, mp
, &ackmp
);
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
)
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",
236 mac_unicst_update(softmac
->smac_mh
, mp
->b_rptr
+ addroff
);
239 case DL_NOTE_LINK_UP
:
240 mac_link_update(softmac
->smac_mh
, LINK_STATE_UP
);
243 case DL_NOTE_LINK_DOWN
:
244 mac_link_update(softmac
->smac_mh
, LINK_STATE_DOWN
);
252 softmac_notify_thread(void *arg
)
254 softmac_t
*softmac
= arg
;
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
)) {
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
);
276 softmac
->smac_notify_head
= softmac
->smac_notify_tail
= NULL
;
277 mutex_exit(&softmac
->smac_mutex
);
282 softmac_process_notify_ind(softmac
, mp
);
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
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
);
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
;
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
);
319 softmac_process_dlpi(softmac_lower_t
*slp
, mblk_t
*mp
, uint_t minlen
,
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
);
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
);
340 slp
->sl_pending_prim
= DL_PRIM_INVAL
;
342 cv_signal(&slp
->sl_cv
);
343 mutex_exit(&slp
->sl_mutex
);
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
);
354 if (len
< sizeof (t_uscalar_t
)) {
355 cmn_err(CE_WARN
, "softmac: got runt DLPI message");
359 primstr
= dl_primstr(dlp
->dl_primitive
);
361 switch (dlp
->dl_primitive
) {
363 if (len
< DL_OK_ACK_SIZE
)
366 softmac_process_dlpi(slp
, mp
, DL_OK_ACK_SIZE
,
367 dlp
->ok_ack
.dl_correct_primitive
);
371 if (len
< DL_ERROR_ACK_SIZE
)
374 softmac_process_dlpi(slp
, mp
, DL_ERROR_ACK_SIZE
,
375 dlp
->error_ack
.dl_error_primitive
);
379 if (len
< DL_NOTIFY_IND_SIZE
)
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;
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
);
408 softmac_process_dlpi(slp
, mp
, DL_NOTIFY_ACK_SIZE
,
412 case DL_CAPABILITY_ACK
:
413 softmac_process_dlpi(slp
, mp
, DL_CAPABILITY_ACK_SIZE
,
418 softmac_process_dlpi(slp
, mp
, DL_BIND_ACK_SIZE
, DL_BIND_REQ
);
422 softmac_process_dlpi(slp
, mp
, DL_CONTROL_ACK_SIZE
,
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.
435 cmn_err(CE_WARN
, "softmac: got unexpected %s", primstr
);
442 cmn_err(CE_WARN
, "softmac: got runt %s", primstr
);
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
)) {
457 * If this is a shared-lower-stream, pass it to softmac to
461 softmac_rput_process_proto(rq
, mp
);
466 * Dedicated-lower-stream.
468 dlp
= (union DL_primitives
*)mp
->b_rptr
;
469 ASSERT(len
>= sizeof (dlp
->dl_primitive
));
470 switch (dlp
->dl_primitive
) {
472 if (len
< DL_OK_ACK_SIZE
)
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
480 if (dlp
->ok_ack
.dl_correct_primitive
== DL_UNBIND_REQ
) {
481 softmac_rput_process_proto(rq
, mp
);
485 putnext(sup
->su_rq
, mp
);
488 if (len
< DL_ERROR_ACK_SIZE
)
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
==
498 softmac_rput_process_proto(rq
, mp
);
502 putnext(sup
->su_rq
, mp
);
505 case DL_CAPABILITY_ACK
:
506 softmac_rput_process_proto(rq
, mp
);
509 putnext(sup
->su_rq
, mp
);
514 if (*mp
->b_rptr
& FLUSHR
)
515 flushq(rq
, FLUSHDATA
);
516 if (*mp
->b_rptr
& FLUSHW
)
517 flushq(OTHERQ(rq
), FLUSHDATA
);
526 putnext(sup
->su_rq
, mp
);
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
));
539 slp
->sl_pending_ioctl
= B_FALSE
;
541 cv_broadcast(&slp
->sl_cv
);
542 mutex_exit(&slp
->sl_mutex
);
546 cmn_err(CE_NOTE
, "softmac: got unsupported mblk type 0x%x",
553 cmn_err(CE_WARN
, "softmac: got runt %s", dl_primstr(dlp
->dl_primitive
));