Merge remote-tracking branch 'origin/master'
[unleashed/lotheac.git] / usr / src / uts / common / io / softmac / softmac_dev.c
blobbbb3e9ad3e3291f0aea8272f6eaf704c0a28c569
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.
27 #include <sys/types.h>
28 #include <inet/common.h>
29 #include <sys/stropts.h>
30 #include <sys/modctl.h>
31 #include <sys/dld.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
45 * the softmac module.
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
54 * the softmac driver.
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 = {
67 SOFTMAC_DEV_NAME,
69 INFPSZ,
70 65536,
71 1024
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 = {
80 SOFTMAC_DEV_NAME,
82 INFPSZ,
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 */
92 NULL, /* qi_qadmin */
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 */
99 NULL, /* qi_qopen */
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,
116 &softmac_dld_modinfo
119 static struct qinit softmac_dld_w_qinit = {
120 (pfi_t)softmac_drv_wput, (pfi_t)softmac_drv_wsrv, NULL, NULL, NULL,
121 &softmac_dld_modinfo
124 static struct fmodsw softmac_fmodsw = {
125 SOFTMAC_DEV_NAME,
126 &softmac_tab,
127 D_MP
130 static struct modldrv softmac_modldrv = {
131 &mod_driverops,
132 "softmac driver",
133 &softmac_ops
136 static struct modlstrmod softmac_modlstrmod = {
137 &mod_strmodops,
138 "softmac module",
139 &softmac_fmodsw
142 static struct modlinkage softmac_modlinkage = {
143 MODREV_1,
144 &softmac_modlstrmod,
145 &softmac_modldrv,
146 NULL
149 static void softmac_dedicated_rx(void *, mac_resource_handle_t, mblk_t *,
150 mac_header_info_t *);
152 /*ARGSUSED*/
153 static int
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));
166 return (0);
169 /*ARGSUSED*/
170 static void
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);
198 _init(void)
200 int err;
202 mac_init_ops(NULL, SOFTMAC_DEV_NAME);
203 softmac_init();
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) {
211 softmac_fini();
212 return (err);
215 return (0);
219 _fini(void)
221 int err;
223 if (softmac_busy())
224 return (EBUSY);
226 if ((err = mod_remove(&softmac_modlinkage)) != 0)
227 return (err);
229 kmem_cache_destroy(softmac_upper_cachep);
230 softmac_fini();
232 return (0);
236 _info(struct modinfo *modinfop)
238 return (mod_info(&softmac_modlinkage, modinfop));
241 static int
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
247 * get opened once.
249 if (rq->q_ptr != NULL)
250 return (EBUSY);
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)
258 return (ENOMEM);
260 slp->sl_wq = WR(rq);
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;
265 qprocson(rq);
266 return (0);
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));
279 /* ARGSUSED */
280 static int
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);
291 qprocsoff(rq);
293 slp->sl_softmac = NULL;
294 slp->sl_lh = 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));
304 return (0);
307 static void
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)) {
321 case M_DATA: {
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);
329 break;
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) {
338 freemsg(mp);
339 return;
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) {
348 freemsg(mp);
349 return;
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);
358 return;
359 } else {
360 softmac_rput_process_data(slp, mp);
362 break;
364 case M_PROTO:
365 case M_PCPROTO:
366 if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
367 freemsg(mp);
368 break;
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,
375 NULL);
376 break;
379 cmn_err(CE_WARN, "got unexpected %s message",
380 dl_primstr(DL_UNITDATA_IND));
381 freemsg(mp);
382 break;
384 /*FALLTHROUGH*/
385 default:
386 softmac_rput_process_notdata(rq, slp->sl_sup, mp);
387 break;
391 static void
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)) {
400 case M_IOCTL: {
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);
410 break;
414 * Assign the devname and perstream handle of the
415 * specific lower stream and return it as a part
416 * of the ioctl.
418 arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
419 arg->si_slp = slp;
420 miocack(wq, mp, sizeof (*arg), 0);
421 break;
423 default:
424 miocnak(wq, mp, 0, EINVAL);
425 break;
427 break;
429 default:
430 freemsg(mp);
431 break;
435 static void
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);
455 static int
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);
463 softmac_dip = dip;
465 return (DDI_SUCCESS);
468 /* ARGSUSED */
469 static int
470 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
472 if (cmd != DDI_DETACH)
473 return (DDI_FAILURE);
475 softmac_dip = NULL;
476 return (DDI_SUCCESS);
479 /* ARGSUSED */
480 static int
481 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
483 switch (infocmd) {
484 case DDI_INFO_DEVT2DEVINFO:
485 if (softmac_dip != NULL) {
486 *result = softmac_dip;
487 return (DDI_SUCCESS);
489 break;
491 case DDI_INFO_DEVT2INSTANCE:
492 *result = NULL;
493 return (DDI_SUCCESS);
497 return (DDI_FAILURE);
500 /*ARGSUSED*/
501 static void
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;
507 if (canputnext(rq))
508 putnext(rq, mp);
509 else
510 freemsg(mp);
513 /*ARGSUSED*/
514 static int
515 softmac_drv_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
517 softmac_upper_t *sup = NULL;
518 softmac_t *softmac;
519 int err = 0;
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)
526 return (err);
528 sup = kmem_cache_alloc(softmac_upper_cachep, KM_NOSLEEP);
529 if (sup == NULL) {
530 err = ENOMEM;
531 goto fail;
534 ASSERT(list_is_empty(&sup->su_req_list));
536 if ((sup->su_tx_flow_mp = allocb(1, BPRI_HI)) == NULL) {
537 err = ENOMEM;
538 goto fail;
541 sup->su_rq = rq;
542 sup->su_wq = WR(rq);
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;
554 goto fail;
557 return (0);
559 fail:
560 if (sup != NULL)
561 kmem_cache_free(softmac_upper_cachep, sup);
562 softmac_rele(softmac);
563 return (err);
566 /* ARGSUSED */
567 static int
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);
575 qprocsoff(rq);
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));
612 static void
613 softmac_drv_wput(queue_t *wq, mblk_t *mp)
615 softmac_upper_t *sup = dld_str_private(wq);
616 t_uscalar_t prim;
618 ASSERT(wq->q_next == NULL);
620 switch (DB_TYPE(mp)) {
621 case M_DATA:
622 case M_MULTIDATA:
623 softmac_wput_data(sup, mp);
624 break;
625 case M_PROTO:
626 case M_PCPROTO:
628 if (MBLKL(mp) < sizeof (t_uscalar_t)) {
629 freemsg(mp);
630 return;
633 prim = ((union DL_primitives *)mp->b_rptr)->dl_primitive;
634 if (prim == DL_UNITDATA_REQ) {
635 softmac_wput_data(sup, mp);
636 return;
639 softmac_wput_nondata(sup, mp);
640 break;
641 default:
642 softmac_wput_nondata(sup, mp);
643 break;
647 static void
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);
661 dld_wsrv(wq);
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);