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.
27 #include <sys/types.h>
28 #include <inet/common.h>
29 #include <sys/stropts.h>
30 #include <sys/modctl.h>
32 #include <sys/softmac_impl.h>
34 dev_info_t
*softmac_dip
= NULL
;
35 static kmem_cache_t
*softmac_upper_cachep
;
38 * This function is a generic open(9E) entry point into the softmac for
39 * both the softmac module and the softmac driver.
41 static int softmac_cmn_open(queue_t
*, dev_t
*, int, int, cred_t
*);
44 * The following softmac_mod_xxx() functions are (9E) entry point functions for
47 static int softmac_mod_close(queue_t
*, int, cred_t
*);
48 static void softmac_mod_rput(queue_t
*, mblk_t
*);
49 static void softmac_mod_wput(queue_t
*, mblk_t
*);
50 static void softmac_mod_wsrv(queue_t
*);
53 * The following softmac_drv_xxx() functions are (9E) entry point functions for
56 static int softmac_drv_open(queue_t
*, dev_t
*, int, int, cred_t
*);
57 static int softmac_drv_close(queue_t
*, int, cred_t
*);
58 static void softmac_drv_wput(queue_t
*, mblk_t
*);
59 static void softmac_drv_wsrv(queue_t
*);
61 static int softmac_attach(dev_info_t
*, ddi_attach_cmd_t
);
62 static int softmac_detach(dev_info_t
*, ddi_detach_cmd_t
);
63 static int softmac_info(dev_info_t
*, ddi_info_cmd_t
, void *, void **);
65 static struct module_info softmac_modinfo
= {
75 * hi-water mark is 1 because of the flow control mechanism implemented in
76 * dld. Refer to the comments in dld_str.c for details.
78 static struct module_info softmac_dld_modinfo
= {
87 static struct qinit softmac_urinit
= {
88 (pfi_t
)softmac_mod_rput
, /* qi_putp */
89 (pfi_t
)NULL
, /* qi_srvp */
90 softmac_cmn_open
, /* qi_qopen */
91 softmac_mod_close
, /* qi_qclose */
93 &softmac_modinfo
/* qi_minfo */
96 static struct qinit softmac_uwinit
= {
97 (pfi_t
)softmac_mod_wput
, /* qi_putp */
98 (pfi_t
)softmac_mod_wsrv
, /* qi_srvp */
100 NULL
, /* qi_qclose */
101 NULL
, /* qi_qadmin */
102 &softmac_modinfo
/* qi_minfo */
105 static struct streamtab softmac_tab
= {
106 &softmac_urinit
, /* st_rdinit */
107 &softmac_uwinit
/* st_wrinit */
110 DDI_DEFINE_STREAM_OPS(softmac_ops
, nulldev
, nulldev
, softmac_attach
,
111 softmac_detach
, nodev
, softmac_info
, D_MP
, &softmac_tab
,
112 ddi_quiesce_not_supported
);
114 static struct qinit softmac_dld_r_qinit
= {
115 NULL
, NULL
, softmac_drv_open
, softmac_drv_close
, NULL
,
119 static struct qinit softmac_dld_w_qinit
= {
120 (pfi_t
)softmac_drv_wput
, (pfi_t
)softmac_drv_wsrv
, NULL
, NULL
, NULL
,
124 static struct fmodsw softmac_fmodsw
= {
130 static struct modldrv softmac_modldrv
= {
136 static struct modlstrmod softmac_modlstrmod
= {
142 static struct modlinkage softmac_modlinkage
= {
149 static void softmac_dedicated_rx(void *, mac_resource_handle_t
, mblk_t
*,
150 mac_header_info_t
*);
154 softmac_upper_constructor(void *buf
, void *arg
, int kmflag
)
156 softmac_upper_t
*sup
= buf
;
158 bzero(buf
, sizeof (softmac_upper_t
));
160 mutex_init(&sup
->su_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
161 cv_init(&sup
->su_cv
, NULL
, CV_DEFAULT
, NULL
);
162 mutex_init(&sup
->su_disp_mutex
, NULL
, MUTEX_DEFAULT
, NULL
);
163 cv_init(&sup
->su_disp_cv
, NULL
, CV_DEFAULT
, NULL
);
164 list_create(&sup
->su_req_list
, sizeof (softmac_switch_req_t
),
165 offsetof(softmac_switch_req_t
, ssq_req_list_node
));
171 softmac_upper_destructor(void *buf
, void *arg
)
173 softmac_upper_t
*sup
= buf
;
175 ASSERT(sup
->su_slp
== NULL
);
176 ASSERT(sup
->su_pending_head
== NULL
&& sup
->su_pending_tail
== NULL
);
177 ASSERT(!sup
->su_dlpi_pending
);
178 ASSERT(!sup
->su_active
);
179 ASSERT(!sup
->su_closing
);
180 ASSERT(sup
->su_tx_flow_mp
== NULL
);
181 ASSERT(sup
->su_tx_inprocess
== 0);
182 ASSERT(sup
->su_mode
== SOFTMAC_UNKNOWN
);
183 ASSERT(!sup
->su_tx_busy
);
184 ASSERT(!sup
->su_bound
);
185 ASSERT(!sup
->su_taskq_scheduled
);
186 ASSERT(sup
->su_tx_notify_func
== NULL
);
187 ASSERT(sup
->su_tx_notify_arg
== NULL
);
188 ASSERT(list_is_empty(&sup
->su_req_list
));
190 list_destroy(&sup
->su_req_list
);
191 mutex_destroy(&sup
->su_mutex
);
192 cv_destroy(&sup
->su_cv
);
193 mutex_destroy(&sup
->su_disp_mutex
);
194 cv_destroy(&sup
->su_disp_cv
);
202 mac_init_ops(NULL
, SOFTMAC_DEV_NAME
);
205 softmac_upper_cachep
= kmem_cache_create("softmac_upper_cache",
206 sizeof (softmac_upper_t
), 0, softmac_upper_constructor
,
207 softmac_upper_destructor
, NULL
, NULL
, NULL
, 0);
208 ASSERT(softmac_upper_cachep
!= NULL
);
210 if ((err
= mod_install(&softmac_modlinkage
)) != 0) {
226 if ((err
= mod_remove(&softmac_modlinkage
)) != 0)
229 kmem_cache_destroy(softmac_upper_cachep
);
236 _info(struct modinfo
*modinfop
)
238 return (mod_info(&softmac_modlinkage
, modinfop
));
242 softmac_cmn_open(queue_t
*rq
, dev_t
*devp
, int flag
, int sflag
, cred_t
*credp
)
244 softmac_lower_t
*slp
;
246 * This is a self-cloning driver so that each queue should only
249 if (rq
->q_ptr
!= NULL
)
252 if (sflag
== MODOPEN
) {
254 * This is the softmac module pushed over an underlying
255 * legacy device. Initialize the lower structure.
257 if ((slp
= kmem_zalloc(sizeof (*slp
), KM_NOSLEEP
)) == NULL
)
261 cv_init(&slp
->sl_cv
, NULL
, CV_DRIVER
, NULL
);
262 mutex_init(&slp
->sl_mutex
, NULL
, MUTEX_DRIVER
, NULL
);
263 slp
->sl_pending_prim
= DL_PRIM_INVAL
;
264 rq
->q_ptr
= WR(rq
)->q_ptr
= slp
;
270 * Regular device open of a softmac DLPI node. We modify
271 * the queues' q_qinfo pointer such that all future STREAMS
272 * operations will go through another set of entry points
274 rq
->q_qinfo
= &softmac_dld_r_qinit
;
275 WR(rq
)->q_qinfo
= &softmac_dld_w_qinit
;
276 return (softmac_drv_open(rq
, devp
, flag
, sflag
, credp
));
281 softmac_mod_close(queue_t
*rq
, int flags __unused
, cred_t
*credp __unused
)
283 softmac_lower_t
*slp
= rq
->q_ptr
;
286 * Call the appropriate delete routine depending on whether this is
287 * a module or device.
289 ASSERT(WR(rq
)->q_next
!= NULL
);
293 slp
->sl_softmac
= NULL
;
296 ASSERT(slp
->sl_ack_mp
== NULL
);
297 ASSERT(slp
->sl_pending_prim
== DL_PRIM_INVAL
);
298 ASSERT(slp
->sl_pending_ioctl
== B_FALSE
);
300 cv_destroy(&slp
->sl_cv
);
301 mutex_destroy(&slp
->sl_mutex
);
303 kmem_free(slp
, sizeof (*slp
));
308 softmac_mod_rput(queue_t
*rq
, mblk_t
*mp
)
310 softmac_lower_t
*slp
= rq
->q_ptr
;
311 softmac_lower_rxinfo_t
*rxinfo
;
312 union DL_primitives
*dlp
;
315 * This is the softmac module.
317 ASSERT(WR(rq
)->q_next
!= NULL
);
318 ASSERT((mp
->b_next
== NULL
) && (mp
->b_prev
== NULL
));
320 switch (DB_TYPE(mp
)) {
324 * If sl_rxinfo is non-NULL. This is dedicated-lower-stream
325 * created for fastpath. Directly call the rx callback.
327 if ((rxinfo
= slp
->sl_rxinfo
) != NULL
) {
328 rxinfo
->slr_rx(rxinfo
->slr_arg
, NULL
, mp
, NULL
);
333 * A shared-lower-stream. Some driver starts to send up
334 * packets even it not in the DL_IDLE state, where
335 * sl_softmac is not set yet. Drop the packet in this case.
337 if (slp
->sl_softmac
== NULL
) {
343 * If this message is looped back from the legacy devices,
344 * drop it as the Nemo framework will be responsible for
345 * looping it back by the mac_txloop() function.
347 if (mp
->b_flag
& MSGNOLOOP
) {
353 * This is the most common case.
355 if (DB_REF(mp
) == 1) {
356 ASSERT(slp
->sl_softmac
!= NULL
);
357 mac_rx(slp
->sl_softmac
->smac_mh
, NULL
, mp
);
360 softmac_rput_process_data(slp
, mp
);
366 if (MBLKL(mp
) < sizeof (dlp
->dl_primitive
)) {
370 dlp
= (union DL_primitives
*)mp
->b_rptr
;
371 if (dlp
->dl_primitive
== DL_UNITDATA_IND
) {
373 if ((rxinfo
= slp
->sl_rxinfo
) != NULL
) {
374 softmac_dedicated_rx(slp
->sl_sup
, NULL
, mp
,
379 cmn_err(CE_WARN
, "got unexpected %s message",
380 dl_primstr(DL_UNITDATA_IND
));
386 softmac_rput_process_notdata(rq
, slp
->sl_sup
, mp
);
392 softmac_mod_wput(queue_t
*wq
, mblk_t
*mp
)
395 * This is the softmac module
397 ASSERT(wq
->q_next
!= NULL
);
399 switch (DB_TYPE(mp
)) {
401 struct iocblk
*ioc
= (struct iocblk
*)mp
->b_rptr
;
403 switch (ioc
->ioc_cmd
) {
404 case SMAC_IOC_START
: {
405 softmac_lower_t
*slp
= wq
->q_ptr
;
406 smac_ioc_start_t
*arg
;
408 if (ioc
->ioc_count
!= sizeof (*arg
)) {
409 miocnak(wq
, mp
, 0, EINVAL
);
414 * Assign the devname and perstream handle of the
415 * specific lower stream and return it as a part
418 arg
= (smac_ioc_start_t
*)mp
->b_cont
->b_rptr
;
420 miocack(wq
, mp
, sizeof (*arg
), 0);
424 miocnak(wq
, mp
, 0, EINVAL
);
436 softmac_mod_wsrv(queue_t
*wq
)
438 softmac_lower_t
*slp
= wq
->q_ptr
;
441 * This is the softmac module
443 ASSERT(wq
->q_next
!= NULL
);
446 * Inform that the tx resource is available; mac_tx_update() will
447 * inform all the upper streams sharing this lower stream.
449 if (slp
->sl_sup
!= NULL
)
450 qenable(slp
->sl_sup
->su_wq
);
451 else if (slp
->sl_softmac
!= NULL
)
452 mac_tx_update(slp
->sl_softmac
->smac_mh
);
456 softmac_attach(dev_info_t
*dip
, ddi_attach_cmd_t cmd
)
458 ASSERT(ddi_get_instance(dip
) == 0);
460 if (cmd
!= DDI_ATTACH
)
461 return (DDI_FAILURE
);
465 return (DDI_SUCCESS
);
470 softmac_detach(dev_info_t
*dip
, ddi_detach_cmd_t cmd
)
472 if (cmd
!= DDI_DETACH
)
473 return (DDI_FAILURE
);
476 return (DDI_SUCCESS
);
481 softmac_info(dev_info_t
*dip
, ddi_info_cmd_t infocmd
, void *arg
, void **result
)
484 case DDI_INFO_DEVT2DEVINFO
:
485 if (softmac_dip
!= NULL
) {
486 *result
= softmac_dip
;
487 return (DDI_SUCCESS
);
491 case DDI_INFO_DEVT2INSTANCE
:
493 return (DDI_SUCCESS
);
497 return (DDI_FAILURE
);
502 softmac_dedicated_rx(void *arg
, mac_resource_handle_t mrh
, mblk_t
*mp
,
503 mac_header_info_t
*mhip
)
505 queue_t
*rq
= ((softmac_upper_t
*)arg
)->su_rq
;
515 softmac_drv_open(queue_t
*rq
, dev_t
*devp
, int flag
, int sflag
, cred_t
*credp
)
517 softmac_upper_t
*sup
= NULL
;
522 * This is a softmac device created for a legacy device, find the
523 * associated softmac and initialize the softmac_upper_t structure.
525 if ((err
= softmac_hold(*devp
, &softmac
)) != 0)
528 sup
= kmem_cache_alloc(softmac_upper_cachep
, KM_NOSLEEP
);
534 ASSERT(list_is_empty(&sup
->su_req_list
));
536 if ((sup
->su_tx_flow_mp
= allocb(1, BPRI_HI
)) == NULL
) {
543 sup
->su_softmac
= softmac
;
544 sup
->su_mode
= SOFTMAC_UNKNOWN
;
546 sup
->su_rxinfo
.slr_arg
= sup
;
547 sup
->su_rxinfo
.slr_rx
= softmac_dedicated_rx
;
548 sup
->su_direct_rxinfo
.slr_arg
= sup
;
549 sup
->su_direct_rxinfo
.slr_rx
= softmac_dedicated_rx
;
551 if ((err
= dld_str_open(rq
, devp
, sup
)) != 0) {
552 freeb(sup
->su_tx_flow_mp
);
553 sup
->su_tx_flow_mp
= NULL
;
561 kmem_cache_free(softmac_upper_cachep
, sup
);
562 softmac_rele(softmac
);
568 softmac_drv_close(queue_t
*rq
, int flags __unused
, cred_t
*credp __unused
)
570 softmac_upper_t
*sup
= dld_str_private(rq
);
571 softmac_t
*softmac
= sup
->su_softmac
;
573 ASSERT(WR(rq
)->q_next
== NULL
);
577 ASSERT(sup
->su_tx_inprocess
== 0);
580 * Wait until the pending request are processed by the worker thread.
582 mutex_enter(&sup
->su_disp_mutex
);
583 sup
->su_closing
= B_TRUE
;
584 while (sup
->su_dlpi_pending
)
585 cv_wait(&sup
->su_disp_cv
, &sup
->su_disp_mutex
);
586 mutex_exit(&sup
->su_disp_mutex
);
588 softmac_upperstream_close(sup
);
590 if (sup
->su_tx_flow_mp
!= NULL
) {
591 freeb(sup
->su_tx_flow_mp
);
592 sup
->su_tx_flow_mp
= NULL
;
595 if (sup
->su_active
) {
596 mutex_enter(&softmac
->smac_active_mutex
);
597 softmac
->smac_nactive
--;
598 mutex_exit(&softmac
->smac_active_mutex
);
599 sup
->su_active
= B_FALSE
;
602 sup
->su_bound
= B_FALSE
;
603 sup
->su_softmac
= NULL
;
604 sup
->su_closing
= B_FALSE
;
606 kmem_cache_free(softmac_upper_cachep
, sup
);
608 softmac_rele(softmac
);
609 return (dld_str_close(rq
));
613 softmac_drv_wput(queue_t
*wq
, mblk_t
*mp
)
615 softmac_upper_t
*sup
= dld_str_private(wq
);
618 ASSERT(wq
->q_next
== NULL
);
620 switch (DB_TYPE(mp
)) {
623 softmac_wput_data(sup
, mp
);
628 if (MBLKL(mp
) < sizeof (t_uscalar_t
)) {
633 prim
= ((union DL_primitives
*)mp
->b_rptr
)->dl_primitive
;
634 if (prim
== DL_UNITDATA_REQ
) {
635 softmac_wput_data(sup
, mp
);
639 softmac_wput_nondata(sup
, mp
);
642 softmac_wput_nondata(sup
, mp
);
648 softmac_drv_wsrv(queue_t
*wq
)
650 softmac_upper_t
*sup
= dld_str_private(wq
);
652 ASSERT(wq
->q_next
== NULL
);
654 mutex_enter(&sup
->su_mutex
);
655 if (sup
->su_mode
!= SOFTMAC_FASTPATH
) {
657 * Bump su_tx_inprocess so that su_mode won't change.
659 sup
->su_tx_inprocess
++;
660 mutex_exit(&sup
->su_mutex
);
662 mutex_enter(&sup
->su_mutex
);
663 if (--sup
->su_tx_inprocess
== 0)
664 cv_signal(&sup
->su_cv
);
665 } else if (sup
->su_tx_busy
&& SOFTMAC_CANPUTNEXT(sup
->su_slp
->sl_wq
)) {
667 * The flow-conctol of the dedicated-lower-stream is
668 * relieved. If DLD_CAPAB_DIRECT is enabled, call tx_notify
669 * callback to relieve the flow-control of the specific client,
670 * otherwise relieve the flow-control of all the upper-stream
671 * using the traditional STREAM mechanism.
673 if (sup
->su_tx_notify_func
!= NULL
) {
674 sup
->su_tx_inprocess
++;
675 mutex_exit(&sup
->su_mutex
);
676 sup
->su_tx_notify_func(sup
->su_tx_notify_arg
,
677 (mac_tx_cookie_t
)sup
);
678 mutex_enter(&sup
->su_mutex
);
679 if (--sup
->su_tx_inprocess
== 0)
680 cv_signal(&sup
->su_cv
);
682 ASSERT(sup
->su_tx_flow_mp
== NULL
);
683 VERIFY((sup
->su_tx_flow_mp
= getq(wq
)) != NULL
);
684 sup
->su_tx_busy
= B_FALSE
;
686 mutex_exit(&sup
->su_mutex
);