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