xref: /illumos-gate/usr/src/uts/common/inet/dlpistub/dlpistub.c (revision 581cede61ac9c14d8d4ea452562a567189eead78)
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 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * DLPI stub driver; currently supports VNI and IPMP stub devices.
28  */
29 
30 #include <sys/conf.h>
31 #include <sys/ddi.h>
32 #include <sys/sunddi.h>
33 #include <sys/dlpi.h>
34 #include <sys/stat.h>
35 #include <sys/strsun.h>
36 #include <sys/stropts.h>
37 #include <sys/types.h>
38 #include <sys/id_space.h>
39 #include <sys/sysmacros.h>
40 #include <sys/kmem.h>
41 #include <sys/modctl.h>
42 #include <sys/mkdev.h>
43 #include <sys/sdt.h>
44 
45 #include "dlpistub_impl.h"
46 
47 static id_space_t *ds_minors;
48 static dev_info_t *ds_dip;
49 
50 /*
51  * DL_INFO_ACK template.
52  */
53 static dl_info_ack_t ds_infoack = {
54 	DL_INFO_ACK,	/* dl_primitive */
55 	0,		/* dl_max_sdu */
56 	0,		/* dl_min_sdu */
57 	0,		/* dl_addr_length */
58 	0,		/* dl_mac_type */
59 	0,		/* dl_reserved */
60 	0,		/* dl_current_state */
61 	0,		/* dl_sap_length */
62 	DL_CLDLS,	/* dl_service_mode */
63 	0,		/* dl_qos_length */
64 	0,		/* dl_qos_offset */
65 	0,		/* dl_qos_range_length */
66 	0,		/* dl_qos_range_offset */
67 	DL_STYLE2,	/* dl_provider_style */
68 	0,		/* dl_addr_offset */
69 	DL_VERSION_2,	/* dl_version */
70 	0,		/* dl_brdcst_addr_length */
71 	0,		/* dl_brdcst_addr_offset */
72 	0		/* dl_growth */
73 };
74 
75 static int
76 ds_attach(dev_info_t *dip, ddi_attach_cmd_t cmd)
77 {
78 	if (cmd != DDI_ATTACH)
79 		return (DDI_FAILURE);
80 
81 	if (ddi_create_minor_node(dip, "vni", S_IFCHR, DS_MINOR_VNI,
82 	    DDI_PSEUDO, 0) == DDI_FAILURE ||
83 	    ddi_create_minor_node(dip, "ipmpstub", S_IFCHR, DS_MINOR_IPMP,
84 	    DDI_PSEUDO, 0) == DDI_FAILURE) {
85 		ddi_remove_minor_node(dip, NULL);
86 		cmn_err(CE_NOTE, "ds_attach: cannot create minor nodes");
87 		return (DDI_FAILURE);
88 	}
89 
90 	ds_dip = dip;
91 	ds_minors = id_space_create("ds_minors", DS_MINOR_START, MAXMIN32);
92 	return (DDI_SUCCESS);
93 }
94 
95 static int
96 ds_detach(dev_info_t *dip, ddi_detach_cmd_t cmd)
97 {
98 	if (cmd != DDI_DETACH)
99 		return (DDI_FAILURE);
100 
101 	id_space_destroy(ds_minors);
102 	ds_minors = NULL;
103 	ASSERT(dip == ds_dip);
104 	ddi_remove_minor_node(dip, NULL);
105 	ds_dip = NULL;
106 	return (DDI_SUCCESS);
107 }
108 
109 /* ARGSUSED */
110 static int
111 ds_devinfo(dev_info_t *dip, ddi_info_cmd_t infocmd, void *arg, void **result)
112 {
113 	int error = DDI_FAILURE;
114 
115 	switch (infocmd) {
116 	case DDI_INFO_DEVT2INSTANCE:
117 		*result = (void *)0;
118 		error = DDI_SUCCESS;
119 		break;
120 	case DDI_INFO_DEVT2DEVINFO:
121 		if (ds_dip != NULL) {
122 			*result = ds_dip;
123 			error = DDI_SUCCESS;
124 		}
125 		break;
126 	}
127 	return (error);
128 }
129 
130 /* ARGSUSED */
131 static int
132 ds_open(queue_t *q, dev_t *devp, int flag, int sflag, cred_t *credp)
133 {
134 	int type;
135 	dlpistub_t *dsp;
136 
137 	if (sflag == CLONEOPEN || sflag == MODOPEN)
138 		return (EINVAL);
139 
140 	if (q->q_ptr != NULL)
141 		return (0);
142 
143 	switch (getminor(*devp)) {
144 	case DS_MINOR_VNI:
145 		type = SUNW_DL_VNI;
146 		break;
147 	case DS_MINOR_IPMP:
148 		type = SUNW_DL_IPMP;
149 		break;
150 	default:
151 		return (ENXIO);
152 	}
153 
154 	dsp = kmem_zalloc(sizeof (dlpistub_t), KM_SLEEP);
155 	dsp->ds_type = type;
156 	dsp->ds_minor = (minor_t)id_alloc(ds_minors);
157 	dsp->ds_state = DL_UNATTACHED;
158 	*devp = makedevice(getmajor(*devp), dsp->ds_minor);
159 	q->q_ptr = WR(q)->q_ptr = dsp;
160 	qprocson(q);
161 
162 	return (0);
163 }
164 
165 /* ARGSUSED */
166 static int
167 ds_close(queue_t *q, int flag, cred_t *credp)
168 {
169 	dlpistub_t	*dsp = q->q_ptr;
170 
171 	qprocsoff(q);
172 	q->q_ptr = WR(q)->q_ptr = NULL;
173 
174 	id_free(ds_minors, dsp->ds_minor);
175 	kmem_free(dsp, sizeof (dlpistub_t));
176 
177 	return (0);
178 }
179 
180 static int
181 ds_badprim(queue_t *q, mblk_t *mp, t_scalar_t prim)
182 {
183 	dlerrorack(q, mp, prim, DL_BADPRIM, 0);
184 	return (0);
185 }
186 
187 static int
188 ds_outstate(queue_t *q, mblk_t *mp, t_scalar_t prim)
189 {
190 	dlerrorack(q, mp, prim, DL_OUTSTATE, 0);
191 	return (0);
192 }
193 
194 static int
195 ds_wput(queue_t *q, mblk_t *mp)
196 {
197 	union DL_primitives	*dlp;
198 	dl_info_ack_t		*dlip;
199 	dlpistub_t		*dsp = q->q_ptr;
200 	t_scalar_t		prim;
201 
202 	switch (DB_TYPE(mp)) {
203 	case M_PROTO:
204 	case M_PCPROTO:
205 		if (MBLKL(mp) < sizeof (t_scalar_t)) {
206 			dlerrorack(q, mp, DL_PRIM_INVAL, DL_UNSUPPORTED, 0);
207 			return (0);
208 		}
209 
210 		dlp = (void *)mp->b_rptr;
211 		prim = dlp->dl_primitive;
212 		switch (prim) {
213 		case DL_ATTACH_REQ:
214 			if (MBLKL(mp) < DL_ATTACH_REQ_SIZE)
215 				return (ds_badprim(q, mp, prim));
216 
217 			if (dsp->ds_state != DL_UNATTACHED)
218 				return (ds_outstate(q, mp, prim));
219 
220 			dsp->ds_state = DL_UNBOUND;
221 			dlokack(q, mp, DL_ATTACH_REQ);
222 			break;
223 
224 		case DL_BIND_REQ:
225 			if (MBLKL(mp) < DL_BIND_REQ_SIZE)
226 				return (ds_badprim(q, mp, prim));
227 
228 			if (dsp->ds_state != DL_UNBOUND)
229 				return (ds_outstate(q, mp, prim));
230 
231 			dsp->ds_state = DL_IDLE;
232 			dlbindack(q, mp, dlp->bind_req.dl_sap, NULL, 0, 0, 0);
233 			break;
234 
235 		case DL_INFO_REQ:
236 			if (MBLKL(mp) < DL_INFO_REQ_SIZE)
237 				return (ds_badprim(q, mp, prim));
238 
239 			mp = mexchange(q, mp, sizeof (dl_info_ack_t),
240 			    M_PCPROTO, DL_INFO_ACK);
241 			if (mp != NULL) {
242 				dlip = (void *)mp->b_rptr;
243 				*dlip = ds_infoack;
244 				dlip->dl_mac_type = dsp->ds_type;
245 				dlip->dl_current_state = dsp->ds_state;
246 				qreply(q, mp);
247 			}
248 			break;
249 
250 		case DL_PHYS_ADDR_REQ:
251 			if (MBLKL(mp) < DL_PHYS_ADDR_REQ_SIZE)
252 				return (ds_badprim(q, mp, prim));
253 
254 			dlphysaddrack(q, mp, NULL, 0);
255 			break;
256 
257 		case DL_UNBIND_REQ:
258 			if (MBLKL(mp) < DL_UNBIND_REQ_SIZE)
259 				return (ds_badprim(q, mp, prim));
260 
261 			if (dsp->ds_state != DL_IDLE)
262 				return (ds_outstate(q, mp, prim));
263 
264 			dsp->ds_state = DL_UNBOUND;
265 			dlokack(q, mp, DL_UNBIND_REQ);
266 			break;
267 
268 		case DL_DETACH_REQ:
269 			if (MBLKL(mp) < DL_DETACH_REQ_SIZE)
270 				return (ds_badprim(q, mp, prim));
271 
272 			if (dsp->ds_state != DL_UNBOUND)
273 				return (ds_outstate(q, mp, prim));
274 
275 			dsp->ds_state = DL_UNATTACHED;
276 			dlokack(q, mp, DL_DETACH_REQ);
277 			break;
278 
279 		case DL_UNITDATA_REQ:
280 			DTRACE_PROBE2(dlpistub__data, dlpistub_t *, dsp,
281 			    mblk_t *, mp);
282 			freemsg(mp);
283 			break;
284 
285 		default:
286 			dlerrorack(q, mp, prim, DL_UNSUPPORTED, 0);
287 		}
288 		break;
289 
290 	case M_IOCTL:
291 		miocnak(q, mp, 0, EINVAL);
292 		break;
293 
294 	case M_FLUSH:
295 		*mp->b_rptr &= ~FLUSHW;
296 		if (*mp->b_rptr & FLUSHR)
297 			qreply(q, mp);
298 		else
299 			freemsg(mp);
300 		break;
301 	default:
302 		freemsg(mp);
303 		break;
304 	}
305 
306 	return (0);
307 }
308 
309 static struct module_info ds_minfo = {
310 	DS_IDNUM,	/* mi_idnum */
311 	"dlpistub",	/* mi_idname */
312 	0,		/* mi_minpsz */
313 	INFPSZ,		/* mi_maxpsz */
314 	0,		/* mi_hiwat */
315 	0,		/* mi_lowat */
316 };
317 
318 static struct qinit ds_rinit = {
319 	NULL,		/* qi_putp */
320 	NULL,		/* qi_srvp */
321 	ds_open,	/* qi_qopen */
322 	ds_close,	/* qi_qclose */
323 	NULL,		/* qi_qadmin */
324 	&ds_minfo,	/* qi_minfo */
325 };
326 
327 static struct qinit ds_winit = {
328 	ds_wput,	/* qi_putp */
329 	NULL,		/* qi_srvp */
330 	NULL,		/* qi_qopen */
331 	NULL,		/* qi_qclose */
332 	NULL,		/* qi_qadmin */
333 	&ds_minfo,	/* qi_minfo */
334 };
335 
336 static struct streamtab ds_info = {
337 	&ds_rinit,	/* st_rdinit */
338 	&ds_winit	/* st_wrinit */
339 };
340 
341 DDI_DEFINE_STREAM_OPS(ds_ops, nulldev, nulldev, ds_attach, ds_detach,
342     nodev, ds_devinfo, D_MP|D_MTPERMOD, &ds_info, ddi_quiesce_not_supported);
343 
344 static struct modldrv modldrv = {
345 	&mod_driverops,
346 	"DLPI stub driver",
347 	&ds_ops,
348 };
349 
350 static struct modlinkage modlinkage = {
351 	MODREV_1, &modldrv, NULL
352 };
353 
354 int
355 _init(void)
356 {
357 	return (mod_install(&modlinkage));
358 }
359 
360 int
361 _fini(void)
362 {
363 	return (mod_remove(&modlinkage));
364 }
365 
366 int
367 _info(struct modinfo *modinfop)
368 {
369 	return (mod_info(&modlinkage, modinfop));
370 }
371