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 #pragma ident	"%Z%%M%	%I%	%E% SMI"
27 
28 #include <sys/types.h>
29 #include <sys/dld.h>
30 #include <inet/common.h>
31 #include <sys/stropts.h>
32 #include <sys/modctl.h>
33 #include <sys/avl.h>
34 #include <sys/softmac_impl.h>
35 #include <sys/softmac.h>
36 
37 dev_info_t		*softmac_dip = NULL;
38 
39 static int softmac_open(queue_t *, dev_t *, int, int, cred_t *);
40 static int softmac_close(queue_t *);
41 static void softmac_rput(queue_t *, mblk_t *);
42 static void softmac_rsrv(queue_t *);
43 static void softmac_wput(queue_t *, mblk_t *);
44 static void softmac_wsrv(queue_t *);
45 static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
46 static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
47 static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
48 
49 static struct module_info softmac_modinfo = {
50 	0,
51 	SOFTMAC_DEV_NAME,
52 	0,
53 	INFPSZ,
54 	65536,
55 	1024
56 };
57 
58 /*
59  * hi-water mark is 1 because of the flow control mechanism implemented in
60  * dld.  Refer to the comments in dld_str.c for details.
61  */
62 static struct module_info softmac_dld_modinfo = {
63 	0,
64 	SOFTMAC_DEV_NAME,
65 	0,
66 	INFPSZ,
67 	1,
68 	0
69 };
70 
71 static struct qinit softmac_urinit = {
72 	(pfi_t)softmac_rput,	/* qi_putp */
73 	(pfi_t)softmac_rsrv,	/* qi_srvp */
74 	softmac_open,		/* qi_qopen */
75 	softmac_close,		/* qi_qclose */
76 	NULL,			/* qi_qadmin */
77 	&softmac_modinfo	/* qi_minfo */
78 };
79 
80 static struct qinit softmac_uwinit = {
81 	(pfi_t)softmac_wput,	/* qi_putp */
82 	(pfi_t)softmac_wsrv,	/* qi_srvp */
83 	NULL,			/* qi_qopen */
84 	NULL,			/* qi_qclose */
85 	NULL,			/* qi_qadmin */
86 	&softmac_modinfo	/* qi_minfo */
87 };
88 
89 static struct streamtab softmac_tab = {
90 	&softmac_urinit,	/* st_rdinit */
91 	&softmac_uwinit		/* st_wrinit */
92 };
93 
94 DDI_DEFINE_STREAM_OPS(softmac_ops, nulldev, nulldev, softmac_attach,
95     softmac_detach, nodev, softmac_info, D_MP, &softmac_tab);
96 
97 static struct qinit softmac_dld_r_qinit = {
98 	NULL, NULL, dld_open, dld_close, NULL, &softmac_dld_modinfo
99 };
100 
101 static struct qinit softmac_dld_w_qinit = {
102 	(pfi_t)dld_wput, (pfi_t)dld_wsrv, NULL, NULL, NULL,
103 	&softmac_dld_modinfo
104 };
105 
106 static struct fmodsw softmac_fmodsw = {
107 	SOFTMAC_DEV_NAME,
108 	&softmac_tab,
109 	D_MP
110 };
111 
112 static struct modldrv softmac_modldrv = {
113 	&mod_driverops,
114 	"softmac driver",
115 	&softmac_ops
116 };
117 
118 static struct modlstrmod softmac_modlstrmod = {
119 	&mod_strmodops,
120 	"softmac module",
121 	&softmac_fmodsw
122 };
123 
124 static struct modlinkage softmac_modlinkage = {
125 	MODREV_1,
126 	&softmac_modlstrmod,
127 	&softmac_modldrv,
128 	NULL
129 };
130 
131 int
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 
146 int
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 
162 int
163 _info(struct modinfo *modinfop)
164 {
165 	return (mod_info(&softmac_modlinkage, modinfop));
166 }
167 
168 static int
169 softmac_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 
209 static int
210 softmac_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 
244 static void
245 softmac_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 */
306 static void
307 softmac_rsrv(queue_t *rq)
308 {
309 }
310 
311 static void
312 softmac_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 
356 static void
357 softmac_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 
374 static int
375 softmac_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 */
388 static int
389 softmac_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 */
399 static int
400 softmac_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