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 }