xref: /dflybsd-src/sys/kern/kern_udev.c (revision 2e7bf158f373428dba2c765c927f14d9e94f00a4)
1 /*
2  * Copyright (c) 2010 The DragonFly Project.  All rights reserved.
3  *
4  * This code is derived from software contributed to The DragonFly Project
5  * by Alex Hornung <ahornung@gmail.com>
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  *
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in
15  *    the documentation and/or other materials provided with the
16  *    distribution.
17  * 3. Neither the name of The DragonFly Project nor the names of its
18  *    contributors may be used to endorse or promote products derived
19  *    from this software without specific, prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24  * FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE
25  * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26  * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
27  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29  * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
30  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
31  * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34 #include <sys/param.h>
35 #include <sys/systm.h>
36 #include <sys/kernel.h>
37 #include <sys/proc.h>
38 #include <sys/buf.h>
39 #include <sys/conf.h>
40 #include <sys/poll.h>
41 #include <sys/ioccom.h>
42 #include <sys/malloc.h>
43 #include <sys/ctype.h>
44 #include <sys/syslog.h>
45 #include <sys/udev.h>
46 #include <sys/devfs.h>
47 #include <libprop/proplib.h>
48 
49 MALLOC_DEFINE(M_UDEV, "udev", "udev allocs");
50 
51 /* XXX: use UUIDs for identification; would need help from devfs */
52 
53 static cdev_t		udev_dev;
54 static d_open_t		udev_dev_open;
55 static d_close_t	udev_dev_close;
56 static d_read_t		udev_dev_read;
57 static d_poll_t		udev_dev_poll;
58 static d_ioctl_t	udev_dev_ioctl;
59 
60 static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *);
61 static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t);
62 static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t);
63 static int _udev_dict_delete_key(prop_dictionary_t, const char *);
64 static prop_dictionary_t udev_init_dict_event(cdev_t, const char *);
65 static int udev_init_dict(cdev_t);
66 static int udev_destroy_dict(cdev_t);
67 static void udev_event_insert(int, prop_dictionary_t);
68 static struct udev_event_kernel *udev_event_remove(void);
69 static void udev_event_free(struct udev_event_kernel *);
70 static char *udev_event_externalize(struct udev_event_kernel *);
71 static void udev_getdevs_scan_callback(cdev_t, void *);
72 static int udev_getdevs_ioctl(struct plistref *, u_long, prop_dictionary_t);
73 
74 struct cmd_function {
75 	const char *cmd;
76 	int  (*fn)(struct plistref *, u_long, prop_dictionary_t);
77 };
78 
79 struct udev_prop_ctx {
80 	prop_array_t cdevs;
81 	int error;
82 };
83 
84 struct udev_event_kernel {
85 	struct udev_event ev;
86 	TAILQ_ENTRY(udev_event_kernel)	link;
87 };
88 
89 struct udev_softc {
90 	int opened;
91 	int initiated;
92 
93 	struct selinfo sel;
94 
95 	int qlen;
96 	struct lock lock;
97 	TAILQ_HEAD(, udev_event_kernel) ev_queue;	/* list of thread_io */
98 } udevctx;
99 
100 static struct dev_ops udev_dev_ops = {
101 	{ "udev", 0, 0 },
102 	.d_open = udev_dev_open,
103 	.d_close = udev_dev_close,
104 	.d_read = udev_dev_read,
105 	.d_poll = udev_dev_poll,
106 	.d_ioctl = udev_dev_ioctl
107 };
108 
109 struct cmd_function cmd_fn[] = {
110 		{ .cmd = "getdevs", .fn = udev_getdevs_ioctl},
111 		{NULL, NULL}
112 };
113 
114 static int
115 _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
116 {
117 	prop_string_t	ps;
118 
119 	KKASSERT(dict != NULL);
120 
121 	ps = prop_string_create_cstring(str);
122 	if (ps == NULL)
123 		return ENOMEM;
124 
125 	if (prop_dictionary_set(dict, key, ps) == false) {
126 		prop_object_release(ps);
127 		return ENOMEM;
128 	}
129 
130 	prop_object_release(ps);
131 	return 0;
132 }
133 
134 static int
135 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
136 {
137 	prop_number_t	pn;
138 
139 	KKASSERT(dict != NULL);
140 
141 	pn = prop_number_create_integer(val);
142 	if (pn == NULL)
143 		return ENOMEM;
144 
145 	if (prop_dictionary_set(dict, key, pn) == false) {
146 		prop_object_release(pn);
147 		return ENOMEM;
148 	}
149 
150 	prop_object_release(pn);
151 	return 0;
152 }
153 
154 static int
155 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
156 {
157 	prop_number_t	pn;
158 
159 	KKASSERT(dict != NULL);
160 
161 	pn = prop_number_create_unsigned_integer(val);
162 	if (pn == NULL)
163 		return ENOMEM;
164 
165 	if (prop_dictionary_set(dict, key, pn) == false) {
166 		prop_object_release(pn);
167 		return ENOMEM;
168 	}
169 
170 	prop_object_release(pn);
171 	return 0;
172 }
173 
174 static int
175 _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
176 {
177 	KKASSERT(dict != NULL);
178 
179 	prop_dictionary_remove(dict, key);
180 
181 	return 0;
182 }
183 
184 /*
185  * Initialize an event dictionary, which contains three parameters to
186  * identify the device referred to (name, devnum, kptr) and the affected key.
187  */
188 static prop_dictionary_t
189 udev_init_dict_event(cdev_t dev, const char *key)
190 {
191 	prop_dictionary_t	dict;
192 	uint64_t	kptr;
193 	int error;
194 
195 	kptr = (uint64_t)(uintptr_t)dev;
196 	KKASSERT(dev != NULL);
197 
198 	dict = prop_dictionary_create();
199 	if (dict == NULL) {
200 		log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
201 		return NULL;
202 	}
203 
204 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
205 		goto error_out;
206 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
207 		goto error_out;
208 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
209 		goto error_out;
210 	if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
211 		goto error_out;
212 
213 error_out:
214 	prop_object_release(dict);
215 	return NULL;
216 }
217 
218 int
219 udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
220 {
221 	prop_dictionary_t	dict;
222 	int error;
223 
224 	KKASSERT(dev != NULL);
225 
226 	/* Queue a key update event */
227 	dict = udev_init_dict_event(dev, key);
228 	if (dict == NULL)
229 		return ENOMEM;
230 	if ((error = _udev_dict_set_cstr(dict, "value", str))) {
231 		prop_object_release(dict);
232 		return error;
233 	}
234 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
235 	prop_object_release(dict);
236 
237 	return _udev_dict_set_cstr(dev->si_dict, key, str);
238 }
239 
240 int
241 udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
242 {
243 	prop_dictionary_t	dict;
244 	int error;
245 
246 	KKASSERT(dev != NULL);
247 
248 	/* Queue a key update event */
249 	dict = udev_init_dict_event(dev, key);
250 	if (dict == NULL)
251 		return ENOMEM;
252 	if ((error = _udev_dict_set_int(dict, "value", val))) {
253 		prop_object_release(dict);
254 		return error;
255 	}
256 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
257 	prop_object_release(dict);
258 
259 	return _udev_dict_set_int(dev->si_dict, key, val);
260 }
261 
262 int
263 udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
264 {
265 	prop_dictionary_t	dict;
266 	int error;
267 
268 	KKASSERT(dev != NULL);
269 
270 	/* Queue a key update event */
271 	dict = udev_init_dict_event(dev, key);
272 	if (dict == NULL)
273 		return ENOMEM;
274 	if ((error = _udev_dict_set_uint(dict, "value", val))) {
275 		prop_object_release(dict);
276 		return error;
277 	}
278 	udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
279 	prop_object_release(dict);
280 
281 	return _udev_dict_set_uint(dev->si_dict, key, val);
282 }
283 
284 int
285 udev_dict_delete_key(cdev_t dev, const char *key)
286 {
287 	prop_dictionary_t	dict;
288 
289 	KKASSERT(dev != NULL);
290 
291 	/* Queue a key removal event */
292 	dict = udev_init_dict_event(dev, key);
293 	if (dict == NULL)
294 		return ENOMEM;
295 	udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
296 	prop_object_release(dict);
297 
298 	return _udev_dict_delete_key(dev->si_dict, key);
299 }
300 
301 static int
302 udev_init_dict(cdev_t dev)
303 {
304 	prop_dictionary_t dict;
305 	uint64_t	kptr;
306 	int error;
307 
308 	kptr = (uint64_t)(uintptr_t)dev;
309 
310 	KKASSERT(dev != NULL);
311 	dict = prop_dictionary_create();
312 	if (dict == NULL) {
313 		log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n");
314 		return ENOMEM;
315 	}
316 
317 	if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
318 		goto error_out;
319 	if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
320 		goto error_out;
321 	if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
322 		goto error_out;
323 
324 	/* XXX: The next 3 are marginallly useful, if at all */
325 	if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
326 		goto error_out;
327 	if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
328 		goto error_out;
329 	if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
330 		goto error_out;
331 
332 	if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode))))
333 		goto error_out;
334 	if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
335 		goto error_out;
336 
337 	dev->si_dict = dict;
338 	return 0;
339 
340 error_out:
341 	dev->si_dict = NULL;
342 	prop_object_release(dict);
343 	return error;
344 }
345 
346 static int
347 udev_destroy_dict(cdev_t dev)
348 {
349 	KKASSERT(dev != NULL);
350 
351 	if (dev->si_dict != NULL) {
352 		prop_object_release(dev->si_dict);
353 		dev->si_dict = NULL;
354 	}
355 
356 	return 0;
357 }
358 
359 static void
360 udev_event_insert(int ev_type, prop_dictionary_t dict)
361 {
362 	struct udev_event_kernel *ev;
363 
364 	/* Only start queing events after client has initiated properly */
365 	if (!udevctx.initiated)
366 		return;
367 
368 	/* XXX: use objcache eventually */
369 	ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK);
370 	ev->ev.ev_dict = prop_dictionary_copy(dict);
371 	if (ev->ev.ev_dict == NULL) {
372 		kfree(ev, M_UDEV);
373 		return;
374 	}
375 	ev->ev.ev_type = ev_type;
376 
377 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
378 	TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link);
379 	++udevctx.qlen;
380 	lockmgr(&udevctx.lock, LK_RELEASE);
381 
382 	wakeup(&udevctx);
383 	selwakeup(&udevctx.sel);
384 }
385 
386 static struct udev_event_kernel *
387 udev_event_remove(void)
388 {
389 	struct udev_event_kernel *ev;
390 
391 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
392 	if (TAILQ_EMPTY(&udevctx.ev_queue)) {
393 		lockmgr(&udevctx.lock, LK_RELEASE);
394 		return NULL;
395 	}
396 
397 	ev = TAILQ_FIRST(&udevctx.ev_queue);
398 	TAILQ_REMOVE(&udevctx.ev_queue, ev, link);
399 	--udevctx.qlen;
400 	lockmgr(&udevctx.lock, LK_RELEASE);
401 
402 	return ev;
403 }
404 
405 static void
406 udev_event_free(struct udev_event_kernel *ev)
407 {
408 	/* XXX: use objcache eventually */
409 	kfree(ev, M_UDEV);
410 }
411 
412 static char *
413 udev_event_externalize(struct udev_event_kernel *ev)
414 {
415 	prop_dictionary_t	dict;
416 	char *xml;
417 	int error;
418 
419 
420 	dict = prop_dictionary_create();
421 	if (dict == NULL) {
422 		log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n");
423 		return NULL;
424 	}
425 
426 	if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
427 		prop_object_release(dict);
428 		return NULL;
429 	}
430 
431 	if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
432 		prop_object_release(dict);
433 		return NULL;
434 	}
435 
436 	prop_object_release(ev->ev.ev_dict);
437 
438 	xml = prop_dictionary_externalize(dict);
439 
440 	prop_object_release(dict);
441 
442 	return xml;
443 }
444 
445 int
446 udev_event_attach(cdev_t dev, char *name, int alias)
447 {
448 	prop_dictionary_t	dict;
449 	int error;
450 
451 	KKASSERT(dev != NULL);
452 
453 	error = udev_init_dict(dev);
454 	if (error)
455 		goto error_out;
456 
457 	if (alias) {
458 		dict = prop_dictionary_copy(dev->si_dict);
459 		if (dict == NULL)
460 			goto error_out;
461 
462 		if ((error = _udev_dict_set_cstr(dict, "name", name))) {
463 			prop_object_release(dict);
464 			goto error_out;
465 		}
466 
467 		_udev_dict_set_int(dict, "alias", 1);
468 
469 		udev_event_insert(UDEV_EVENT_ATTACH, dict);
470 		prop_object_release(dict);
471 	} else {
472 		_udev_dict_set_int(dev->si_dict, "alias", 0);
473 		udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict);
474 	}
475 
476 error_out:
477 	return error;
478 }
479 
480 int
481 udev_event_detach(cdev_t dev, char *name, int alias)
482 {
483 	prop_dictionary_t	dict;
484 
485 	KKASSERT(dev != NULL);
486 
487 	if (alias) {
488 		dict = prop_dictionary_copy(dev->si_dict);
489 		if (dict == NULL)
490 			goto error_out;
491 
492 		if (_udev_dict_set_cstr(dict, "name", name)) {
493 			prop_object_release(dict);
494 			goto error_out;
495 		}
496 
497 		_udev_dict_set_int(dict, "alias", 1);
498 
499 		udev_event_insert(UDEV_EVENT_DETACH, dict);
500 		prop_object_release(dict);
501 	} else {
502 		udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict);
503 	}
504 
505 error_out:
506 	udev_destroy_dict(dev);
507 
508 	return 0;
509 }
510 
511 /*
512  * dev stuff
513  */
514 static int
515 udev_dev_open(struct dev_open_args *ap)
516 {
517 	if (udevctx.opened)
518 		return EBUSY;
519 
520 	udevctx.opened = 1;
521 
522 	return 0;
523 }
524 
525 static int
526 udev_dev_close(struct dev_close_args *ap)
527 {
528 	udevctx.opened = 0;
529 	udevctx.initiated = 0;
530 	wakeup(&udevctx);
531 
532 	return 0;
533 }
534 
535 static int
536 udev_dev_poll(struct dev_poll_args *ap)
537 {
538 	int revents = 0;
539 
540         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
541         if (ap->a_events & (POLLIN | POLLRDNORM)) {
542                 if (!TAILQ_EMPTY(&udevctx.ev_queue))
543                         revents = ap->a_events & (POLLIN | POLLRDNORM);
544                 else
545                         selrecord(curthread, &udevctx.sel);
546         }
547         lockmgr(&udevctx.lock, LK_RELEASE);
548 
549         ap->a_events = revents;
550         return 0;
551 }
552 
553 static int
554 udev_dev_read(struct dev_read_args *ap)
555 {
556 	struct udev_event_kernel *ev;
557 	struct uio *uio = ap->a_uio;
558 	char	*xml;
559 	size_t	len;
560 	int	error;
561 
562 
563 	lockmgr(&udevctx.lock, LK_EXCLUSIVE);
564 
565 	for (;;) {
566 		if ((ev = udev_event_remove()) != NULL) {
567 			if ((xml = udev_event_externalize(ev)) == NULL) {
568 				lockmgr(&udevctx.lock, LK_RELEASE);
569 				return ENOMEM;
570 			}
571 
572 			len = strlen(xml) + 1; /* account for NULL-termination */
573 			if (uio->uio_resid < len) {
574 				error = ENOMEM;
575 			} else {
576 				error = uiomove((caddr_t)xml, len, uio);
577 			}
578 
579 			kfree(xml, M_TEMP);
580 			udev_event_free(ev);
581 			lockmgr(&udevctx.lock, LK_RELEASE);
582 			return error;
583 		}
584 
585 		if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) {
586 			lockmgr(&udevctx.lock, LK_RELEASE);
587 			return error;
588 		}
589 	}
590 
591 	lockmgr(&udevctx.lock, LK_RELEASE);
592 
593 }
594 
595 static int
596 udev_dev_ioctl(struct dev_ioctl_args *ap)
597 {
598 	prop_dictionary_t dict;
599 	prop_object_t	po;
600 	prop_string_t	ps;
601 	struct plistref *pref;
602 	int i, error;
603 
604 	error = 0;
605 
606 	switch(ap->a_cmd) {
607 	case UDEVPROP:
608 		/* Use proplib(3) for userspace/kernel communication */
609 		pref = (struct plistref *)ap->a_data;
610 		error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
611 		if (error)
612 			return error;
613 
614 		po = prop_dictionary_get(dict, "command");
615 		if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
616 			log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
617 			prop_object_release(dict);
618 			return EINVAL;
619 		}
620 
621 		ps = po;
622 		/* Handle cmd */
623 		for(i = 0; cmd_fn[i].cmd != NULL; i++) {
624 			if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
625 				break;
626 		}
627 
628 		if (cmd_fn[i].cmd != NULL) {
629 			log(LOG_DEBUG, "udev: ioctl %s called\n", cmd_fn[i].cmd);
630 			error = cmd_fn[i].fn(pref, ap->a_cmd, dict);
631 		} else {
632 			error = EINVAL;
633 		}
634 
635 		//prop_object_release(po);
636 		kprintf("foo\n");
637 		prop_object_release(dict);
638 		break;
639 	default:
640 		error = ENOTTY; /* Inappropriate ioctl for device */
641 		break;
642 	}
643 
644 	return(error);
645 }
646 
647 static void
648 udev_getdevs_scan_callback(cdev_t cdev, void *arg)
649 {
650 	struct udev_prop_ctx *ctx = arg;
651 
652 	KKASSERT(arg != NULL);
653 
654 	if (cdev->si_dict == NULL)
655 		return;
656 
657 	if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
658 		ctx->error = EINVAL;
659 		return;
660 	}
661 }
662 
663 static int
664 udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict)
665 {
666 	prop_dictionary_t odict;
667 	struct udev_prop_ctx ctx;
668 	int error;
669 
670 	ctx.error = 0;
671 	ctx.cdevs = prop_array_create();
672 	if (ctx.cdevs == NULL) {
673 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n");
674 		return EINVAL;
675 	}
676 
677 	/* XXX: need devfs_scan_alias_callback() */
678 	devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
679 
680 	if (ctx.error != 0) {
681 		prop_object_release(ctx.cdevs);
682 		return (ctx.error);
683 	}
684 	udevctx.initiated = 1;
685 
686 	odict = prop_dictionary_create();
687 	if (odict == NULL) {
688 		return ENOMEM;
689 	}
690 
691 	if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
692 		log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n");
693 		prop_object_release(odict);
694 		return ENOMEM;
695 	}
696 
697 	error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
698 
699 	/* XXX: need to release ctx.cdevs? */
700 	prop_object_release(odict);
701 	return error;
702 }
703 
704 
705 /*
706  * SYSINIT stuff
707  */
708 static void
709 udev_init(void)
710 {
711 	lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE);
712 	TAILQ_INIT(&udevctx.ev_queue);
713 }
714 
715 static void
716 udev_uninit(void)
717 {
718 }
719 
720 static void
721 udev_dev_init(void)
722 {
723 	udev_dev = make_dev(&udev_dev_ops,
724             0,
725             UID_ROOT,
726             GID_WHEEL,
727             0600,
728             "udev");
729 }
730 
731 static void
732 udev_dev_uninit(void)
733 {
734 	destroy_dev(udev_dev);
735 }
736 
737 SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL);
738 SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL);
739 SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL);
740 SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL);
741