softmac_dev.c revision 7656:2621e50fdf4a
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
27#include <sys/types.h>
28#include <sys/dld.h>
29#include <inet/common.h>
30#include <sys/stropts.h>
31#include <sys/modctl.h>
32#include <sys/avl.h>
33#include <sys/softmac_impl.h>
34#include <sys/softmac.h>
35
36dev_info_t		*softmac_dip = NULL;
37
38static int softmac_open(queue_t *, dev_t *, int, int, cred_t *);
39static int softmac_close(queue_t *);
40static void softmac_rput(queue_t *, mblk_t *);
41static void softmac_rsrv(queue_t *);
42static void softmac_wput(queue_t *, mblk_t *);
43static void softmac_wsrv(queue_t *);
44static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
45static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
46static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
47
48static struct module_info softmac_modinfo = {
49	0,
50	SOFTMAC_DEV_NAME,
51	0,
52	INFPSZ,
53	65536,
54	1024
55};
56
57/*
58 * hi-water mark is 1 because of the flow control mechanism implemented in
59 * dld.  Refer to the comments in dld_str.c for details.
60 */
61static struct module_info softmac_dld_modinfo = {
62	0,
63	SOFTMAC_DEV_NAME,
64	0,
65	INFPSZ,
66	1,
67	0
68};
69
70static struct qinit softmac_urinit = {
71	(pfi_t)softmac_rput,	/* qi_putp */
72	(pfi_t)softmac_rsrv,	/* qi_srvp */
73	softmac_open,		/* qi_qopen */
74	softmac_close,		/* qi_qclose */
75	NULL,			/* qi_qadmin */
76	&softmac_modinfo	/* qi_minfo */
77};
78
79static struct qinit softmac_uwinit = {
80	(pfi_t)softmac_wput,	/* qi_putp */
81	(pfi_t)softmac_wsrv,	/* qi_srvp */
82	NULL,			/* qi_qopen */
83	NULL,			/* qi_qclose */
84	NULL,			/* qi_qadmin */
85	&softmac_modinfo	/* qi_minfo */
86};
87
88static struct streamtab softmac_tab = {
89	&softmac_urinit,	/* st_rdinit */
90	&softmac_uwinit		/* st_wrinit */
91};
92
93DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
94    softmac_detach, nodev, softmac_info, D_MP, &softmac_tab,
95    ddi_quiesce_not_supported);
96
97static struct qinit softmac_dld_r_qinit = {
98	NULL, NULL, dld_open, dld_close, NULL, &softmac_dld_modinfo
99};
100
101static struct qinit softmac_dld_w_qinit = {
102	(pfi_t)dld_wput, (pfi_t)dld_wsrv, NULL, NULL, NULL,
103	&softmac_dld_modinfo
104};
105
106static struct fmodsw softmac_fmodsw = {
107	SOFTMAC_DEV_NAME,
108	&softmac_tab,
109	D_MP
110};
111
112static struct modldrv softmac_modldrv = {
113	&mod_driverops,
114	"softmac driver",
115	&softmac_ops
116};
117
118static struct modlstrmod softmac_modlstrmod = {
119	&mod_strmodops,
120	"softmac module",
121	&softmac_fmodsw
122};
123
124static struct modlinkage softmac_modlinkage = {
125	MODREV_1,
126	&softmac_modlstrmod,
127	&softmac_modldrv,
128	NULL
129};
130
131int
132_init(void)
133{
134	int	err;
135
136	softmac_init();
137
138	if ((err = mod_install(&softmac_modlinkage)) != 0) {
139		softmac_fini();
140		return (err);
141	}
142
143	return (0);
144}
145
146int
147_fini(void)
148{
149	int err;
150
151	if (softmac_busy())
152		return (EBUSY);
153
154	if ((err = mod_remove(&softmac_modlinkage)) != 0)
155		return (err);
156
157	softmac_fini();
158
159	return (0);
160}
161
162int
163_info(struct modinfo *modinfop)
164{
165	return (mod_info(&softmac_modlinkage, modinfop));
166}
167
168static int
169softmac_open(queue_t *rq, dev_t *devp, int flag, int sflag, cred_t *credp)
170{
171	softmac_lower_t	*slp;
172	/*
173	 * This is a self-cloning driver so that each queue should only
174	 * get opened once.
175	 */
176	if (rq->q_ptr != NULL)
177		return (EBUSY);
178
179	if (sflag == MODOPEN) {
180		/*
181		 * This is the softmac module pushed over an underlying
182		 * legacy device.  Initialize the lower structure.
183		 */
184		if ((slp = kmem_zalloc(sizeof (*slp), KM_NOSLEEP)) == NULL)
185			return (ENOMEM);
186
187		slp->sl_wq = WR(rq);
188		cv_init(&slp->sl_cv, NULL, CV_DRIVER, NULL);
189		mutex_init(&slp->sl_mutex, NULL, MUTEX_DRIVER, NULL);
190		cv_init(&slp->sl_ctl_cv, NULL, CV_DRIVER, NULL);
191		mutex_init(&slp->sl_ctl_mutex, NULL, MUTEX_DRIVER, NULL);
192		slp->sl_pending_prim = DL_PRIM_INVAL;
193		rq->q_ptr = WR(rq)->q_ptr = slp;
194		qprocson(rq);
195		return (0);
196	}
197
198	/*
199	 * Regular device open of a softmac DLPI node.  We modify
200	 * the queues' q_qinfo pointer such that all future STREAMS
201	 * operations will go through dld's entry points (including
202	 * dld_close()).
203	 */
204	rq->q_qinfo = &softmac_dld_r_qinit;
205	WR(rq)->q_qinfo = &softmac_dld_w_qinit;
206	return (dld_open(rq, devp, flag, sflag, credp));
207}
208
209static int
210softmac_close(queue_t *rq)
211{
212	softmac_lower_t	*slp = rq->q_ptr;
213
214	/*
215	 * Call the appropriate delete routine depending on whether this is
216	 * a module or device.
217	 */
218	ASSERT(WR(rq)->q_next != NULL);
219
220	qprocsoff(rq);
221
222	slp->sl_softmac = NULL;
223	slp->sl_lh = NULL;
224
225	/*
226	 * slp->sl_handle could be non-NULL if it is in the aggregation.
227	 */
228	slp->sl_handle = (mac_resource_handle_t)NULL;
229
230	ASSERT(slp->sl_ack_mp == NULL);
231	ASSERT(slp->sl_ctl_inprogress == B_FALSE);
232	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
233	ASSERT(slp->sl_pending_ioctl == B_FALSE);
234
235	cv_destroy(&slp->sl_cv);
236	mutex_destroy(&slp->sl_mutex);
237	cv_destroy(&slp->sl_ctl_cv);
238	mutex_destroy(&slp->sl_ctl_mutex);
239
240	kmem_free(slp, sizeof (*slp));
241	return (0);
242}
243
244static void
245softmac_rput(queue_t *rq, mblk_t *mp)
246{
247	softmac_lower_t *slp = rq->q_ptr;
248	union DL_primitives *dlp;
249
250	/*
251	 * This is the softmac module.
252	 */
253	ASSERT(WR(rq)->q_next != NULL);
254	ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
255
256	switch (DB_TYPE(mp)) {
257	case M_DATA:
258		/*
259		 * Some drivers start to send up packets even if not in the
260		 * DL_IDLE state, where sl_softmac is not set yet.  Drop the
261		 * packet in this case.
262		 */
263		if (slp->sl_softmac == NULL) {
264			freemsg(mp);
265			return;
266		}
267
268		/*
269		 * This is the most common case.
270		 */
271		if (DB_REF(mp) == 1) {
272			ASSERT(slp->sl_softmac != NULL);
273			/*
274			 * We don't need any locks to protect sl_handle
275			 * because ip_input() can tolerate if sl_handle
276			 * is reset to NULL when DL_CAPAB_POLL is
277			 * disabled.
278			 */
279			mac_rx(slp->sl_softmac->smac_mh, slp->sl_handle, mp);
280			return;
281		} else {
282			softmac_rput_process_data(slp, mp);
283		}
284		break;
285	case M_PROTO:
286	case M_PCPROTO:
287		if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
288			freemsg(mp);
289			break;
290		}
291		dlp = (union DL_primitives *)mp->b_rptr;
292		if (dlp->dl_primitive == DL_UNITDATA_IND) {
293			cmn_err(CE_WARN, "got unexpected %s message",
294			    dl_primstr(DL_UNITDATA_IND));
295			freemsg(mp);
296			break;
297		}
298		/*FALLTHROUGH*/
299	default:
300		softmac_rput_process_notdata(rq, mp);
301		break;
302	}
303}
304
305/* ARGSUSED */
306static void
307softmac_rsrv(queue_t *rq)
308{
309}
310
311static void
312softmac_wput(queue_t *wq, mblk_t *mp)
313{
314	/*
315	 * This is the softmac module
316	 */
317	ASSERT(wq->q_next != NULL);
318
319	switch (DB_TYPE(mp)) {
320	case M_IOCTL: {
321		struct iocblk		*ioc = (struct iocblk *)mp->b_rptr;
322
323		switch (ioc->ioc_cmd) {
324		case SMAC_IOC_START: {
325			softmac_lower_t		*slp = wq->q_ptr;
326			smac_ioc_start_t	*arg;
327
328			if (ioc->ioc_count != sizeof (*arg)) {
329				miocnak(wq, mp, 0, EINVAL);
330				break;
331			}
332
333			/*
334			 * Assign the devname and perstream handle of the
335			 * specific lower stream and return it as a part
336			 * of the ioctl.
337			 */
338			arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
339			arg->si_slp = slp;
340
341			miocack(wq, mp, sizeof (*arg), 0);
342			break;
343		}
344		default:
345			miocnak(wq, mp, 0, EINVAL);
346			break;
347		}
348		break;
349	}
350	default:
351		freemsg(mp);
352		break;
353	}
354}
355
356static void
357softmac_wsrv(queue_t *wq)
358{
359	softmac_lower_t *slp = wq->q_ptr;
360
361	/*
362	 * This is the softmac module
363	 */
364	ASSERT(wq->q_next != NULL);
365
366	/*
367	 * Inform that the tx resource is available; mac_tx_update() will
368	 * inform all the upper streams sharing this lower stream.
369	 */
370	if (slp->sl_softmac != NULL)
371		mac_tx_update(slp->sl_softmac->smac_mh);
372}
373
374static int
375softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
376{
377	ASSERT(ddi_get_instance(dip) == 0);
378
379	if (cmd != DDI_ATTACH)
380		return (DDI_FAILURE);
381
382	softmac_dip = dip;
383
384	return (DDI_SUCCESS);
385}
386
387/* ARGSUSED */
388static int
389softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
390{
391	if (cmd != DDI_DETACH)
392		return (DDI_FAILURE);
393
394	softmac_dip = NULL;
395	return (DDI_SUCCESS);
396}
397
398/* ARGSUSED */
399static int
400softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
401{
402	switch (infocmd) {
403	case DDI_INFO_DEVT2DEVINFO:
404		if (softmac_dip != NULL) {
405			*result = softmac_dip;
406			return (DDI_SUCCESS);
407		}
408		break;
409
410	case DDI_INFO_DEVT2INSTANCE:
411		*result = NULL;
412		return (DDI_SUCCESS);
413
414	}
415
416	return (DDI_FAILURE);
417}
418