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 }