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 
36 dev_info_t		*softmac_dip = NULL;
37 
38 static int softmac_open(queue_t *, dev_t *, int, int, cred_t *);
39 static int softmac_close(queue_t *);
40 static void softmac_rput(queue_t *, mblk_t *);
41 static void softmac_rsrv(queue_t *);
42 static void softmac_wput(queue_t *, mblk_t *);
43 static void softmac_wsrv(queue_t *);
44 static int softmac_attach(dev_info_t *, ddi_attach_cmd_t);
45 static int softmac_detach(dev_info_t *, ddi_detach_cmd_t);
46 static int softmac_info(dev_info_t *, ddi_info_cmd_t, void *, void **);
47 
48 static 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  */
61 static struct module_info softmac_dld_modinfo = {
62 	0,
63 	SOFTMAC_DEV_NAME,
64 	0,
65 	INFPSZ,
66 	1,
67 	0
68 };
69 
70 static 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 
79 static 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 
88 static struct streamtab softmac_tab = {
89 	&softmac_urinit,	/* st_rdinit */
90 	&softmac_uwinit		/* st_wrinit */
91 };
92 
93 DDI_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 
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 	ASSERT(slp->sl_ack_mp == NULL);
226 	ASSERT(slp->sl_ctl_inprogress == B_FALSE);
227 	ASSERT(slp->sl_pending_prim == DL_PRIM_INVAL);
228 	ASSERT(slp->sl_pending_ioctl == B_FALSE);
229 
230 	cv_destroy(&slp->sl_cv);
231 	mutex_destroy(&slp->sl_mutex);
232 	cv_destroy(&slp->sl_ctl_cv);
233 	mutex_destroy(&slp->sl_ctl_mutex);
234 
235 	kmem_free(slp, sizeof (*slp));
236 	return (0);
237 }
238 
239 static void
240 softmac_rput(queue_t *rq, mblk_t *mp)
241 {
242 	softmac_lower_t *slp = rq->q_ptr;
243 	union DL_primitives *dlp;
244 
245 	/*
246 	 * This is the softmac module.
247 	 */
248 	ASSERT(WR(rq)->q_next != NULL);
249 	ASSERT((mp->b_next == NULL) && (mp->b_prev == NULL));
250 
251 	switch (DB_TYPE(mp)) {
252 	case M_DATA:
253 		/*
254 		 * Some drivers start to send up packets even if not in the
255 		 * DL_IDLE state, where sl_softmac is not set yet.  Drop the
256 		 * packet in this case.
257 		 */
258 		if (slp->sl_softmac == NULL) {
259 			freemsg(mp);
260 			return;
261 		}
262 
263 		/*
264 		 * If this message is looped back from the legacy devices,
265 		 * drop it as the Nemo framework will be responsible for
266 		 * looping it back by the mac_txloop() function.
267 		 */
268 		if (mp->b_flag & MSGNOLOOP) {
269 			freemsg(mp);
270 			return;
271 		}
272 
273 		/*
274 		 * This is the most common case.
275 		 */
276 		if (DB_REF(mp) == 1) {
277 			ASSERT(slp->sl_softmac != NULL);
278 			/*
279 			 * We don't need any locks to protect sl_handle
280 			 * because ip_input() can tolerate if sl_handle
281 			 * is reset to NULL when DL_CAPAB_POLL is
282 			 * disabled.
283 			 */
284 			mac_rx(slp->sl_softmac->smac_mh, NULL, mp);
285 			return;
286 		} else {
287 			softmac_rput_process_data(slp, mp);
288 		}
289 		break;
290 	case M_PROTO:
291 	case M_PCPROTO:
292 		if (MBLKL(mp) < sizeof (dlp->dl_primitive)) {
293 			freemsg(mp);
294 			break;
295 		}
296 		dlp = (union DL_primitives *)mp->b_rptr;
297 		if (dlp->dl_primitive == DL_UNITDATA_IND) {
298 			cmn_err(CE_WARN, "got unexpected %s message",
299 			    dl_primstr(DL_UNITDATA_IND));
300 			freemsg(mp);
301 			break;
302 		}
303 		/*FALLTHROUGH*/
304 	default:
305 		softmac_rput_process_notdata(rq, mp);
306 		break;
307 	}
308 }
309 
310 /* ARGSUSED */
311 static void
312 softmac_rsrv(queue_t *rq)
313 {
314 }
315 
316 static void
317 softmac_wput(queue_t *wq, mblk_t *mp)
318 {
319 	/*
320 	 * This is the softmac module
321 	 */
322 	ASSERT(wq->q_next != NULL);
323 
324 	switch (DB_TYPE(mp)) {
325 	case M_IOCTL: {
326 		struct iocblk		*ioc = (struct iocblk *)mp->b_rptr;
327 
328 		switch (ioc->ioc_cmd) {
329 		case SMAC_IOC_START: {
330 			softmac_lower_t		*slp = wq->q_ptr;
331 			smac_ioc_start_t	*arg;
332 
333 			if (ioc->ioc_count != sizeof (*arg)) {
334 				miocnak(wq, mp, 0, EINVAL);
335 				break;
336 			}
337 
338 			/*
339 			 * Assign the devname and perstream handle of the
340 			 * specific lower stream and return it as a part
341 			 * of the ioctl.
342 			 */
343 			arg = (smac_ioc_start_t *)mp->b_cont->b_rptr;
344 			arg->si_slp = slp;
345 
346 			miocack(wq, mp, sizeof (*arg), 0);
347 			break;
348 		}
349 		default:
350 			miocnak(wq, mp, 0, EINVAL);
351 			break;
352 		}
353 		break;
354 	}
355 	default:
356 		freemsg(mp);
357 		break;
358 	}
359 }
360 
361 static void
362 softmac_wsrv(queue_t *wq)
363 {
364 	softmac_lower_t *slp = wq->q_ptr;
365 
366 	/*
367 	 * This is the softmac module
368 	 */
369 	ASSERT(wq->q_next != NULL);
370 
371 	/*
372 	 * Inform that the tx resource is available; mac_tx_update() will
373 	 * inform all the upper streams sharing this lower stream.
374 	 */
375 	if (slp->sl_softmac != NULL)
376 		mac_tx_update(slp->sl_softmac->smac_mh);
377 }
378 
379 static int
380 softmac_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
381 {
382 	ASSERT(ddi_get_instance(dip) == 0);
383 
384 	if (cmd != DDI_ATTACH)
385 		return (DDI_FAILURE);
386 
387 	softmac_dip = dip;
388 
389 	return (DDI_SUCCESS);
390 }
391 
392 /* ARGSUSED */
393 static int
394 softmac_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
395 {
396 	if (cmd != DDI_DETACH)
397 		return (DDI_FAILURE);
398 
399 	softmac_dip = NULL;
400 	return (DDI_SUCCESS);
401 }
402 
403 /* ARGSUSED */
404 static int
405 softmac_info(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
406 {
407 	switch (infocmd) {
408 	case DDI_INFO_DEVT2DEVINFO:
409 		if (softmac_dip != NULL) {
410 			*result = softmac_dip;
411 			return (DDI_SUCCESS);
412 		}
413 		break;
414 
415 	case DDI_INFO_DEVT2INSTANCE:
416 		*result = NULL;
417 		return (DDI_SUCCESS);
418 
419 	}
420 
421 	return (DDI_FAILURE);
422 }
423