blob: 69065b9506397c9dbeaae5f45f7e2601fffde374 [file] [log] [blame]
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07001/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
Sherry Moore19397402008-09-22 16:30:26 -07005 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -07007 *
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/*
Gavin Maltbyf6e214c2010-07-30 17:04:17 +100022 * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070023 */
24
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -070025
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
45static dev_info_t *sysevent_devi;
46
47/* Definitions for binding handle array */
48static ulong_t sysevent_bitmap_initial = 1; /* index 0 indicates error */
49static ulong_t *sysevent_minor_bitmap = &sysevent_bitmap_initial;
50static size_t sysevent_minor_bits = BT_NBIPUL;
51static kmutex_t sysevent_minor_mutex;
52
53/*
54 * evchan_ctl acts as a container for the binding handle
55 */
56typedef struct evchan_ctl {
57 evchan_t *chp;
58} evchan_ctl_t;
59
60static 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 */
68static int
69sysevent_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 */
84static ulong_t *
85sysevent_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
103static void
104sysevent_minor_free(ulong_t *bitmap)
105{
106 if (bitmap != &sysevent_bitmap_initial)
107 kmem_free(bitmap, BT_SIZEOFMAP(sysevent_minor_bits));
108}
109
110static index_t
111sysevent_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
135static void
136sysevent_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
144static void
145sysevent_minor_init(void)
146{
147 mutex_init(&sysevent_minor_mutex, NULL, MUTEX_DEFAULT, NULL);
148}
149
150/* ARGSUSED */
151static int
152sysevent_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 */
203static int
204sysevent_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 */
252static int
253sysevent_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 */
287static int
288sysevent_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 */
348static int
349sysevent_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 */
387static int
388sysevent_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 */
421static int
422sysevent_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
Gavin Maltbyf6e214c2010-07-30 17:04:17 +1000473/* ARGSUSED */
474static int
475sysevent_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 */
519static int
520sysevent_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
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700583/*ARGSUSED*/
584static int
585sysevent_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;
Gavin Maltbyf6e214c2010-07-30 17:04:17 +1000612 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;
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700618 default:
619 rc = EINVAL;
620 }
621
622 return (rc);
623}
624
625/*ARGSUSED*/
626static int
627sysevent_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*/
654static int
655sysevent_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 */
681static int
682sysevent_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 */
697static int
698sysevent_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
717static int
718sysevent_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
729static 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
749static 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 */
Sherry Moore19397402008-09-22 16:30:26 -0700760 nulldev, /* power */
761 ddi_quiesce_not_needed, /* quiesce */
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700762};
763
764static struct modldrv modldrv = {
Sherry Moore19397402008-09-22 16:30:26 -0700765 &mod_driverops, "sysevent driver", &sysevent_ops
stevel@tonic-gate7c478bd2005-06-14 00:00:00 -0700766};
767
768static struct modlinkage modlinkage = {
769 MODREV_1, &modldrv, NULL
770};
771
772int
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
786int
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
798int
799_info(struct modinfo *modinfop)
800{
801 return (mod_info(&modlinkage, modinfop));
802}