udev - Tag some subsystems/drivers
[dragonfly.git] / sys / kern / kern_udev.c
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 static 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
126         if (prop_dictionary_set(dict, key, ps) == false) {
127                 prop_object_release(ps);
128                 return ENOMEM;
129         }
130
131         prop_object_release(ps);
132         return 0;
133 }
134
135 static int
136 _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
137 {
138         prop_number_t   pn;
139
140         KKASSERT(dict != NULL);
141
142         pn = prop_number_create_integer(val);
143         if (pn == NULL)
144                 return ENOMEM;
145
146         if (prop_dictionary_set(dict, key, pn) == false) {
147                 prop_object_release(pn);
148                 return ENOMEM;
149         }
150
151         prop_object_release(pn);
152         return 0;
153 }
154
155 static int
156 _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
157 {
158         prop_number_t   pn;
159
160         KKASSERT(dict != NULL);
161
162         pn = prop_number_create_unsigned_integer(val);
163         if (pn == NULL)
164                 return ENOMEM;
165
166         if (prop_dictionary_set(dict, key, pn) == false) {
167                 prop_object_release(pn);
168                 return ENOMEM;
169         }
170
171         prop_object_release(pn);
172         return 0;
173 }
174
175 static int
176 _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
177 {
178         KKASSERT(dict != NULL);
179
180         prop_dictionary_remove(dict, key);
181
182         return 0;
183 }
184
185 /*
186  * Initialize an event dictionary, which contains three parameters to
187  * identify the device referred to (name, devnum, kptr) and the affected key.
188  */
189 static prop_dictionary_t
190 udev_init_dict_event(cdev_t dev, const char *key)
191 {
192         prop_dictionary_t       dict;
193         uint64_t        kptr;
194         int error;
195
196         kptr = (uint64_t)(uintptr_t)dev;
197         KKASSERT(dev != NULL);
198
199         dict = prop_dictionary_create();
200         if (dict == NULL) {
201                 log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
202                 return NULL;
203         }
204
205         if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
206                 goto error_out;
207         if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
208                 goto error_out;
209         if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
210                 goto error_out;
211         if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
212                 goto error_out;
213
214         return dict;
215
216 error_out:
217         prop_object_release(dict);
218         return NULL;
219 }
220
221 int
222 udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
223 {
224         prop_dictionary_t       dict;
225         int error;
226
227         KKASSERT(dev != NULL);
228
229         if (dev->si_dict == NULL) {
230                 error = udev_init_dict(dev);
231                 if (error)
232                         return -1;
233         }
234
235         /* Queue a key update event */
236         dict = udev_init_dict_event(dev, key);
237         if (dict == NULL)
238                 return ENOMEM;
239
240         if ((error = _udev_dict_set_cstr(dict, "value", str))) {
241                 prop_object_release(dict);
242                 return error;
243         }
244         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
245         prop_object_release(dict);
246
247         error = _udev_dict_set_cstr(dev->si_dict, key, str);
248         return error;
249 }
250
251 int
252 udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
253 {
254         prop_dictionary_t       dict;
255         int error;
256
257         KKASSERT(dev != NULL);
258
259         if (dev->si_dict == NULL) {
260                 error = udev_init_dict(dev);
261                 if (error)
262                         return -1;
263         }
264
265         /* Queue a key update event */
266         dict = udev_init_dict_event(dev, key);
267         if (dict == NULL)
268                 return ENOMEM;
269         if ((error = _udev_dict_set_int(dict, "value", val))) {
270                 prop_object_release(dict);
271                 return error;
272         }
273         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
274         prop_object_release(dict);
275
276         return _udev_dict_set_int(dev->si_dict, key, val);
277 }
278
279 int
280 udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
281 {
282         prop_dictionary_t       dict;
283         int error;
284
285         KKASSERT(dev != NULL);
286
287         if (dev->si_dict == NULL) {
288                 error = udev_init_dict(dev);
289                 if (error)
290                         return -1;
291         }
292
293         /* Queue a key update event */
294         dict = udev_init_dict_event(dev, key);
295         if (dict == NULL)
296                 return ENOMEM;
297         if ((error = _udev_dict_set_uint(dict, "value", val))) {
298                 prop_object_release(dict);
299                 return error;
300         }
301         udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
302         prop_object_release(dict);
303
304         return _udev_dict_set_uint(dev->si_dict, key, val);
305 }
306
307 int
308 udev_dict_delete_key(cdev_t dev, const char *key)
309 {
310         prop_dictionary_t       dict;
311
312         KKASSERT(dev != NULL);
313
314         /* Queue a key removal event */
315         dict = udev_init_dict_event(dev, key);
316         if (dict == NULL)
317                 return ENOMEM;
318         udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
319         prop_object_release(dict);
320
321         return _udev_dict_delete_key(dev->si_dict, key);
322 }
323
324 static int
325 udev_init_dict(cdev_t dev)
326 {
327         prop_dictionary_t dict;
328         uint64_t        kptr;
329         int error;
330
331         kptr = (uint64_t)(uintptr_t)dev;
332
333         KKASSERT(dev != NULL);
334
335         if (dev->si_dict != NULL) {
336 #if 0
337                 log(LOG_DEBUG,
338                     "udev_init_dict: new dict for %s, but has dict already (%p)!\n",
339                     dev->si_name, dev->si_dict);
340 #endif
341                 return 0;
342         }
343
344         dict = prop_dictionary_create();
345         if (dict == NULL) {
346                 log(LOG_DEBUG, "udev_init_dict: prop_dictionary_create() failed\n");
347                 return ENOMEM;
348         }
349
350         if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
351                 goto error_out;
352         if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
353                 goto error_out;
354         if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
355                 goto error_out;
356
357         /* XXX: The next 3 are marginallly useful, if at all */
358         if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
359                 goto error_out;
360         if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
361                 goto error_out;
362         if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
363                 goto error_out;
364
365         if ((error = _udev_dict_set_int(dict, "major", umajor(dev->si_inode))))
366                 goto error_out;
367         if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
368                 goto error_out;
369         if (dev->si_ops->head.name != NULL) {
370                 if ((error = _udev_dict_set_cstr(dict, "driver",
371                     __DECONST(char *, dev->si_ops->head.name))))
372                         goto error_out;
373         }
374
375         dev->si_dict = dict;
376         return 0;
377
378 error_out:
379         dev->si_dict = NULL;
380         prop_object_release(dict);
381         return error;
382 }
383
384 static int
385 udev_destroy_dict(cdev_t dev)
386 {
387         KKASSERT(dev != NULL);
388
389         if (dev->si_dict != NULL) {
390                 prop_object_release(dev->si_dict);
391                 dev->si_dict = NULL;
392         }
393
394         return 0;
395 }
396
397 static void
398 udev_event_insert(int ev_type, prop_dictionary_t dict)
399 {
400         struct udev_event_kernel *ev;
401
402         /* Only start queing events after client has initiated properly */
403         if (!udevctx.initiated)
404                 return;
405
406         /* XXX: use objcache eventually */
407         ev = kmalloc(sizeof(*ev), M_UDEV, M_WAITOK);
408         ev->ev.ev_dict = prop_dictionary_copy(dict);
409         if (ev->ev.ev_dict == NULL) {
410                 kfree(ev, M_UDEV);
411                 return;
412         }
413         ev->ev.ev_type = ev_type;
414
415         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
416         TAILQ_INSERT_TAIL(&udevctx.ev_queue, ev, link);
417         ++udevctx.qlen;
418         lockmgr(&udevctx.lock, LK_RELEASE);
419
420         wakeup(&udevctx);
421         selwakeup(&udevctx.sel);
422 }
423
424 static struct udev_event_kernel *
425 udev_event_remove(void)
426 {
427         struct udev_event_kernel *ev;
428
429         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
430         if (TAILQ_EMPTY(&udevctx.ev_queue)) {
431                 lockmgr(&udevctx.lock, LK_RELEASE);
432                 return NULL;
433         }
434
435         ev = TAILQ_FIRST(&udevctx.ev_queue);
436         TAILQ_REMOVE(&udevctx.ev_queue, ev, link);
437         --udevctx.qlen;
438         lockmgr(&udevctx.lock, LK_RELEASE);
439
440         return ev;
441 }
442
443 static void
444 udev_event_free(struct udev_event_kernel *ev)
445 {
446         /* XXX: use objcache eventually */
447         kfree(ev, M_UDEV);
448 }
449
450 static char *
451 udev_event_externalize(struct udev_event_kernel *ev)
452 {
453         prop_dictionary_t       dict;
454         char *xml;
455         int error;
456
457
458         dict = prop_dictionary_create();
459         if (dict == NULL) {
460                 log(LOG_DEBUG, "udev_event_externalize: prop_dictionary_create() failed\n");
461                 return NULL;
462         }
463
464         if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
465                 prop_object_release(dict);
466                 return NULL;
467         }
468
469         if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
470                 prop_object_release(dict);
471                 return NULL;
472         }
473
474         prop_object_release(ev->ev.ev_dict);
475
476         xml = prop_dictionary_externalize(dict);
477
478         prop_object_release(dict);
479
480         return xml;
481 }
482
483 int
484 udev_event_attach(cdev_t dev, char *name, int alias)
485 {
486         prop_dictionary_t       dict;
487         int error;
488
489         KKASSERT(dev != NULL);
490
491         error = ENOMEM;
492
493         if (alias) {
494                 dict = prop_dictionary_copy(dev->si_dict);
495                 if (dict == NULL)
496                         goto error_out;
497
498                 if ((error = _udev_dict_set_cstr(dict, "name", name))) {
499                         prop_object_release(dict);
500                         goto error_out;
501                 }
502
503                 _udev_dict_set_int(dict, "alias", 1);
504
505                 udev_event_insert(UDEV_EVENT_ATTACH, dict);
506                 prop_object_release(dict);
507         } else {
508                 error = udev_init_dict(dev);
509                 if (error)
510                         goto error_out;
511
512                 _udev_dict_set_int(dev->si_dict, "alias", 0);
513                 udev_event_insert(UDEV_EVENT_ATTACH, dev->si_dict);
514         }
515
516 error_out:
517         return error;
518 }
519
520 int
521 udev_event_detach(cdev_t dev, char *name, int alias)
522 {
523         prop_dictionary_t       dict;
524
525         KKASSERT(dev != NULL);
526
527         if (alias) {
528                 dict = prop_dictionary_copy(dev->si_dict);
529                 if (dict == NULL)
530                         goto error_out;
531
532                 if (_udev_dict_set_cstr(dict, "name", name)) {
533                         prop_object_release(dict);
534                         goto error_out;
535                 }
536
537                 _udev_dict_set_int(dict, "alias", 1);
538
539                 udev_event_insert(UDEV_EVENT_DETACH, dict);
540                 prop_object_release(dict);
541         } else {
542                 udev_event_insert(UDEV_EVENT_DETACH, dev->si_dict);
543         }
544
545 error_out:
546         udev_destroy_dict(dev);
547
548         return 0;
549 }
550
551 /*
552  * dev stuff
553  */
554 static int
555 udev_dev_open(struct dev_open_args *ap)
556 {
557         if (udevctx.opened)
558                 return EBUSY;
559
560         udevctx.opened = 1;
561
562         return 0;
563 }
564
565 static int
566 udev_dev_close(struct dev_close_args *ap)
567 {
568         udevctx.opened = 0;
569         udevctx.initiated = 0;
570         wakeup(&udevctx);
571
572         return 0;
573 }
574
575 static int
576 udev_dev_poll(struct dev_poll_args *ap)
577 {
578         int revents = 0;
579
580         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
581         if (ap->a_events & (POLLIN | POLLRDNORM)) {
582                 if (!TAILQ_EMPTY(&udevctx.ev_queue))
583                         revents = ap->a_events & (POLLIN | POLLRDNORM);
584                 else
585                         selrecord(curthread, &udevctx.sel);
586         }
587         lockmgr(&udevctx.lock, LK_RELEASE);
588
589         ap->a_events = revents;
590         return 0;
591 }
592
593 static int
594 udev_dev_read(struct dev_read_args *ap)
595 {
596         struct udev_event_kernel *ev;
597         struct uio *uio = ap->a_uio;
598         char    *xml;
599         size_t  len;
600         int     error;
601
602
603         lockmgr(&udevctx.lock, LK_EXCLUSIVE);
604
605         for (;;) {
606                 if ((ev = udev_event_remove()) != NULL) {
607                         if ((xml = udev_event_externalize(ev)) == NULL) {
608                                 lockmgr(&udevctx.lock, LK_RELEASE);
609                                 return ENOMEM;
610                         }
611
612                         len = strlen(xml) + 1; /* account for NULL-termination */
613                         if (uio->uio_resid < len) {
614                                 error = ENOMEM;
615                         } else {
616                                 error = uiomove((caddr_t)xml, len, uio);
617                         }
618
619                         kfree(xml, M_TEMP);
620                         udev_event_free(ev);
621                         lockmgr(&udevctx.lock, LK_RELEASE);
622                         return error;
623                 }
624
625                 if ((error = lksleep(&udevctx, &udevctx.lock, 0, "udevq", 0))) {
626                         lockmgr(&udevctx.lock, LK_RELEASE);
627                         return error;
628                 }
629         }
630
631         lockmgr(&udevctx.lock, LK_RELEASE);
632
633 }
634
635 static int
636 udev_dev_ioctl(struct dev_ioctl_args *ap)
637 {
638         prop_dictionary_t dict;
639         prop_object_t   po;
640         prop_string_t   ps;
641         struct plistref *pref;
642         int i, error;
643
644         error = 0;
645
646         switch(ap->a_cmd) {
647         case UDEVPROP:
648                 /* Use proplib(3) for userspace/kernel communication */
649                 pref = (struct plistref *)ap->a_data;
650                 error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
651                 if (error)
652                         return error;
653
654                 po = prop_dictionary_get(dict, "command");
655                 if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
656                         log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
657                         prop_object_release(dict);
658                         return EINVAL;
659                 }
660
661                 ps = po;
662                 /* Handle cmd */
663                 for(i = 0; cmd_fn[i].cmd != NULL; i++) {
664                         if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
665                                 break;
666                 }
667
668                 if (cmd_fn[i].cmd != NULL) {
669                         error = cmd_fn[i].fn(pref, ap->a_cmd, dict);
670                 } else {
671                         error = EINVAL;
672                 }
673
674                 //prop_object_release(po);
675                 prop_object_release(dict);
676                 break;
677         default:
678                 error = ENOTTY; /* Inappropriate ioctl for device */
679                 break;
680         }
681
682         return(error);
683 }
684
685 static void
686 udev_getdevs_scan_callback(cdev_t cdev, void *arg)
687 {
688         struct udev_prop_ctx *ctx = arg;
689
690         KKASSERT(arg != NULL);
691
692         if (cdev->si_dict == NULL)
693                 return;
694
695         if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
696                 ctx->error = EINVAL;
697                 return;
698         }
699 }
700
701 static int
702 udev_getdevs_ioctl(struct plistref *pref, u_long cmd, prop_dictionary_t dict)
703 {
704         prop_dictionary_t odict;
705         struct udev_prop_ctx ctx;
706         int error;
707
708         ctx.error = 0;
709         ctx.cdevs = prop_array_create();
710         if (ctx.cdevs == NULL) {
711                 log(LOG_DEBUG, "udev_getdevs_ioctl: prop_array_create() failed\n");
712                 return EINVAL;
713         }
714
715         /* XXX: need devfs_scan_alias_callback() */
716         devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
717
718         if (ctx.error != 0) {
719                 prop_object_release(ctx.cdevs);
720                 return (ctx.error);
721         }
722         udevctx.initiated = 1;
723
724         odict = prop_dictionary_create();
725         if (odict == NULL) {
726                 return ENOMEM;
727         }
728
729         if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
730                 log(LOG_DEBUG, "udev_getdevs_ioctl: prop_dictionary_set failed\n");
731                 prop_object_release(odict);
732                 return ENOMEM;
733         }
734
735         error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
736
737         prop_object_release(odict);
738         return error;
739 }
740
741
742 /*
743  * SYSINIT stuff
744  */
745 static void
746 udev_init(void)
747 {
748         lockinit(&udevctx.lock, "udevevq", 0, LK_CANRECURSE);
749         TAILQ_INIT(&udevctx.ev_queue);
750 }
751
752 static void
753 udev_uninit(void)
754 {
755 }
756
757 static void
758 udev_dev_init(void)
759 {
760         udev_dev = make_dev(&udev_dev_ops,
761             0,
762             UID_ROOT,
763             GID_WHEEL,
764             0600,
765             "udev");
766 }
767
768 static void
769 udev_dev_uninit(void)
770 {
771         destroy_dev(udev_dev);
772 }
773
774 SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_init, NULL);
775 SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY, udev_uninit, NULL);
776 SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_init, NULL);
777 SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY, udev_dev_uninit, NULL);