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 (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 26 /* 27 * Sysevent Driver for GPEC 28 */ 29 30 #include <sys/types.h> 31 #include <sys/param.h> 32 #include <sys/cred.h> 33 #include <sys/file.h> 34 #include <sys/stat.h> 35 #include <sys/conf.h> 36 #include <sys/ddi.h> 37 #include <sys/sunddi.h> 38 #include <sys/modctl.h> 39 #include <sys/open.h> /* OTYP_CHR definition */ 40 #include <sys/sysmacros.h> /* L_BITSMINOR definition */ 41 #include <sys/bitmap.h> 42 #include <sys/sysevent.h> 43 #include <sys/sysevent_impl.h> 44 45 static dev_info_t *sysevent_devi; 46 47 /* Definitions for binding handle array */ 48 static ulong_t sysevent_bitmap_initial = 1; /* index 0 indicates error */ 49 static ulong_t *sysevent_minor_bitmap = &sysevent_bitmap_initial; 50 static size_t sysevent_minor_bits = BT_NBIPUL; 51 static kmutex_t sysevent_minor_mutex; 52 53 /* 54 * evchan_ctl acts as a container for the binding handle 55 */ 56 typedef struct evchan_ctl { 57 evchan_t *chp; 58 } evchan_ctl_t; 59 60 static void *evchan_ctlp; 61 62 /* 63 * Check if it's a null terminated array - to avoid DoS attack 64 * It is supposed that string points to an array with 65 * a minimum length of len. len must be strlen + 1. 66 * Checks for printable characters are already done in library. 67 */ 68 static int 69 sysevent_isstrend(char *string, size_t len) 70 { 71 /* Return 0 if string has length of zero */ 72 if (len > 0) { 73 return (string[len - 1] == '\0' ? 1 : 0); 74 } else { 75 return (0); 76 } 77 } 78 79 /* 80 * Following sysevent_minor_* routines map 81 * a binding handle (evchan_t *) to a minor number 82 * Has to be called w/ locks held. 83 */ 84 static ulong_t * 85 sysevent_minor_alloc(void) 86 { 87 ulong_t *bhst = sysevent_minor_bitmap; 88 89 /* Increase bitmap by one BT_NBIPUL */ 90 if (sysevent_minor_bits + BT_NBIPUL > SYSEVENT_MINOR_MAX) { 91 return ((ulong_t *)NULL); 92 } 93 sysevent_minor_bitmap = kmem_zalloc( 94 BT_SIZEOFMAP(sysevent_minor_bits + BT_NBIPUL), KM_SLEEP); 95 bcopy(bhst, sysevent_minor_bitmap, BT_SIZEOFMAP(sysevent_minor_bits)); 96 if (bhst != &sysevent_bitmap_initial) 97 kmem_free(bhst, BT_SIZEOFMAP(sysevent_minor_bits)); 98 sysevent_minor_bits += BT_NBIPUL; 99 100 return (sysevent_minor_bitmap); 101 } 102 103 static void 104 sysevent_minor_free(ulong_t *bitmap) 105 { 106 if (bitmap != &sysevent_bitmap_initial) 107 kmem_free(bitmap, BT_SIZEOFMAP(sysevent_minor_bits)); 108 } 109 110 static index_t 111 sysevent_minor_get(void) 112 { 113 index_t idx; 114 ulong_t *bhst; 115 116 /* Search for an available index */ 117 mutex_enter(&sysevent_minor_mutex); 118 if ((idx = bt_availbit(sysevent_minor_bitmap, 119 sysevent_minor_bits)) == -1) { 120 /* All busy - allocate additional binding handle bitmap space */ 121 if ((bhst = sysevent_minor_alloc()) == NULL) { 122 /* Reached our maximum of id's == SHRT_MAX */ 123 mutex_exit(&sysevent_minor_mutex); 124 return (0); 125 } else { 126 sysevent_minor_bitmap = bhst; 127 } 128 idx = bt_availbit(sysevent_minor_bitmap, sysevent_minor_bits); 129 } 130 BT_SET(sysevent_minor_bitmap, idx); 131 mutex_exit(&sysevent_minor_mutex); 132 return (idx); 133 } 134 135 static void 136 sysevent_minor_rele(index_t idx) 137 { 138 mutex_enter(&sysevent_minor_mutex); 139 ASSERT(BT_TEST(sysevent_minor_bitmap, idx) == 1); 140 BT_CLEAR(sysevent_minor_bitmap, idx); 141 mutex_exit(&sysevent_minor_mutex); 142 } 143 144 static void 145 sysevent_minor_init(void) 146 { 147 mutex_init(&sysevent_minor_mutex, NULL, MUTEX_DEFAULT, NULL); 148 } 149 150 /* ARGSUSED */ 151 static int 152 sysevent_publish(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 153 { 154 int km_flags; 155 sev_publish_args_t uargs; 156 sysevent_impl_t *ev; 157 evchan_ctl_t *ctl; 158 159 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 160 if (ctl == NULL || ctl->chp == NULL) 161 return (ENXIO); 162 163 if (copyin(arg, &uargs, sizeof (sev_publish_args_t)) != 0) 164 return (EFAULT); 165 166 /* 167 * This limits the size of an event 168 */ 169 if (uargs.ev.len > MAX_EV_SIZE_LEN) 170 return (EOVERFLOW); 171 172 /* 173 * Check for valid uargs.flags 174 */ 175 if (uargs.flags & ~(EVCH_NOSLEEP | EVCH_SLEEP | EVCH_QWAIT)) 176 return (EINVAL); 177 178 /* 179 * Check that at least one of EVCH_NOSLEEP or EVCH_SLEEP is 180 * specified 181 */ 182 km_flags = uargs.flags & (EVCH_NOSLEEP | EVCH_SLEEP); 183 if (km_flags != EVCH_NOSLEEP && km_flags != EVCH_SLEEP) 184 return (EINVAL); 185 186 ev = evch_usrallocev(uargs.ev.len, uargs.flags); 187 188 if (copyin((void *)(uintptr_t)uargs.ev.name, ev, uargs.ev.len) != 0) { 189 evch_usrfreeev(ev); 190 return (EFAULT); 191 } 192 193 return (evch_usrpostevent(ctl->chp, ev, uargs.flags)); 194 195 /* Event will be freed internally */ 196 } 197 198 /* 199 * sysevent_chan_open - used to open a channel in the GPEC channel layer 200 */ 201 202 /* ARGSUSED */ 203 static int 204 sysevent_chan_open(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 205 { 206 sev_bind_args_t uargs; 207 evchan_ctl_t *ctl; 208 char *chan_name; 209 int ec; 210 211 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 212 if (ctl == NULL) { 213 return (ENXIO); 214 } 215 216 if (copyin(arg, &uargs, sizeof (sev_bind_args_t)) != 0) 217 return (EFAULT); 218 219 if (uargs.chan_name.len > MAX_CHNAME_LEN) 220 return (EINVAL); 221 222 chan_name = kmem_alloc(uargs.chan_name.len, KM_SLEEP); 223 224 if (copyin((void *)(uintptr_t)uargs.chan_name.name, chan_name, 225 uargs.chan_name.len) != 0) { 226 kmem_free(chan_name, uargs.chan_name.len); 227 return (EFAULT); 228 } 229 230 if (!sysevent_isstrend(chan_name, uargs.chan_name.len)) { 231 kmem_free(chan_name, uargs.chan_name.len); 232 return (EINVAL); 233 } 234 235 /* 236 * Check of uargs.flags and uargs.perms just to avoid DoS attacks. 237 * libsysevent does this carefully 238 */ 239 ctl->chp = evch_usrchanopen((const char *)chan_name, 240 uargs.flags & EVCH_B_FLAGS, &ec); 241 242 kmem_free(chan_name, uargs.chan_name.len); 243 244 if (ec != 0) { 245 return (ec); 246 } 247 248 return (0); 249 } 250 251 /* ARGSUSED */ 252 static int 253 sysevent_chan_control(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 254 { 255 sev_control_args_t uargs; 256 evchan_ctl_t *ctl; 257 int rc; 258 259 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 260 if (ctl == NULL || ctl->chp == NULL) 261 return (ENXIO); 262 263 if (copyin(arg, &uargs, sizeof (sev_control_args_t)) != 0) 264 return (EFAULT); 265 266 switch (uargs.cmd) { 267 case EVCH_GET_CHAN_LEN: 268 case EVCH_GET_CHAN_LEN_MAX: 269 rc = evch_usrcontrol_get(ctl->chp, uargs.cmd, &uargs.value); 270 if (rc == 0) { 271 if (copyout((void *)&uargs, arg, 272 sizeof (sev_control_args_t)) != 0) { 273 rc = EFAULT; 274 } 275 } 276 break; 277 case EVCH_SET_CHAN_LEN: 278 rc = evch_usrcontrol_set(ctl->chp, uargs.cmd, uargs.value); 279 break; 280 default: 281 rc = EINVAL; 282 } 283 return (rc); 284 } 285 286 /* ARGSUSED */ 287 static int 288 sysevent_subscribe(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 289 { 290 sev_subscribe_args_t uargs; 291 char *sid; 292 char *class_info = NULL; 293 evchan_ctl_t *ctl; 294 int rc; 295 296 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 297 if (ctl == NULL || ctl->chp == NULL) 298 return (ENXIO); 299 300 if (copyin(arg, &uargs, sizeof (sev_subscribe_args_t)) != 0) 301 return (EFAULT); 302 303 if (uargs.sid.len > MAX_SUBID_LEN || 304 uargs.class_info.len > MAX_CLASS_LEN) 305 return (EINVAL); 306 307 sid = kmem_alloc(uargs.sid.len, KM_SLEEP); 308 if (copyin((void *)(uintptr_t)uargs.sid.name, 309 sid, uargs.sid.len) != 0) { 310 kmem_free(sid, uargs.sid.len); 311 return (EFAULT); 312 } 313 if (!sysevent_isstrend(sid, uargs.sid.len)) { 314 kmem_free(sid, uargs.sid.len); 315 return (EINVAL); 316 } 317 318 /* If class string empty then class EC_ALL is assumed */ 319 if (uargs.class_info.len != 0) { 320 class_info = kmem_alloc(uargs.class_info.len, KM_SLEEP); 321 if (copyin((void *)(uintptr_t)uargs.class_info.name, class_info, 322 uargs.class_info.len) != 0) { 323 kmem_free(class_info, uargs.class_info.len); 324 kmem_free(sid, uargs.sid.len); 325 return (EFAULT); 326 } 327 if (!sysevent_isstrend(class_info, uargs.class_info.len)) { 328 kmem_free(class_info, uargs.class_info.len); 329 kmem_free(sid, uargs.sid.len); 330 return (EINVAL); 331 } 332 } 333 334 /* 335 * Check of uargs.flags just to avoid DoS attacks 336 * libsysevent does this carefully. 337 */ 338 rc = evch_usrsubscribe(ctl->chp, sid, class_info, 339 (int)uargs.door_desc, uargs.flags); 340 341 kmem_free(class_info, uargs.class_info.len); 342 kmem_free(sid, uargs.sid.len); 343 344 return (rc); 345 } 346 347 /* ARGSUSED */ 348 static int 349 sysevent_unsubscribe(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 350 { 351 sev_unsubscribe_args_t uargs; 352 char *sid; 353 evchan_ctl_t *ctl; 354 355 if (copyin(arg, &uargs, sizeof (sev_unsubscribe_args_t)) != 0) 356 return (EFAULT); 357 358 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 359 if (ctl == NULL || ctl->chp == NULL) 360 return (ENXIO); 361 362 if (uargs.sid.len > MAX_SUBID_LEN) 363 return (EINVAL); 364 365 /* Unsubscribe for all */ 366 if (uargs.sid.len == 0) { 367 evch_usrunsubscribe(ctl->chp, NULL, 0); 368 return (0); 369 } 370 371 sid = kmem_alloc(uargs.sid.len, KM_SLEEP); 372 373 if (copyin((void *)(uintptr_t)uargs.sid.name, 374 sid, uargs.sid.len) != 0) { 375 kmem_free(sid, uargs.sid.len); 376 return (EFAULT); 377 } 378 379 evch_usrunsubscribe(ctl->chp, sid, 0); 380 381 kmem_free(sid, uargs.sid.len); 382 383 return (0); 384 } 385 386 /* ARGSUSED */ 387 static int 388 sysevent_channames(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 389 { 390 sev_chandata_args_t uargs; 391 char *buf; 392 int len; 393 int rc = 0; 394 395 if (copyin(arg, &uargs, sizeof (sev_chandata_args_t)) != 0) 396 return (EFAULT); 397 398 if (uargs.out_data.len == 0 || uargs.out_data.len > EVCH_MAX_DATA_SIZE) 399 return (EINVAL); 400 401 buf = kmem_alloc(uargs.out_data.len, KM_SLEEP); 402 403 if ((len = evch_usrgetchnames(buf, uargs.out_data.len)) == -1) { 404 rc = EOVERFLOW; 405 } 406 407 if (rc == 0) { 408 ASSERT(len <= uargs.out_data.len); 409 if (copyout(buf, 410 (void *)(uintptr_t)uargs.out_data.name, len) != 0) { 411 rc = EFAULT; 412 } 413 } 414 415 kmem_free(buf, uargs.out_data.len); 416 417 return (rc); 418 } 419 420 /* ARGSUSED */ 421 static int 422 sysevent_chandata(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 423 { 424 sev_chandata_args_t uargs; 425 char *channel; 426 char *buf; 427 int len; 428 int rc = 0; 429 430 if (copyin(arg, &uargs, sizeof (sev_chandata_args_t)) != 0) 431 return (EFAULT); 432 433 if (uargs.in_data.len > MAX_CHNAME_LEN || 434 uargs.out_data.len > EVCH_MAX_DATA_SIZE) 435 return (EINVAL); 436 437 channel = kmem_alloc(uargs.in_data.len, KM_SLEEP); 438 439 if (copyin((void *)(uintptr_t)uargs.in_data.name, channel, 440 uargs.in_data.len) != 0) { 441 kmem_free(channel, uargs.in_data.len); 442 return (EFAULT); 443 } 444 445 if (!sysevent_isstrend(channel, uargs.in_data.len)) { 446 kmem_free(channel, uargs.in_data.len); 447 return (EINVAL); 448 } 449 450 buf = kmem_alloc(uargs.out_data.len, KM_SLEEP); 451 452 len = evch_usrgetchdata(channel, buf, uargs.out_data.len); 453 if (len == 0) { 454 rc = EOVERFLOW; 455 } else if (len == -1) { 456 rc = ENOENT; 457 } 458 459 if (rc == 0) { 460 ASSERT(len <= uargs.out_data.len); 461 if (copyout(buf, 462 (void *)(uintptr_t)uargs.out_data.name, len) != 0) { 463 rc = EFAULT; 464 } 465 } 466 467 kmem_free(buf, uargs.out_data.len); 468 kmem_free(channel, uargs.in_data.len); 469 470 return (rc); 471 } 472 473 /* ARGSUSED */ 474 static int 475 sysevent_setpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 476 { 477 sev_propnvl_args_t uargs; 478 nvlist_t *nvl = NULL; 479 evchan_ctl_t *ctl; 480 size_t bufsz; 481 char *buf; 482 483 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 484 if (ctl == NULL || ctl->chp == NULL) 485 return (ENXIO); 486 487 if (copyin(arg, &uargs, sizeof (uargs)) != 0) 488 return (EFAULT); 489 490 if (uargs.packednvl.name != 0) { 491 bufsz = uargs.packednvl.len; 492 493 if (bufsz == 0) 494 return (EINVAL); 495 496 if (bufsz > EVCH_MAX_DATA_SIZE) 497 return (EOVERFLOW); 498 499 buf = kmem_alloc(bufsz, KM_SLEEP); 500 501 if (copyin((void *)(uintptr_t)uargs.packednvl.name, buf, 502 bufsz) != 0 || 503 nvlist_unpack(buf, bufsz, &nvl, KM_SLEEP) != 0) { 504 kmem_free(buf, bufsz); 505 return (EFAULT); 506 } 507 508 kmem_free(buf, bufsz); 509 510 if (nvl == NULL) 511 return (EINVAL); 512 } 513 514 evch_usrsetpropnvl(ctl->chp, nvl); 515 return (0); 516 } 517 518 /* ARGSUSED */ 519 static int 520 sysevent_getpropnvl(dev_t dev, int *rvalp, void *arg, int flag, cred_t *cr) 521 { 522 sev_propnvl_args_t uargs; 523 size_t reqsz, avlsz; 524 evchan_ctl_t *ctl; 525 nvlist_t *nvl; 526 int64_t gen; 527 int rc; 528 529 ctl = ddi_get_soft_state(evchan_ctlp, getminor(dev)); 530 531 if (ctl == NULL || ctl->chp == NULL) 532 return (ENXIO); 533 534 if (copyin(arg, &uargs, sizeof (uargs)) != 0) 535 return (EFAULT); 536 537 if ((rc = evch_usrgetpropnvl(ctl->chp, &nvl, &gen)) != 0) 538 return (rc); 539 540 if (nvl != NULL) { 541 avlsz = uargs.packednvl.len; 542 543 if (nvlist_size(nvl, &reqsz, NV_ENCODE_NATIVE) != 0) { 544 nvlist_free(nvl); 545 return (EINVAL); 546 } 547 548 if (reqsz > EVCH_MAX_DATA_SIZE) { 549 nvlist_free(nvl); 550 return (E2BIG); 551 } 552 553 if (reqsz <= avlsz) { 554 char *buf = kmem_alloc(reqsz, KM_SLEEP); 555 556 if (nvlist_pack(nvl, &buf, &reqsz, 557 NV_ENCODE_NATIVE, 0) != 0 || copyout(buf, 558 (void *)(uintptr_t)uargs.packednvl.name, 559 reqsz) != 0) { 560 kmem_free(buf, reqsz); 561 nvlist_free(nvl); 562 return (EFAULT); 563 } 564 kmem_free(buf, reqsz); 565 rc = 0; 566 } else { 567 rc = EOVERFLOW; 568 } 569 uargs.packednvl.len = (uint32_t)reqsz; 570 nvlist_free(nvl); 571 } else { 572 uargs.packednvl.len = 0; 573 rc = 0; 574 } 575 576 uargs.generation = gen; 577 if (copyout((void *)&uargs, arg, sizeof (uargs)) != 0) 578 rc = EFAULT; 579 580 return (rc); 581 } 582 583 /*ARGSUSED*/ 584 static int 585 sysevent_ioctl(dev_t dev, int cmd, intptr_t arg, 586 int flag, cred_t *cr, int *rvalp) 587 { 588 int rc; 589 590 switch (cmd) { 591 case SEV_PUBLISH: 592 rc = sysevent_publish(dev, rvalp, (void *)arg, flag, cr); 593 break; 594 case SEV_CHAN_OPEN: 595 rc = sysevent_chan_open(dev, rvalp, (void *)arg, flag, cr); 596 break; 597 case SEV_CHAN_CONTROL: 598 rc = sysevent_chan_control(dev, rvalp, (void *)arg, flag, cr); 599 break; 600 case SEV_SUBSCRIBE: 601 rc = sysevent_subscribe(dev, rvalp, (void *)arg, flag, cr); 602 break; 603 case SEV_UNSUBSCRIBE: 604 rc = sysevent_unsubscribe(dev, rvalp, (void *)arg, flag, cr); 605 break; 606 case SEV_CHANNAMES: 607 rc = sysevent_channames(dev, rvalp, (void *)arg, flag, cr); 608 break; 609 case SEV_CHANDATA: 610 rc = sysevent_chandata(dev, rvalp, (void *)arg, flag, cr); 611 break; 612 case SEV_SETPROPNVL: 613 rc = sysevent_setpropnvl(dev, rvalp, (void *)arg, flag, cr); 614 break; 615 case SEV_GETPROPNVL: 616 rc = sysevent_getpropnvl(dev, rvalp, (void *)arg, flag, cr); 617 break; 618 default: 619 rc = EINVAL; 620 } 621 622 return (rc); 623 } 624 625 /*ARGSUSED*/ 626 static int 627 sysevent_open(dev_t *devp, int flag, int otyp, cred_t *cr) 628 { 629 int minor; 630 631 if (otyp != OTYP_CHR) 632 return (EINVAL); 633 634 if (getminor(*devp) != 0) 635 return (ENXIO); 636 637 minor = sysevent_minor_get(); 638 if (minor == 0) 639 /* All minors are busy */ 640 return (EBUSY); 641 642 if (ddi_soft_state_zalloc(evchan_ctlp, minor) 643 != DDI_SUCCESS) { 644 sysevent_minor_rele(minor); 645 return (ENOMEM); 646 } 647 648 *devp = makedevice(getmajor(*devp), minor); 649 650 return (0); 651 } 652 653 /*ARGSUSED*/ 654 static int 655 sysevent_close(dev_t dev, int flag, int otyp, cred_t *cr) 656 { 657 int minor = (int)getminor(dev); 658 evchan_ctl_t *ctl; 659 660 if (otyp != OTYP_CHR) 661 return (EINVAL); 662 663 ctl = ddi_get_soft_state(evchan_ctlp, minor); 664 if (ctl == NULL) { 665 return (ENXIO); 666 } 667 668 if (ctl->chp) { 669 /* Release all non-persistant subscriptions */ 670 evch_usrunsubscribe(ctl->chp, NULL, EVCH_SUB_KEEP); 671 evch_usrchanclose(ctl->chp); 672 } 673 674 ddi_soft_state_free(evchan_ctlp, minor); 675 sysevent_minor_rele(minor); 676 677 return (0); 678 } 679 680 /* ARGSUSED */ 681 static int 682 sysevent_info(dev_info_t *dip, ddi_info_cmd_t infocmd, 683 void *arg, void **result) 684 { 685 switch (infocmd) { 686 case DDI_INFO_DEVT2DEVINFO: 687 *result = sysevent_devi; 688 return (DDI_SUCCESS); 689 case DDI_INFO_DEVT2INSTANCE: 690 *result = 0; 691 return (DDI_SUCCESS); 692 } 693 return (DDI_FAILURE); 694 } 695 696 /* ARGSUSED */ 697 static int 698 sysevent_attach(dev_info_t *devi, ddi_attach_cmd_t cmd) 699 { 700 701 if (cmd != DDI_ATTACH) { 702 return (DDI_FAILURE); 703 } 704 705 if (ddi_create_minor_node(devi, "sysevent", S_IFCHR, 706 0, DDI_PSEUDO, NULL) == DDI_FAILURE) { 707 ddi_remove_minor_node(devi, NULL); 708 return (DDI_FAILURE); 709 } 710 sysevent_devi = devi; 711 712 sysevent_minor_init(); 713 714 return (DDI_SUCCESS); 715 } 716 717 static int 718 sysevent_detach(dev_info_t *devi, ddi_detach_cmd_t cmd) 719 { 720 if (cmd != DDI_DETACH) { 721 return (DDI_FAILURE); 722 } 723 724 sysevent_minor_free(sysevent_minor_bitmap); 725 ddi_remove_minor_node(devi, NULL); 726 return (DDI_SUCCESS); 727 } 728 729 static struct cb_ops sysevent_cb_ops = { 730 sysevent_open, /* open */ 731 sysevent_close, /* close */ 732 nodev, /* strategy */ 733 nodev, /* print */ 734 nodev, /* dump */ 735 nodev, /* read */ 736 nodev, /* write */ 737 sysevent_ioctl, /* ioctl */ 738 nodev, /* devmap */ 739 nodev, /* mmap */ 740 nodev, /* segmap */ 741 nochpoll, /* poll */ 742 ddi_prop_op, /* prop_op */ 743 0, /* streamtab */ 744 D_NEW|D_MP, /* flag */ 745 NULL, /* aread */ 746 NULL /* awrite */ 747 }; 748 749 static struct dev_ops sysevent_ops = { 750 DEVO_REV, /* devo_rev */ 751 0, /* refcnt */ 752 sysevent_info, /* info */ 753 nulldev, /* identify */ 754 nulldev, /* probe */ 755 sysevent_attach, /* attach */ 756 sysevent_detach, /* detach */ 757 nodev, /* reset */ 758 &sysevent_cb_ops, /* driver operations */ 759 (struct bus_ops *)0, /* no bus operations */ 760 nulldev, /* power */ 761 ddi_quiesce_not_needed, /* quiesce */ 762 }; 763 764 static struct modldrv modldrv = { 765 &mod_driverops, "sysevent driver", &sysevent_ops 766 }; 767 768 static struct modlinkage modlinkage = { 769 MODREV_1, &modldrv, NULL 770 }; 771 772 int 773 _init(void) 774 { 775 int s; 776 777 s = ddi_soft_state_init(&evchan_ctlp, sizeof (evchan_ctl_t), 1); 778 if (s != 0) 779 return (s); 780 781 if ((s = mod_install(&modlinkage)) != 0) 782 ddi_soft_state_fini(&evchan_ctlp); 783 return (s); 784 } 785 786 int 787 _fini(void) 788 { 789 int s; 790 791 if ((s = mod_remove(&modlinkage)) != 0) 792 return (s); 793 794 ddi_soft_state_fini(&evchan_ctlp); 795 return (s); 796 } 797 798 int 799 _info(struct modinfo *modinfop) 800 { 801 return (mod_info(&modlinkage, modinfop)); 802 } 803