13a3826b3SAlex Hornung /*
2108c361cSMatthew Dillon * Copyright (c) 2010,2018 The DragonFly Project. All rights reserved.
33a3826b3SAlex Hornung *
43a3826b3SAlex Hornung * This code is derived from software contributed to The DragonFly Project
53a3826b3SAlex Hornung * by Alex Hornung <ahornung@gmail.com>
63a3826b3SAlex Hornung *
73a3826b3SAlex Hornung * Redistribution and use in source and binary forms, with or without
83a3826b3SAlex Hornung * modification, are permitted provided that the following conditions
93a3826b3SAlex Hornung * are met:
103a3826b3SAlex Hornung *
113a3826b3SAlex Hornung * 1. Redistributions of source code must retain the above copyright
123a3826b3SAlex Hornung * notice, this list of conditions and the following disclaimer.
133a3826b3SAlex Hornung * 2. Redistributions in binary form must reproduce the above copyright
143a3826b3SAlex Hornung * notice, this list of conditions and the following disclaimer in
153a3826b3SAlex Hornung * the documentation and/or other materials provided with the
163a3826b3SAlex Hornung * distribution.
173a3826b3SAlex Hornung * 3. Neither the name of The DragonFly Project nor the names of its
183a3826b3SAlex Hornung * contributors may be used to endorse or promote products derived
193a3826b3SAlex Hornung * from this software without specific, prior written permission.
203a3826b3SAlex Hornung *
213a3826b3SAlex Hornung * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
223a3826b3SAlex Hornung * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
233a3826b3SAlex Hornung * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
243a3826b3SAlex Hornung * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
253a3826b3SAlex Hornung * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
263a3826b3SAlex Hornung * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING,
273a3826b3SAlex Hornung * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
283a3826b3SAlex Hornung * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
293a3826b3SAlex Hornung * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
303a3826b3SAlex Hornung * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
313a3826b3SAlex Hornung * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
323a3826b3SAlex Hornung * SUCH DAMAGE.
333a3826b3SAlex Hornung */
343a3826b3SAlex Hornung #include <sys/param.h>
353a3826b3SAlex Hornung #include <sys/systm.h>
36*13dd34d8Szrj #include <sys/uio.h>
373a3826b3SAlex Hornung #include <sys/kernel.h>
383a3826b3SAlex Hornung #include <sys/proc.h>
393a3826b3SAlex Hornung #include <sys/buf.h>
403a3826b3SAlex Hornung #include <sys/conf.h>
4138714400SSamuel J. Greear #include <sys/event.h>
4284f2d39fSMatthew Dillon #include <sys/vnode.h>
433a3826b3SAlex Hornung #include <sys/malloc.h>
447ac0a0e3SMichael Neumann #include <sys/objcache.h>
453a3826b3SAlex Hornung #include <sys/ctype.h>
463a3826b3SAlex Hornung #include <sys/syslog.h>
473a3826b3SAlex Hornung #include <sys/udev.h>
483a3826b3SAlex Hornung #include <sys/devfs.h>
493a3826b3SAlex Hornung #include <libprop/proplib.h>
503a3826b3SAlex Hornung
513a3826b3SAlex Hornung MALLOC_DEFINE(M_UDEV, "udev", "udev allocs");
527ac0a0e3SMichael Neumann static struct objcache *udev_event_kernel_cache;
533a3826b3SAlex Hornung
543a3826b3SAlex Hornung /* XXX: use UUIDs for identification; would need help from devfs */
553a3826b3SAlex Hornung
563a3826b3SAlex Hornung static cdev_t udev_dev;
573a3826b3SAlex Hornung static d_open_t udev_dev_open;
583a3826b3SAlex Hornung static d_close_t udev_dev_close;
593a3826b3SAlex Hornung static d_read_t udev_dev_read;
6038714400SSamuel J. Greear static d_kqfilter_t udev_dev_kqfilter;
613a3826b3SAlex Hornung static d_ioctl_t udev_dev_ioctl;
629b881e96SMatthew Dillon static d_clone_t udev_dev_clone;
633a3826b3SAlex Hornung
643a3826b3SAlex Hornung struct udev_prop_ctx {
653a3826b3SAlex Hornung prop_array_t cdevs;
663a3826b3SAlex Hornung int error;
673a3826b3SAlex Hornung };
683a3826b3SAlex Hornung
693a3826b3SAlex Hornung struct udev_event_kernel {
703a3826b3SAlex Hornung struct udev_event ev;
713a3826b3SAlex Hornung TAILQ_ENTRY(udev_event_kernel) link;
723a3826b3SAlex Hornung };
733a3826b3SAlex Hornung
743a3826b3SAlex Hornung struct udev_softc {
759b881e96SMatthew Dillon TAILQ_ENTRY(udev_softc) entry;
763a3826b3SAlex Hornung int opened;
773a3826b3SAlex Hornung int initiated;
789b881e96SMatthew Dillon int unit;
799b881e96SMatthew Dillon cdev_t dev;
803a3826b3SAlex Hornung
819b881e96SMatthew Dillon struct udev_event_kernel marker; /* udev_evq marker */
829b881e96SMatthew Dillon };
833a3826b3SAlex Hornung
849b881e96SMatthew Dillon struct cmd_function {
859b881e96SMatthew Dillon const char *cmd;
869b881e96SMatthew Dillon int (*fn)(struct udev_softc *, struct plistref *,
879b881e96SMatthew Dillon u_long, prop_dictionary_t);
889b881e96SMatthew Dillon };
899b881e96SMatthew Dillon
909b881e96SMatthew Dillon static int _udev_dict_set_cstr(prop_dictionary_t, const char *, char *);
919b881e96SMatthew Dillon static int _udev_dict_set_int(prop_dictionary_t, const char *, int64_t);
929b881e96SMatthew Dillon static int _udev_dict_set_uint(prop_dictionary_t, const char *, uint64_t);
939b881e96SMatthew Dillon static int _udev_dict_delete_key(prop_dictionary_t, const char *);
949b881e96SMatthew Dillon static prop_dictionary_t udev_init_dict_event(cdev_t, const char *);
959b881e96SMatthew Dillon static int udev_init_dict(cdev_t);
969b881e96SMatthew Dillon static int udev_destroy_dict(cdev_t);
979b881e96SMatthew Dillon static void udev_event_insert(int, prop_dictionary_t);
989b881e96SMatthew Dillon static void udev_clean_events_locked(void);
999b881e96SMatthew Dillon static char *udev_event_externalize(struct udev_event_kernel *);
1009b881e96SMatthew Dillon static void udev_getdevs_scan_callback(char *, cdev_t, bool, void *);
1019b881e96SMatthew Dillon static int udev_getdevs_ioctl(struct udev_softc *, struct plistref *,
1029b881e96SMatthew Dillon u_long, prop_dictionary_t);
1039b881e96SMatthew Dillon static void udev_dev_filter_detach(struct knote *);
1049b881e96SMatthew Dillon static int udev_dev_filter_read(struct knote *, long);
1053a3826b3SAlex Hornung
1063a3826b3SAlex Hornung static struct dev_ops udev_dev_ops = {
107d4b8aec4SMatthew Dillon { "udev", 0, 0 },
1083a3826b3SAlex Hornung .d_open = udev_dev_open,
1093a3826b3SAlex Hornung .d_close = udev_dev_close,
1103a3826b3SAlex Hornung .d_read = udev_dev_read,
11138714400SSamuel J. Greear .d_kqfilter = udev_dev_kqfilter,
1123a3826b3SAlex Hornung .d_ioctl = udev_dev_ioctl
1133a3826b3SAlex Hornung };
1143a3826b3SAlex Hornung
115f5d8307cSAlex Hornung static struct cmd_function cmd_fn[] = {
1163a3826b3SAlex Hornung { .cmd = "getdevs", .fn = udev_getdevs_ioctl},
1173a3826b3SAlex Hornung {NULL, NULL}
1183a3826b3SAlex Hornung };
1193a3826b3SAlex Hornung
1200cf7fc2cSSascha Wildner DEVFS_DEFINE_CLONE_BITMAP(udev);
1219b881e96SMatthew Dillon
1229b881e96SMatthew Dillon static TAILQ_HEAD(, udev_softc) udevq;
1239b881e96SMatthew Dillon static TAILQ_HEAD(, udev_event_kernel) udev_evq;
1249b881e96SMatthew Dillon static struct kqinfo udev_kq;
1259b881e96SMatthew Dillon static struct lock udev_lk;
1269b881e96SMatthew Dillon static int udev_evqlen;
1279b881e96SMatthew Dillon static int udev_initiated_count;
12884f2d39fSMatthew Dillon static int udev_open_count;
12984f2d39fSMatthew Dillon static int udev_seqwait;
13084f2d39fSMatthew Dillon static int udev_seq;
131108c361cSMatthew Dillon static struct lock udev_dict_lk = LOCK_INITIALIZER("dict", 0, 0);
132108c361cSMatthew Dillon
133108c361cSMatthew Dillon /*
134108c361cSMatthew Dillon * Acquire the device's si_dict and lock the device's si_dict field.
135108c361cSMatthew Dillon * If the device does not have an attached dictionary, NULL is returned.
136108c361cSMatthew Dillon *
137108c361cSMatthew Dillon * This function must be matched by a udev_put_dict() call regardless of
138108c361cSMatthew Dillon * the return value. The device field is STILL LOCKED even when NULL is
139108c361cSMatthew Dillon * returned.
140108c361cSMatthew Dillon *
141108c361cSMatthew Dillon * Currently a single global lock is implemented.
142108c361cSMatthew Dillon */
143108c361cSMatthew Dillon static prop_dictionary_t
udev_get_dict(cdev_t dev)144108c361cSMatthew Dillon udev_get_dict(cdev_t dev)
145108c361cSMatthew Dillon {
146108c361cSMatthew Dillon prop_dictionary_t udict;
147108c361cSMatthew Dillon
148108c361cSMatthew Dillon lockmgr(&udev_dict_lk, LK_EXCLUSIVE|LK_CANRECURSE);
149108c361cSMatthew Dillon udict = dev->si_dict;
150108c361cSMatthew Dillon if (udict)
151108c361cSMatthew Dillon prop_object_retain(udict);
152108c361cSMatthew Dillon return udict;
153108c361cSMatthew Dillon }
154108c361cSMatthew Dillon
155108c361cSMatthew Dillon /*
156108c361cSMatthew Dillon * Release the dictionary previously returned by udev_get_dict() and unlock
157108c361cSMatthew Dillon * the device's si_dict field. udict may be NULL.
158108c361cSMatthew Dillon */
159108c361cSMatthew Dillon static void
udev_put_dict(cdev_t dev,prop_dictionary_t udict)160108c361cSMatthew Dillon udev_put_dict(cdev_t dev, prop_dictionary_t udict)
161108c361cSMatthew Dillon {
162108c361cSMatthew Dillon if (udict)
163108c361cSMatthew Dillon prop_object_release(udict);
164108c361cSMatthew Dillon lockmgr(&udev_dict_lk, LK_RELEASE);
165108c361cSMatthew Dillon }
1669b881e96SMatthew Dillon
1673a3826b3SAlex Hornung static int
_udev_dict_set_cstr(prop_dictionary_t dict,const char * key,char * str)1683a3826b3SAlex Hornung _udev_dict_set_cstr(prop_dictionary_t dict, const char *key, char *str)
1693a3826b3SAlex Hornung {
1703a3826b3SAlex Hornung prop_string_t ps;
1713a3826b3SAlex Hornung
1723a3826b3SAlex Hornung KKASSERT(dict != NULL);
1733a3826b3SAlex Hornung
1743a3826b3SAlex Hornung ps = prop_string_create_cstring(str);
175f5d8307cSAlex Hornung if (ps == NULL) {
1763a3826b3SAlex Hornung return ENOMEM;
177f5d8307cSAlex Hornung }
1783a3826b3SAlex Hornung
1793a3826b3SAlex Hornung if (prop_dictionary_set(dict, key, ps) == false) {
1803a3826b3SAlex Hornung prop_object_release(ps);
1813a3826b3SAlex Hornung return ENOMEM;
1823a3826b3SAlex Hornung }
1833a3826b3SAlex Hornung
1843a3826b3SAlex Hornung prop_object_release(ps);
1853a3826b3SAlex Hornung return 0;
1863a3826b3SAlex Hornung }
1873a3826b3SAlex Hornung
1883a3826b3SAlex Hornung static int
_udev_dict_set_int(prop_dictionary_t dict,const char * key,int64_t val)1893a3826b3SAlex Hornung _udev_dict_set_int(prop_dictionary_t dict, const char *key, int64_t val)
1903a3826b3SAlex Hornung {
1913a3826b3SAlex Hornung prop_number_t pn;
1923a3826b3SAlex Hornung
1933a3826b3SAlex Hornung KKASSERT(dict != NULL);
1943a3826b3SAlex Hornung
1953a3826b3SAlex Hornung pn = prop_number_create_integer(val);
1963a3826b3SAlex Hornung if (pn == NULL)
1973a3826b3SAlex Hornung return ENOMEM;
1983a3826b3SAlex Hornung
1993a3826b3SAlex Hornung if (prop_dictionary_set(dict, key, pn) == false) {
2003a3826b3SAlex Hornung prop_object_release(pn);
2013a3826b3SAlex Hornung return ENOMEM;
2023a3826b3SAlex Hornung }
2033a3826b3SAlex Hornung
2043a3826b3SAlex Hornung prop_object_release(pn);
2053a3826b3SAlex Hornung return 0;
2063a3826b3SAlex Hornung }
2073a3826b3SAlex Hornung
2083a3826b3SAlex Hornung static int
_udev_dict_set_uint(prop_dictionary_t dict,const char * key,uint64_t val)2093a3826b3SAlex Hornung _udev_dict_set_uint(prop_dictionary_t dict, const char *key, uint64_t val)
2103a3826b3SAlex Hornung {
2113a3826b3SAlex Hornung prop_number_t pn;
2123a3826b3SAlex Hornung
2133a3826b3SAlex Hornung KKASSERT(dict != NULL);
2143a3826b3SAlex Hornung
2153a3826b3SAlex Hornung pn = prop_number_create_unsigned_integer(val);
2163a3826b3SAlex Hornung if (pn == NULL)
2173a3826b3SAlex Hornung return ENOMEM;
2183a3826b3SAlex Hornung
2193a3826b3SAlex Hornung if (prop_dictionary_set(dict, key, pn) == false) {
2203a3826b3SAlex Hornung prop_object_release(pn);
2213a3826b3SAlex Hornung return ENOMEM;
2223a3826b3SAlex Hornung }
2233a3826b3SAlex Hornung
2243a3826b3SAlex Hornung prop_object_release(pn);
2253a3826b3SAlex Hornung return 0;
2263a3826b3SAlex Hornung }
2273a3826b3SAlex Hornung
2283a3826b3SAlex Hornung static int
_udev_dict_delete_key(prop_dictionary_t dict,const char * key)2293a3826b3SAlex Hornung _udev_dict_delete_key(prop_dictionary_t dict, const char *key)
2303a3826b3SAlex Hornung {
2313a3826b3SAlex Hornung KKASSERT(dict != NULL);
2323a3826b3SAlex Hornung
2333a3826b3SAlex Hornung prop_dictionary_remove(dict, key);
2343a3826b3SAlex Hornung
2353a3826b3SAlex Hornung return 0;
2363a3826b3SAlex Hornung }
2373a3826b3SAlex Hornung
2383a3826b3SAlex Hornung /*
2393a3826b3SAlex Hornung * Initialize an event dictionary, which contains three parameters to
2403a3826b3SAlex Hornung * identify the device referred to (name, devnum, kptr) and the affected key.
2413a3826b3SAlex Hornung */
2423a3826b3SAlex Hornung static prop_dictionary_t
udev_init_dict_event(cdev_t dev,const char * key)2433a3826b3SAlex Hornung udev_init_dict_event(cdev_t dev, const char *key)
2443a3826b3SAlex Hornung {
2453a3826b3SAlex Hornung prop_dictionary_t dict;
2463a3826b3SAlex Hornung uint64_t kptr;
2473a3826b3SAlex Hornung int error;
2483a3826b3SAlex Hornung
2493a3826b3SAlex Hornung kptr = (uint64_t)(uintptr_t)dev;
2503a3826b3SAlex Hornung KKASSERT(dev != NULL);
2513a3826b3SAlex Hornung
2523a3826b3SAlex Hornung dict = prop_dictionary_create();
2533a3826b3SAlex Hornung if (dict == NULL) {
2543a3826b3SAlex Hornung log(LOG_DEBUG, "udev_init_dict_event: prop_dictionary_create() failed\n");
2553a3826b3SAlex Hornung return NULL;
2563a3826b3SAlex Hornung }
2573a3826b3SAlex Hornung
2583a3826b3SAlex Hornung if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
2593a3826b3SAlex Hornung goto error_out;
2603a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
2613a3826b3SAlex Hornung goto error_out;
262539b1063SAlex Hornung if ((error = _udev_dict_set_uint(dict, "devtype", (dev_dflags(dev) & D_TYPEMASK))))
263751a320cSAlex Hornung goto error_out;
2643a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
2653a3826b3SAlex Hornung goto error_out;
2663a3826b3SAlex Hornung if ((error = _udev_dict_set_cstr(dict, "key", __DECONST(char *, key))))
2673a3826b3SAlex Hornung goto error_out;
2683a3826b3SAlex Hornung
269f5d8307cSAlex Hornung return dict;
270f5d8307cSAlex Hornung
2713a3826b3SAlex Hornung error_out:
2723a3826b3SAlex Hornung prop_object_release(dict);
2733a3826b3SAlex Hornung return NULL;
2743a3826b3SAlex Hornung }
2753a3826b3SAlex Hornung
2763a3826b3SAlex Hornung int
udev_dict_set_cstr(cdev_t dev,const char * key,char * str)2773a3826b3SAlex Hornung udev_dict_set_cstr(cdev_t dev, const char *key, char *str)
2783a3826b3SAlex Hornung {
2793a3826b3SAlex Hornung prop_dictionary_t dict;
280108c361cSMatthew Dillon prop_dictionary_t udict;
2813a3826b3SAlex Hornung int error;
2823a3826b3SAlex Hornung
2833a3826b3SAlex Hornung KKASSERT(dev != NULL);
2843a3826b3SAlex Hornung
285108c361cSMatthew Dillon while ((udict = udev_get_dict(dev)) == NULL) {
286f5d8307cSAlex Hornung error = udev_init_dict(dev);
287108c361cSMatthew Dillon udev_put_dict(dev, udict);
288f5d8307cSAlex Hornung if (error)
289f5d8307cSAlex Hornung return -1;
290f5d8307cSAlex Hornung }
291f5d8307cSAlex Hornung
2923a3826b3SAlex Hornung /* Queue a key update event */
2933a3826b3SAlex Hornung dict = udev_init_dict_event(dev, key);
294108c361cSMatthew Dillon if (dict == NULL) {
295108c361cSMatthew Dillon error = ENOMEM;
296108c361cSMatthew Dillon goto errout;
297108c361cSMatthew Dillon }
298f5d8307cSAlex Hornung
2993a3826b3SAlex Hornung if ((error = _udev_dict_set_cstr(dict, "value", str))) {
3003a3826b3SAlex Hornung prop_object_release(dict);
301108c361cSMatthew Dillon goto errout;
3023a3826b3SAlex Hornung }
3033a3826b3SAlex Hornung udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
3043a3826b3SAlex Hornung prop_object_release(dict);
305108c361cSMatthew Dillon error = _udev_dict_set_cstr(udict, key, str);
3063a3826b3SAlex Hornung
307108c361cSMatthew Dillon errout:
308108c361cSMatthew Dillon udev_put_dict(dev, udict);
309108c361cSMatthew Dillon
310f5d8307cSAlex Hornung return error;
3113a3826b3SAlex Hornung }
3123a3826b3SAlex Hornung
3133a3826b3SAlex Hornung int
udev_dict_set_int(cdev_t dev,const char * key,int64_t val)3143a3826b3SAlex Hornung udev_dict_set_int(cdev_t dev, const char *key, int64_t val)
3153a3826b3SAlex Hornung {
3163a3826b3SAlex Hornung prop_dictionary_t dict;
317108c361cSMatthew Dillon prop_dictionary_t udict;
3183a3826b3SAlex Hornung int error;
3193a3826b3SAlex Hornung
3203a3826b3SAlex Hornung KKASSERT(dev != NULL);
3213a3826b3SAlex Hornung
322108c361cSMatthew Dillon while ((udict = udev_get_dict(dev)) == NULL) {
323f5d8307cSAlex Hornung error = udev_init_dict(dev);
324108c361cSMatthew Dillon udev_put_dict(dev, udict);
325f5d8307cSAlex Hornung if (error)
326f5d8307cSAlex Hornung return -1;
327f5d8307cSAlex Hornung }
328f5d8307cSAlex Hornung
3293a3826b3SAlex Hornung /* Queue a key update event */
3303a3826b3SAlex Hornung dict = udev_init_dict_event(dev, key);
331108c361cSMatthew Dillon if (dict == NULL) {
332108c361cSMatthew Dillon error = ENOMEM;
333108c361cSMatthew Dillon goto errout;
334108c361cSMatthew Dillon }
3353a3826b3SAlex Hornung if ((error = _udev_dict_set_int(dict, "value", val))) {
3363a3826b3SAlex Hornung prop_object_release(dict);
337108c361cSMatthew Dillon goto errout;
3383a3826b3SAlex Hornung }
3393a3826b3SAlex Hornung udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
3403a3826b3SAlex Hornung prop_object_release(dict);
341108c361cSMatthew Dillon error = _udev_dict_set_int(udict, key, val);
342108c361cSMatthew Dillon errout:
343108c361cSMatthew Dillon udev_put_dict(dev, udict);
3443a3826b3SAlex Hornung
345108c361cSMatthew Dillon return error;
3463a3826b3SAlex Hornung }
3473a3826b3SAlex Hornung
3483a3826b3SAlex Hornung int
udev_dict_set_uint(cdev_t dev,const char * key,uint64_t val)3493a3826b3SAlex Hornung udev_dict_set_uint(cdev_t dev, const char *key, uint64_t val)
3503a3826b3SAlex Hornung {
3513a3826b3SAlex Hornung prop_dictionary_t dict;
352108c361cSMatthew Dillon prop_dictionary_t udict;
3533a3826b3SAlex Hornung int error;
3543a3826b3SAlex Hornung
3553a3826b3SAlex Hornung KKASSERT(dev != NULL);
3563a3826b3SAlex Hornung
357108c361cSMatthew Dillon while ((udict = udev_get_dict(dev)) == NULL) {
358f5d8307cSAlex Hornung error = udev_init_dict(dev);
359108c361cSMatthew Dillon udev_put_dict(dev, udict);
360f5d8307cSAlex Hornung if (error)
361f5d8307cSAlex Hornung return -1;
362f5d8307cSAlex Hornung }
363f5d8307cSAlex Hornung
3643a3826b3SAlex Hornung /* Queue a key update event */
3653a3826b3SAlex Hornung dict = udev_init_dict_event(dev, key);
366108c361cSMatthew Dillon if (dict == NULL) {
367108c361cSMatthew Dillon error = ENOMEM;
368108c361cSMatthew Dillon goto errout;
369108c361cSMatthew Dillon }
3703a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "value", val))) {
3713a3826b3SAlex Hornung prop_object_release(dict);
372108c361cSMatthew Dillon goto errout;
3733a3826b3SAlex Hornung }
3743a3826b3SAlex Hornung udev_event_insert(UDEV_EV_KEY_UPDATE, dict);
3753a3826b3SAlex Hornung prop_object_release(dict);
376108c361cSMatthew Dillon error = _udev_dict_set_uint(udict, key, val);
377108c361cSMatthew Dillon errout:
378108c361cSMatthew Dillon udev_put_dict(dev, udict);
3793a3826b3SAlex Hornung
380108c361cSMatthew Dillon return error;
3813a3826b3SAlex Hornung }
3823a3826b3SAlex Hornung
3833a3826b3SAlex Hornung int
udev_dict_delete_key(cdev_t dev,const char * key)3843a3826b3SAlex Hornung udev_dict_delete_key(cdev_t dev, const char *key)
3853a3826b3SAlex Hornung {
3863a3826b3SAlex Hornung prop_dictionary_t dict;
387108c361cSMatthew Dillon prop_dictionary_t udict;
388108c361cSMatthew Dillon int error;
3893a3826b3SAlex Hornung
3903a3826b3SAlex Hornung KKASSERT(dev != NULL);
391108c361cSMatthew Dillon udict = udev_get_dict(dev);
392108c361cSMatthew Dillon if (udict == NULL) {
393108c361cSMatthew Dillon error = ENOMEM;
394108c361cSMatthew Dillon goto errout;
395108c361cSMatthew Dillon }
3963a3826b3SAlex Hornung
3973a3826b3SAlex Hornung /* Queue a key removal event */
3983a3826b3SAlex Hornung dict = udev_init_dict_event(dev, key);
399108c361cSMatthew Dillon if (dict == NULL) {
400108c361cSMatthew Dillon error = ENOMEM;
401108c361cSMatthew Dillon goto errout;
402108c361cSMatthew Dillon }
4033a3826b3SAlex Hornung udev_event_insert(UDEV_EV_KEY_REMOVE, dict);
4043a3826b3SAlex Hornung prop_object_release(dict);
405108c361cSMatthew Dillon error = _udev_dict_delete_key(udict, key);
406108c361cSMatthew Dillon errout:
407108c361cSMatthew Dillon udev_put_dict(dev, udict);
4083a3826b3SAlex Hornung
409108c361cSMatthew Dillon return error;
4103a3826b3SAlex Hornung }
4113a3826b3SAlex Hornung
412108c361cSMatthew Dillon /*
413108c361cSMatthew Dillon * device dictionary access already locked
414108c361cSMatthew Dillon */
4153a3826b3SAlex Hornung static int
udev_init_dict(cdev_t dev)4163a3826b3SAlex Hornung udev_init_dict(cdev_t dev)
4173a3826b3SAlex Hornung {
4183a3826b3SAlex Hornung prop_dictionary_t dict;
4193a3826b3SAlex Hornung uint64_t kptr;
4203a3826b3SAlex Hornung int error;
4213a3826b3SAlex Hornung
4223a3826b3SAlex Hornung kptr = (uint64_t)(uintptr_t)dev;
4233a3826b3SAlex Hornung
4243a3826b3SAlex Hornung KKASSERT(dev != NULL);
425f5d8307cSAlex Hornung
426f5d8307cSAlex Hornung if (dev->si_dict != NULL) {
427f5d8307cSAlex Hornung log(LOG_DEBUG,
428108c361cSMatthew Dillon "udev_init_dict: new dict for %s, but has "
429108c361cSMatthew Dillon "dict already (%p)!\n",
430f5d8307cSAlex Hornung dev->si_name, dev->si_dict);
431f5d8307cSAlex Hornung return 0;
432f5d8307cSAlex Hornung }
433f5d8307cSAlex Hornung
4343a3826b3SAlex Hornung dict = prop_dictionary_create();
4353a3826b3SAlex Hornung if (dict == NULL) {
436108c361cSMatthew Dillon log(LOG_DEBUG,
437108c361cSMatthew Dillon "udev_init_dict: prop_dictionary_create() failed\n");
4383a3826b3SAlex Hornung return ENOMEM;
4393a3826b3SAlex Hornung }
4403a3826b3SAlex Hornung
4413a3826b3SAlex Hornung if ((error = _udev_dict_set_cstr(dict, "name", dev->si_name)))
4423a3826b3SAlex Hornung goto error_out;
4433a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "devnum", dev->si_inode)))
4443a3826b3SAlex Hornung goto error_out;
4453a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "kptr", kptr)))
4463a3826b3SAlex Hornung goto error_out;
447108c361cSMatthew Dillon if ((error = _udev_dict_set_uint(dict, "devtype",
448108c361cSMatthew Dillon (dev_dflags(dev) & D_TYPEMASK)))) {
449751a320cSAlex Hornung goto error_out;
450108c361cSMatthew Dillon }
4513a3826b3SAlex Hornung
4523a3826b3SAlex Hornung /* XXX: The next 3 are marginallly useful, if at all */
4533a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "uid", dev->si_uid)))
4543a3826b3SAlex Hornung goto error_out;
4553a3826b3SAlex Hornung if ((error = _udev_dict_set_uint(dict, "gid", dev->si_gid)))
4563a3826b3SAlex Hornung goto error_out;
4573a3826b3SAlex Hornung if ((error = _udev_dict_set_int(dict, "mode", dev->si_perms)))
4583a3826b3SAlex Hornung goto error_out;
4593a3826b3SAlex Hornung
460619e8f47SSascha Wildner if ((error = _udev_dict_set_int(dict, "major", dev->si_umajor)))
4613a3826b3SAlex Hornung goto error_out;
4623a3826b3SAlex Hornung if ((error = _udev_dict_set_int(dict, "minor", dev->si_uminor)))
4633a3826b3SAlex Hornung goto error_out;
464f5d8307cSAlex Hornung if (dev->si_ops->head.name != NULL) {
465f5d8307cSAlex Hornung if ((error = _udev_dict_set_cstr(dict, "driver",
466f5d8307cSAlex Hornung __DECONST(char *, dev->si_ops->head.name))))
467f5d8307cSAlex Hornung goto error_out;
468f5d8307cSAlex Hornung }
4693a3826b3SAlex Hornung
4703a3826b3SAlex Hornung dev->si_dict = dict;
4713a3826b3SAlex Hornung return 0;
4723a3826b3SAlex Hornung
4733a3826b3SAlex Hornung error_out:
4743a3826b3SAlex Hornung dev->si_dict = NULL;
4753a3826b3SAlex Hornung prop_object_release(dict);
4763a3826b3SAlex Hornung return error;
4773a3826b3SAlex Hornung }
4783a3826b3SAlex Hornung
479108c361cSMatthew Dillon /*
480108c361cSMatthew Dillon * device dictionary access already locked
481108c361cSMatthew Dillon */
4823a3826b3SAlex Hornung static int
udev_destroy_dict(cdev_t dev)4833a3826b3SAlex Hornung udev_destroy_dict(cdev_t dev)
4843a3826b3SAlex Hornung {
485108c361cSMatthew Dillon prop_dictionary_t udict;
4863a3826b3SAlex Hornung
487108c361cSMatthew Dillon KKASSERT(dev != NULL);
488108c361cSMatthew Dillon udict = udev_get_dict(dev);
489108c361cSMatthew Dillon
490108c361cSMatthew Dillon /* field is now locked, use directly to handle races */
491108c361cSMatthew Dillon if (dev->si_dict) {
4923a3826b3SAlex Hornung prop_object_release(dev->si_dict);
4933a3826b3SAlex Hornung dev->si_dict = NULL;
4943a3826b3SAlex Hornung }
495108c361cSMatthew Dillon udev_put_dict(dev, udict);
4963a3826b3SAlex Hornung
4973a3826b3SAlex Hornung return 0;
4983a3826b3SAlex Hornung }
4993a3826b3SAlex Hornung
5003a3826b3SAlex Hornung static void
udev_event_insert(int ev_type,prop_dictionary_t dict)5013a3826b3SAlex Hornung udev_event_insert(int ev_type, prop_dictionary_t dict)
5023a3826b3SAlex Hornung {
5033a3826b3SAlex Hornung struct udev_event_kernel *ev;
5047ac0a0e3SMichael Neumann prop_dictionary_t dict_copy;
5053a3826b3SAlex Hornung
5063a3826b3SAlex Hornung /* Only start queing events after client has initiated properly */
50784f2d39fSMatthew Dillon if (udev_initiated_count) {
5087ac0a0e3SMichael Neumann dict_copy = prop_dictionary_copy(dict);
5097ac0a0e3SMichael Neumann if (dict_copy == NULL)
5103a3826b3SAlex Hornung return;
5117ac0a0e3SMichael Neumann ev = objcache_get(udev_event_kernel_cache, M_WAITOK);
5127ac0a0e3SMichael Neumann ev->ev.ev_dict = dict_copy;
5133a3826b3SAlex Hornung ev->ev.ev_type = ev_type;
5143a3826b3SAlex Hornung
5159b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
5169b881e96SMatthew Dillon TAILQ_INSERT_TAIL(&udev_evq, ev, link);
5179b881e96SMatthew Dillon ++udev_evqlen;
51884f2d39fSMatthew Dillon ++udev_seq;
51984f2d39fSMatthew Dillon if (udev_seqwait)
52084f2d39fSMatthew Dillon wakeup(&udev_seqwait);
5219b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
5229b881e96SMatthew Dillon wakeup(&udev_evq);
5239b881e96SMatthew Dillon KNOTE(&udev_kq.ki_note, 0);
52484f2d39fSMatthew Dillon } else if (udev_open_count) {
52584f2d39fSMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
52684f2d39fSMatthew Dillon ++udev_seq;
52784f2d39fSMatthew Dillon if (udev_seqwait)
52884f2d39fSMatthew Dillon wakeup(&udev_seqwait);
52984f2d39fSMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
53084f2d39fSMatthew Dillon KNOTE(&udev_kq.ki_note, 0);
53184f2d39fSMatthew Dillon }
5323a3826b3SAlex Hornung }
5333a3826b3SAlex Hornung
5343a3826b3SAlex Hornung static void
udev_clean_events_locked(void)5359b881e96SMatthew Dillon udev_clean_events_locked(void)
5363a3826b3SAlex Hornung {
5379b881e96SMatthew Dillon struct udev_event_kernel *ev;
5389b881e96SMatthew Dillon
5399b881e96SMatthew Dillon while ((ev = TAILQ_FIRST(&udev_evq)) &&
5409b881e96SMatthew Dillon ev->ev.ev_dict != NULL) {
5419b881e96SMatthew Dillon TAILQ_REMOVE(&udev_evq, ev, link);
5427ac0a0e3SMichael Neumann objcache_put(udev_event_kernel_cache, ev);
5439b881e96SMatthew Dillon --udev_evqlen;
5449b881e96SMatthew Dillon }
5453a3826b3SAlex Hornung }
5463a3826b3SAlex Hornung
5473a3826b3SAlex Hornung static char *
udev_event_externalize(struct udev_event_kernel * ev)5483a3826b3SAlex Hornung udev_event_externalize(struct udev_event_kernel *ev)
5493a3826b3SAlex Hornung {
5503a3826b3SAlex Hornung prop_dictionary_t dict;
5513a3826b3SAlex Hornung char *xml;
5523a3826b3SAlex Hornung int error;
5533a3826b3SAlex Hornung
5543a3826b3SAlex Hornung dict = prop_dictionary_create();
5553a3826b3SAlex Hornung if (dict == NULL) {
5569b881e96SMatthew Dillon log(LOG_DEBUG,
5579b881e96SMatthew Dillon "udev_event_externalize: prop_dictionary_create() failed\n");
5583a3826b3SAlex Hornung return NULL;
5593a3826b3SAlex Hornung }
5603a3826b3SAlex Hornung
5613a3826b3SAlex Hornung if ((error = _udev_dict_set_int(dict, "evtype", ev->ev.ev_type))) {
5623a3826b3SAlex Hornung prop_object_release(dict);
5633a3826b3SAlex Hornung return NULL;
5643a3826b3SAlex Hornung }
5653a3826b3SAlex Hornung
5663a3826b3SAlex Hornung if (prop_dictionary_set(dict, "evdict", ev->ev.ev_dict) == false) {
5673a3826b3SAlex Hornung prop_object_release(dict);
5683a3826b3SAlex Hornung return NULL;
5693a3826b3SAlex Hornung }
5703a3826b3SAlex Hornung
5713a3826b3SAlex Hornung prop_object_release(ev->ev.ev_dict);
5723a3826b3SAlex Hornung
5733a3826b3SAlex Hornung xml = prop_dictionary_externalize(dict);
5743a3826b3SAlex Hornung
5753a3826b3SAlex Hornung prop_object_release(dict);
5763a3826b3SAlex Hornung
5773a3826b3SAlex Hornung return xml;
5783a3826b3SAlex Hornung }
5793a3826b3SAlex Hornung
5803a3826b3SAlex Hornung int
udev_event_attach(cdev_t dev,char * name,int alias)5813a3826b3SAlex Hornung udev_event_attach(cdev_t dev, char *name, int alias)
5823a3826b3SAlex Hornung {
5833a3826b3SAlex Hornung prop_dictionary_t dict;
584108c361cSMatthew Dillon prop_dictionary_t udict;
5853a3826b3SAlex Hornung int error;
5863a3826b3SAlex Hornung
5873a3826b3SAlex Hornung KKASSERT(dev != NULL);
5883a3826b3SAlex Hornung
589f5d8307cSAlex Hornung error = ENOMEM;
5903a3826b3SAlex Hornung
591108c361cSMatthew Dillon udict = udev_get_dict(dev);
5923a3826b3SAlex Hornung if (alias) {
593108c361cSMatthew Dillon if (udict == NULL)
594108c361cSMatthew Dillon goto error_out;
595108c361cSMatthew Dillon dict = prop_dictionary_copy(udict);
5963a3826b3SAlex Hornung if (dict == NULL)
5973a3826b3SAlex Hornung goto error_out;
5983a3826b3SAlex Hornung
5993a3826b3SAlex Hornung if ((error = _udev_dict_set_cstr(dict, "name", name))) {
6003a3826b3SAlex Hornung prop_object_release(dict);
6013a3826b3SAlex Hornung goto error_out;
6023a3826b3SAlex Hornung }
6033a3826b3SAlex Hornung
6043a3826b3SAlex Hornung _udev_dict_set_int(dict, "alias", 1);
6053a3826b3SAlex Hornung
6063a3826b3SAlex Hornung udev_event_insert(UDEV_EVENT_ATTACH, dict);
6073a3826b3SAlex Hornung prop_object_release(dict);
6083a3826b3SAlex Hornung } else {
609108c361cSMatthew Dillon while (udict == NULL) {
610f5d8307cSAlex Hornung error = udev_init_dict(dev);
611f5d8307cSAlex Hornung if (error)
612f5d8307cSAlex Hornung goto error_out;
613108c361cSMatthew Dillon udev_put_dict(dev, udict);
614108c361cSMatthew Dillon udict = udev_get_dict(dev);
6153a3826b3SAlex Hornung }
616108c361cSMatthew Dillon _udev_dict_set_int(udict, "alias", 0);
617108c361cSMatthew Dillon udev_event_insert(UDEV_EVENT_ATTACH, udict);
618108c361cSMatthew Dillon }
6193a3826b3SAlex Hornung error_out:
620108c361cSMatthew Dillon udev_put_dict(dev, udict);
621108c361cSMatthew Dillon
6223a3826b3SAlex Hornung return error;
6233a3826b3SAlex Hornung }
6243a3826b3SAlex Hornung
6253a3826b3SAlex Hornung int
udev_event_detach(cdev_t dev,char * name,int alias)6263a3826b3SAlex Hornung udev_event_detach(cdev_t dev, char *name, int alias)
6273a3826b3SAlex Hornung {
6283a3826b3SAlex Hornung prop_dictionary_t dict;
629108c361cSMatthew Dillon prop_dictionary_t udict;
6303a3826b3SAlex Hornung
6313a3826b3SAlex Hornung KKASSERT(dev != NULL);
6323a3826b3SAlex Hornung
633108c361cSMatthew Dillon udict = udev_get_dict(dev);
6343a3826b3SAlex Hornung if (alias) {
635108c361cSMatthew Dillon dict = prop_dictionary_copy(udict);
6363a3826b3SAlex Hornung if (dict == NULL)
6373a3826b3SAlex Hornung goto error_out;
6383a3826b3SAlex Hornung
6393a3826b3SAlex Hornung if (_udev_dict_set_cstr(dict, "name", name)) {
6403a3826b3SAlex Hornung prop_object_release(dict);
6413a3826b3SAlex Hornung goto error_out;
6423a3826b3SAlex Hornung }
6433a3826b3SAlex Hornung
6443a3826b3SAlex Hornung _udev_dict_set_int(dict, "alias", 1);
6453a3826b3SAlex Hornung
6463a3826b3SAlex Hornung udev_event_insert(UDEV_EVENT_DETACH, dict);
6473a3826b3SAlex Hornung prop_object_release(dict);
6483a3826b3SAlex Hornung } else {
649108c361cSMatthew Dillon if (udict)
650108c361cSMatthew Dillon udev_event_insert(UDEV_EVENT_DETACH, udict);
6513a3826b3SAlex Hornung }
6523a3826b3SAlex Hornung
6533a3826b3SAlex Hornung error_out:
6543a3826b3SAlex Hornung udev_destroy_dict(dev);
655108c361cSMatthew Dillon udev_put_dict(dev, udict);
6563a3826b3SAlex Hornung
6573a3826b3SAlex Hornung return 0;
6583a3826b3SAlex Hornung }
6593a3826b3SAlex Hornung
6603a3826b3SAlex Hornung /*
6619b881e96SMatthew Dillon * Allow multiple opens. Each opener gets a different device.
6629b881e96SMatthew Dillon * Messages are replicated to all devices using a marker system.
6639b881e96SMatthew Dillon */
6649b881e96SMatthew Dillon static int
udev_dev_clone(struct dev_clone_args * ap)6659b881e96SMatthew Dillon udev_dev_clone(struct dev_clone_args *ap)
6669b881e96SMatthew Dillon {
6679b881e96SMatthew Dillon struct udev_softc *softc;
6689b881e96SMatthew Dillon int unit;
6699b881e96SMatthew Dillon
6709b881e96SMatthew Dillon unit = devfs_clone_bitmap_get(&DEVFS_CLONE_BITMAP(udev), 1000);
6719b881e96SMatthew Dillon if (unit < 0) {
6729b881e96SMatthew Dillon ap->a_dev = NULL;
6739b881e96SMatthew Dillon return 1;
6749b881e96SMatthew Dillon }
6759b881e96SMatthew Dillon
6769b881e96SMatthew Dillon softc = kmalloc(sizeof(*softc), M_UDEV, M_WAITOK | M_ZERO);
6779b881e96SMatthew Dillon softc->unit = unit;
6789b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
6799b881e96SMatthew Dillon TAILQ_INSERT_TAIL(&udevq, softc, entry);
6809b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
6819b881e96SMatthew Dillon
6829b881e96SMatthew Dillon softc->dev = make_only_dev(&udev_dev_ops, unit, ap->a_cred->cr_ruid,
6830049c59cSMatthew Dillon 0, 0600, "udevs/%d", unit);
6849b881e96SMatthew Dillon softc->dev->si_drv1 = softc;
6859b881e96SMatthew Dillon ap->a_dev = softc->dev;
6869b881e96SMatthew Dillon return 0;
6879b881e96SMatthew Dillon }
6889b881e96SMatthew Dillon
6899b881e96SMatthew Dillon /*
6903a3826b3SAlex Hornung * dev stuff
6913a3826b3SAlex Hornung */
6923a3826b3SAlex Hornung static int
udev_dev_open(struct dev_open_args * ap)6933a3826b3SAlex Hornung udev_dev_open(struct dev_open_args *ap)
6943a3826b3SAlex Hornung {
6959b881e96SMatthew Dillon struct udev_softc *softc = ap->a_head.a_dev->si_drv1;
6963a3826b3SAlex Hornung
6979b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
6989b881e96SMatthew Dillon if (softc == NULL || softc->opened) {
6999b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
7009b881e96SMatthew Dillon return EBUSY;
7019b881e96SMatthew Dillon }
7029b881e96SMatthew Dillon softc->opened = 1;
70384f2d39fSMatthew Dillon ++udev_open_count;
7049b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
7053a3826b3SAlex Hornung
7063a3826b3SAlex Hornung return 0;
7073a3826b3SAlex Hornung }
7083a3826b3SAlex Hornung
7093a3826b3SAlex Hornung static int
udev_dev_close(struct dev_close_args * ap)7103a3826b3SAlex Hornung udev_dev_close(struct dev_close_args *ap)
7113a3826b3SAlex Hornung {
7129b881e96SMatthew Dillon struct udev_softc *softc = ap->a_head.a_dev->si_drv1;
7139b881e96SMatthew Dillon
7149b881e96SMatthew Dillon KKASSERT(softc->dev == ap->a_head.a_dev);
7159b881e96SMatthew Dillon KKASSERT(softc->opened == 1);
7169b881e96SMatthew Dillon
7179b444d7bSMatthew Dillon destroy_dev(ap->a_head.a_dev);
7189b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
7199b881e96SMatthew Dillon TAILQ_REMOVE(&udevq, softc, entry);
7209b881e96SMatthew Dillon
7219b881e96SMatthew Dillon if (softc->initiated) {
7229b881e96SMatthew Dillon TAILQ_REMOVE(&udev_evq, &softc->marker, link);
7239b881e96SMatthew Dillon softc->initiated = 0;
7249b881e96SMatthew Dillon --udev_initiated_count;
7259b881e96SMatthew Dillon udev_clean_events_locked();
7269b881e96SMatthew Dillon }
7279b881e96SMatthew Dillon softc->opened = 0;
7289b881e96SMatthew Dillon softc->dev = NULL;
7299b881e96SMatthew Dillon ap->a_head.a_dev->si_drv1 = NULL;
73084f2d39fSMatthew Dillon --udev_open_count;
7319b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
7329b881e96SMatthew Dillon
7339b444d7bSMatthew Dillon /*
7349b444d7bSMatthew Dillon * WARNING! devfs_clone_bitmap_put() interacts with the devfs
7359b444d7bSMatthew Dillon * thread, avoid deadlocks by ensuring we are unlocked
7369b444d7bSMatthew Dillon * before calling.
7379b444d7bSMatthew Dillon */
7389b444d7bSMatthew Dillon devfs_clone_bitmap_put(&DEVFS_CLONE_BITMAP(udev), softc->unit);
7399b881e96SMatthew Dillon wakeup(&udev_evq);
7409b881e96SMatthew Dillon
7419b881e96SMatthew Dillon kfree(softc, M_UDEV);
7423a3826b3SAlex Hornung
7433a3826b3SAlex Hornung return 0;
7443a3826b3SAlex Hornung }
7453a3826b3SAlex Hornung
74638714400SSamuel J. Greear static struct filterops udev_dev_read_filtops =
747481d12aaSMatthew Dillon { FILTEROP_ISFD | FILTEROP_MPSAFE, NULL,
748481d12aaSMatthew Dillon udev_dev_filter_detach, udev_dev_filter_read };
74938714400SSamuel J. Greear
75038714400SSamuel J. Greear static int
udev_dev_kqfilter(struct dev_kqfilter_args * ap)75138714400SSamuel J. Greear udev_dev_kqfilter(struct dev_kqfilter_args *ap)
75238714400SSamuel J. Greear {
7539b881e96SMatthew Dillon struct udev_softc *softc = ap->a_head.a_dev->si_drv1;
75438714400SSamuel J. Greear struct knote *kn = ap->a_kn;
75538714400SSamuel J. Greear struct klist *klist;
75638714400SSamuel J. Greear
75738714400SSamuel J. Greear ap->a_result = 0;
7589b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
75938714400SSamuel J. Greear
76038714400SSamuel J. Greear switch (kn->kn_filter) {
76138714400SSamuel J. Greear case EVFILT_READ:
76238714400SSamuel J. Greear kn->kn_fop = &udev_dev_read_filtops;
7639b881e96SMatthew Dillon kn->kn_hook = (caddr_t)softc;
76438714400SSamuel J. Greear break;
76538714400SSamuel J. Greear default:
766b287d649SMatthew Dillon ap->a_result = EOPNOTSUPP;
7679b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
76838714400SSamuel J. Greear return (0);
76938714400SSamuel J. Greear }
77038714400SSamuel J. Greear
7719b881e96SMatthew Dillon klist = &udev_kq.ki_note;
7725b22f1a7SSamuel J. Greear knote_insert(klist, kn);
77338714400SSamuel J. Greear
7749b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
77538714400SSamuel J. Greear
77638714400SSamuel J. Greear return (0);
77738714400SSamuel J. Greear }
77838714400SSamuel J. Greear
77938714400SSamuel J. Greear static void
udev_dev_filter_detach(struct knote * kn)78038714400SSamuel J. Greear udev_dev_filter_detach(struct knote *kn)
78138714400SSamuel J. Greear {
78238714400SSamuel J. Greear struct klist *klist;
78338714400SSamuel J. Greear
7849b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
7859b881e96SMatthew Dillon klist = &udev_kq.ki_note;
7865b22f1a7SSamuel J. Greear knote_remove(klist, kn);
7879b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
78838714400SSamuel J. Greear }
78938714400SSamuel J. Greear
79038714400SSamuel J. Greear static int
udev_dev_filter_read(struct knote * kn,long hint)79138714400SSamuel J. Greear udev_dev_filter_read(struct knote *kn, long hint)
79238714400SSamuel J. Greear {
7939b881e96SMatthew Dillon struct udev_softc *softc = (void *)kn->kn_hook;
7949b881e96SMatthew Dillon struct udev_event_kernel *ev;
79538714400SSamuel J. Greear int ready = 0;
79638714400SSamuel J. Greear
7979b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
7989b881e96SMatthew Dillon if (softc->initiated) {
7999b881e96SMatthew Dillon ev = TAILQ_NEXT(&softc->marker, link);
8009b881e96SMatthew Dillon while (ev && ev->ev.ev_dict == NULL)
8019b881e96SMatthew Dillon ev = TAILQ_NEXT(ev, link);
8029b881e96SMatthew Dillon if (ev)
80338714400SSamuel J. Greear ready = 1;
8049b881e96SMatthew Dillon }
8059b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
80638714400SSamuel J. Greear
80738714400SSamuel J. Greear return (ready);
80838714400SSamuel J. Greear }
80938714400SSamuel J. Greear
8103a3826b3SAlex Hornung static int
udev_dev_read(struct dev_read_args * ap)8113a3826b3SAlex Hornung udev_dev_read(struct dev_read_args *ap)
8123a3826b3SAlex Hornung {
8139b881e96SMatthew Dillon struct udev_softc *softc = ap->a_head.a_dev->si_drv1;
8143a3826b3SAlex Hornung struct udev_event_kernel *ev;
8153a3826b3SAlex Hornung struct uio *uio = ap->a_uio;
8163a3826b3SAlex Hornung char *xml;
8173a3826b3SAlex Hornung size_t len;
8183a3826b3SAlex Hornung int error;
8193a3826b3SAlex Hornung
8209b881e96SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
8213a3826b3SAlex Hornung
822cd6f4f79SMatthew Dillon /*
823cd6f4f79SMatthew Dillon * Automatically enable message collection if it has not already
824cd6f4f79SMatthew Dillon * been enabled.
825cd6f4f79SMatthew Dillon */
826cd6f4f79SMatthew Dillon if (softc->initiated == 0) {
827cd6f4f79SMatthew Dillon softc->initiated = 1;
828cd6f4f79SMatthew Dillon ++udev_initiated_count;
829cd6f4f79SMatthew Dillon TAILQ_INSERT_HEAD(&udev_evq, &softc->marker, link);
830cd6f4f79SMatthew Dillon }
831cd6f4f79SMatthew Dillon
832cd6f4f79SMatthew Dillon /*
833cd6f4f79SMatthew Dillon * Loop, sleep interruptably until we get an event or signal.
834cd6f4f79SMatthew Dillon */
8359b881e96SMatthew Dillon error = 0;
8363a3826b3SAlex Hornung for (;;) {
8379b881e96SMatthew Dillon if (softc->initiated) {
8389b881e96SMatthew Dillon ev = TAILQ_NEXT(&softc->marker, link);
8399b881e96SMatthew Dillon while (ev && ev->ev.ev_dict == NULL)
8409b881e96SMatthew Dillon ev = TAILQ_NEXT(ev, link);
8419b881e96SMatthew Dillon if (ev) {
8423a3826b3SAlex Hornung if ((xml = udev_event_externalize(ev)) == NULL) {
8433a3826b3SAlex Hornung error = ENOMEM;
8449b881e96SMatthew Dillon break;
8459b881e96SMatthew Dillon }
8469b881e96SMatthew Dillon len = strlen(xml) + 1; /* include terminator */
8479b881e96SMatthew Dillon if (uio->uio_resid < len)
8489b881e96SMatthew Dillon error = ENOMEM;
8499b881e96SMatthew Dillon else
8503a3826b3SAlex Hornung error = uiomove((caddr_t)xml, len, uio);
8513a3826b3SAlex Hornung kfree(xml, M_TEMP);
8529b881e96SMatthew Dillon
8539b881e96SMatthew Dillon /*
8549b881e96SMatthew Dillon * Move the marker
8559b881e96SMatthew Dillon */
8569b881e96SMatthew Dillon TAILQ_REMOVE(&udev_evq, &softc->marker, link);
8579b881e96SMatthew Dillon TAILQ_INSERT_AFTER(&udev_evq,
8589b881e96SMatthew Dillon ev, &softc->marker, link);
8599b881e96SMatthew Dillon udev_clean_events_locked();
8609b881e96SMatthew Dillon break;
8619b881e96SMatthew Dillon }
8629b881e96SMatthew Dillon }
86384f2d39fSMatthew Dillon if (ap->a_ioflag & IO_NDELAY) {
86484f2d39fSMatthew Dillon error = EWOULDBLOCK;
86584f2d39fSMatthew Dillon break;
86684f2d39fSMatthew Dillon }
8679b881e96SMatthew Dillon if ((error = lksleep(&udev_evq, &udev_lk, PCATCH, "udevq", 0)))
8689b881e96SMatthew Dillon break;
8699b881e96SMatthew Dillon }
8709b881e96SMatthew Dillon
8719b881e96SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
8723a3826b3SAlex Hornung return error;
8733a3826b3SAlex Hornung }
8743a3826b3SAlex Hornung
8753a3826b3SAlex Hornung static int
udev_dev_ioctl(struct dev_ioctl_args * ap)8763a3826b3SAlex Hornung udev_dev_ioctl(struct dev_ioctl_args *ap)
8773a3826b3SAlex Hornung {
8789b881e96SMatthew Dillon struct udev_softc *softc = ap->a_head.a_dev->si_drv1;
8793a3826b3SAlex Hornung prop_dictionary_t dict;
8803a3826b3SAlex Hornung prop_object_t po;
8813a3826b3SAlex Hornung prop_string_t ps;
8823a3826b3SAlex Hornung struct plistref *pref;
8833a3826b3SAlex Hornung int i, error;
88484f2d39fSMatthew Dillon int seq;
8853a3826b3SAlex Hornung
8863a3826b3SAlex Hornung error = 0;
8873a3826b3SAlex Hornung
8883a3826b3SAlex Hornung switch(ap->a_cmd) {
8893a3826b3SAlex Hornung case UDEVPROP:
8903a3826b3SAlex Hornung /* Use proplib(3) for userspace/kernel communication */
8913a3826b3SAlex Hornung pref = (struct plistref *)ap->a_data;
8923a3826b3SAlex Hornung error = prop_dictionary_copyin_ioctl(pref, ap->a_cmd, &dict);
8933a3826b3SAlex Hornung if (error)
8943a3826b3SAlex Hornung return error;
8953a3826b3SAlex Hornung
8963a3826b3SAlex Hornung po = prop_dictionary_get(dict, "command");
8973a3826b3SAlex Hornung if (po == NULL || prop_object_type(po) != PROP_TYPE_STRING) {
8983a3826b3SAlex Hornung log(LOG_DEBUG, "udev: prop_dictionary_get() failed\n");
8993a3826b3SAlex Hornung prop_object_release(dict);
9003a3826b3SAlex Hornung return EINVAL;
9013a3826b3SAlex Hornung }
9023a3826b3SAlex Hornung
9033a3826b3SAlex Hornung ps = po;
9043a3826b3SAlex Hornung /* Handle cmd */
9053a3826b3SAlex Hornung for(i = 0; cmd_fn[i].cmd != NULL; i++) {
9063a3826b3SAlex Hornung if (prop_string_equals_cstring(ps, cmd_fn[i].cmd))
9073a3826b3SAlex Hornung break;
9083a3826b3SAlex Hornung }
9093a3826b3SAlex Hornung
9103a3826b3SAlex Hornung if (cmd_fn[i].cmd != NULL) {
9119b881e96SMatthew Dillon error = cmd_fn[i].fn(softc, pref, ap->a_cmd, dict);
9123a3826b3SAlex Hornung } else {
9133a3826b3SAlex Hornung error = EINVAL;
9143a3826b3SAlex Hornung }
9153a3826b3SAlex Hornung
9163a3826b3SAlex Hornung //prop_object_release(po);
9173a3826b3SAlex Hornung prop_object_release(dict);
9183a3826b3SAlex Hornung break;
91984f2d39fSMatthew Dillon case UDEVWAIT:
92084f2d39fSMatthew Dillon /*
92184f2d39fSMatthew Dillon * Wait for events based on sequence number. Updates
92284f2d39fSMatthew Dillon * sequence number for loop.
92384f2d39fSMatthew Dillon */
92484f2d39fSMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
92584f2d39fSMatthew Dillon seq = *(int *)ap->a_data;
92684f2d39fSMatthew Dillon ++udev_seqwait;
92784f2d39fSMatthew Dillon while (seq == udev_seq) {
92884f2d39fSMatthew Dillon error = lksleep(&udev_seqwait, &udev_lk,
92984f2d39fSMatthew Dillon PCATCH, "udevw", 0);
93084f2d39fSMatthew Dillon if (error)
93184f2d39fSMatthew Dillon break;
93284f2d39fSMatthew Dillon }
93384f2d39fSMatthew Dillon --udev_seqwait;
93484f2d39fSMatthew Dillon *(int *)ap->a_data = udev_seq;
93584f2d39fSMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
93684f2d39fSMatthew Dillon break;
9373a3826b3SAlex Hornung default:
9383a3826b3SAlex Hornung error = ENOTTY; /* Inappropriate ioctl for device */
9393a3826b3SAlex Hornung break;
9403a3826b3SAlex Hornung }
9413a3826b3SAlex Hornung
9423a3826b3SAlex Hornung return(error);
9433a3826b3SAlex Hornung }
9443a3826b3SAlex Hornung
9453a3826b3SAlex Hornung static void
udev_getdevs_scan_callback(char * name,cdev_t cdev,bool is_alias,void * arg)946b28e21efSSascha Wildner udev_getdevs_scan_callback(char *name, cdev_t cdev, bool is_alias, void *arg)
9473a3826b3SAlex Hornung {
9483a3826b3SAlex Hornung struct udev_prop_ctx *ctx = arg;
9493a3826b3SAlex Hornung
9503a3826b3SAlex Hornung KKASSERT(arg != NULL);
9513a3826b3SAlex Hornung
9523a3826b3SAlex Hornung if (cdev->si_dict == NULL)
9533a3826b3SAlex Hornung return;
9543a3826b3SAlex Hornung
9553a3826b3SAlex Hornung if (prop_array_add(ctx->cdevs, cdev->si_dict) == false) {
9563a3826b3SAlex Hornung ctx->error = EINVAL;
9573a3826b3SAlex Hornung return;
9583a3826b3SAlex Hornung }
9593a3826b3SAlex Hornung }
9603a3826b3SAlex Hornung
9613a3826b3SAlex Hornung static int
udev_getdevs_ioctl(struct udev_softc * softc,struct plistref * pref,u_long cmd,prop_dictionary_t dict)9629b881e96SMatthew Dillon udev_getdevs_ioctl(struct udev_softc *softc, struct plistref *pref,
9639b881e96SMatthew Dillon u_long cmd, prop_dictionary_t dict)
9643a3826b3SAlex Hornung {
9653a3826b3SAlex Hornung prop_dictionary_t odict;
9663a3826b3SAlex Hornung struct udev_prop_ctx ctx;
9673a3826b3SAlex Hornung int error;
9683a3826b3SAlex Hornung
969cd6f4f79SMatthew Dillon /*
970cd6f4f79SMatthew Dillon * Ensure event notification is enabled before doing the devfs
971cd6f4f79SMatthew Dillon * scan so nothing gets missed.
972cd6f4f79SMatthew Dillon */
973cd6f4f79SMatthew Dillon lockmgr(&udev_lk, LK_EXCLUSIVE);
974cd6f4f79SMatthew Dillon if (softc->initiated == 0) {
975cd6f4f79SMatthew Dillon softc->initiated = 1;
976cd6f4f79SMatthew Dillon ++udev_initiated_count;
977cd6f4f79SMatthew Dillon TAILQ_INSERT_HEAD(&udev_evq, &softc->marker, link);
978cd6f4f79SMatthew Dillon }
979cd6f4f79SMatthew Dillon lockmgr(&udev_lk, LK_RELEASE);
980cd6f4f79SMatthew Dillon
981cd6f4f79SMatthew Dillon /*
982cd6f4f79SMatthew Dillon * Devfs scan to build full dictionary.
983cd6f4f79SMatthew Dillon */
9843a3826b3SAlex Hornung ctx.error = 0;
9853a3826b3SAlex Hornung ctx.cdevs = prop_array_create();
9863a3826b3SAlex Hornung if (ctx.cdevs == NULL) {
9879b881e96SMatthew Dillon log(LOG_DEBUG,
9889b881e96SMatthew Dillon "udev_getdevs_ioctl: prop_array_create() failed\n");
9893a3826b3SAlex Hornung return EINVAL;
9903a3826b3SAlex Hornung }
9913a3826b3SAlex Hornung
9923a3826b3SAlex Hornung devfs_scan_callback(udev_getdevs_scan_callback, &ctx);
9933a3826b3SAlex Hornung
9943a3826b3SAlex Hornung if (ctx.error != 0) {
9953a3826b3SAlex Hornung prop_object_release(ctx.cdevs);
9963a3826b3SAlex Hornung return (ctx.error);
9973a3826b3SAlex Hornung }
9983a3826b3SAlex Hornung
9993a3826b3SAlex Hornung odict = prop_dictionary_create();
10003a3826b3SAlex Hornung if (odict == NULL) {
10013a3826b3SAlex Hornung return ENOMEM;
10023a3826b3SAlex Hornung }
10033a3826b3SAlex Hornung
10043a3826b3SAlex Hornung if ((prop_dictionary_set(odict, "array", ctx.cdevs)) == 0) {
10059b881e96SMatthew Dillon log(LOG_DEBUG,
10069b881e96SMatthew Dillon "udev_getdevs_ioctl: prop_dictionary_set failed\n");
10073a3826b3SAlex Hornung prop_object_release(odict);
10083a3826b3SAlex Hornung return ENOMEM;
10093a3826b3SAlex Hornung }
10103a3826b3SAlex Hornung
10113a3826b3SAlex Hornung error = prop_dictionary_copyout_ioctl(pref, cmd, odict);
10123a3826b3SAlex Hornung
10133a3826b3SAlex Hornung prop_object_release(odict);
10143a3826b3SAlex Hornung return error;
10153a3826b3SAlex Hornung }
10163a3826b3SAlex Hornung
10173a3826b3SAlex Hornung
10183a3826b3SAlex Hornung /*
10193a3826b3SAlex Hornung * SYSINIT stuff
10203a3826b3SAlex Hornung */
10213a3826b3SAlex Hornung static void
udev_init(void)10223a3826b3SAlex Hornung udev_init(void)
10233a3826b3SAlex Hornung {
10249b881e96SMatthew Dillon lockinit(&udev_lk, "udevlk", 0, LK_CANRECURSE);
10259b881e96SMatthew Dillon TAILQ_INIT(&udevq);
10269b881e96SMatthew Dillon TAILQ_INIT(&udev_evq);
10277ac0a0e3SMichael Neumann udev_event_kernel_cache = objcache_create_simple(M_UDEV, sizeof(struct udev_event_kernel));
10283a3826b3SAlex Hornung }
10293a3826b3SAlex Hornung
10303a3826b3SAlex Hornung static void
udev_uninit(void)10313a3826b3SAlex Hornung udev_uninit(void)
10323a3826b3SAlex Hornung {
10337ac0a0e3SMichael Neumann objcache_destroy(udev_event_kernel_cache);
10343a3826b3SAlex Hornung }
10353a3826b3SAlex Hornung
10363a3826b3SAlex Hornung static void
udev_dev_init(void)10373a3826b3SAlex Hornung udev_dev_init(void)
10383a3826b3SAlex Hornung {
10399b881e96SMatthew Dillon udev_dev = make_autoclone_dev(&udev_dev_ops, &DEVFS_CLONE_BITMAP(udev),
10409b881e96SMatthew Dillon udev_dev_clone,
10419b881e96SMatthew Dillon UID_ROOT, GID_WHEEL, 0600, "udev");
10423a3826b3SAlex Hornung }
10433a3826b3SAlex Hornung
10443a3826b3SAlex Hornung static void
udev_dev_uninit(void)10453a3826b3SAlex Hornung udev_dev_uninit(void)
10463a3826b3SAlex Hornung {
10473a3826b3SAlex Hornung destroy_dev(udev_dev);
10483a3826b3SAlex Hornung }
10493a3826b3SAlex Hornung
10509b881e96SMatthew Dillon SYSINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY,
10519b881e96SMatthew Dillon udev_init, NULL);
10529b881e96SMatthew Dillon SYSUNINIT(subr_udev_register, SI_SUB_CREATE_INIT, SI_ORDER_ANY,
10539b881e96SMatthew Dillon udev_uninit, NULL);
10549b881e96SMatthew Dillon SYSINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY,
10559b881e96SMatthew Dillon udev_dev_init, NULL);
10569b881e96SMatthew Dillon SYSUNINIT(subr_udev_dev_register, SI_SUB_DRIVERS, SI_ORDER_ANY,
10579b881e96SMatthew Dillon udev_dev_uninit, NULL);
1058