xref: /freebsd/sys/kern/kern_devctl.c (revision ef9ffb8594eee294334ced627755bf5b46b48f9f)
1102e6817SAlexander V. Chernikov /*-
2102e6817SAlexander V. Chernikov  * SPDX-License-Identifier: BSD-2-Clause
3102e6817SAlexander V. Chernikov  *
4102e6817SAlexander V. Chernikov  * Copyright (c) 2002-2020 M. Warner Losh <imp@FreeBSD.org>
5102e6817SAlexander V. Chernikov  * All rights reserved.
6102e6817SAlexander V. Chernikov  *
7102e6817SAlexander V. Chernikov  * Redistribution and use in source and binary forms, with or without
8102e6817SAlexander V. Chernikov  * modification, are permitted provided that the following conditions
9102e6817SAlexander V. Chernikov  * are met:
10102e6817SAlexander V. Chernikov  * 1. Redistributions of source code must retain the above copyright
11102e6817SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer.
12102e6817SAlexander V. Chernikov  * 2. Redistributions in binary form must reproduce the above copyright
13102e6817SAlexander V. Chernikov  *    notice, this list of conditions and the following disclaimer in the
14102e6817SAlexander V. Chernikov  *    documentation and/or other materials provided with the distribution.
15102e6817SAlexander V. Chernikov  *
16102e6817SAlexander V. Chernikov  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
17102e6817SAlexander V. Chernikov  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18102e6817SAlexander V. Chernikov  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19102e6817SAlexander V. Chernikov  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
20102e6817SAlexander V. Chernikov  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21102e6817SAlexander V. Chernikov  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22102e6817SAlexander V. Chernikov  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23102e6817SAlexander V. Chernikov  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24102e6817SAlexander V. Chernikov  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25102e6817SAlexander V. Chernikov  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26102e6817SAlexander V. Chernikov  * SUCH DAMAGE.
27102e6817SAlexander V. Chernikov  */
28102e6817SAlexander V. Chernikov #include <sys/cdefs.h>
29102e6817SAlexander V. Chernikov #include "opt_bus.h"
30102e6817SAlexander V. Chernikov #include "opt_ddb.h"
31102e6817SAlexander V. Chernikov 
32102e6817SAlexander V. Chernikov #include <sys/param.h>
33102e6817SAlexander V. Chernikov #include <sys/conf.h>
34102e6817SAlexander V. Chernikov #include <sys/eventhandler.h>
35102e6817SAlexander V. Chernikov #include <sys/filio.h>
36102e6817SAlexander V. Chernikov #include <sys/lock.h>
37102e6817SAlexander V. Chernikov #include <sys/kernel.h>
38102e6817SAlexander V. Chernikov #include <sys/malloc.h>
39102e6817SAlexander V. Chernikov #include <sys/mutex.h>
40102e6817SAlexander V. Chernikov #include <sys/poll.h>
41102e6817SAlexander V. Chernikov #include <sys/priv.h>
42102e6817SAlexander V. Chernikov #include <sys/proc.h>
43102e6817SAlexander V. Chernikov #include <sys/condvar.h>
44102e6817SAlexander V. Chernikov #include <sys/queue.h>
45102e6817SAlexander V. Chernikov #include <machine/bus.h>
46102e6817SAlexander V. Chernikov #include <sys/sbuf.h>
47102e6817SAlexander V. Chernikov #include <sys/selinfo.h>
48102e6817SAlexander V. Chernikov #include <sys/smp.h>
49102e6817SAlexander V. Chernikov #include <sys/sysctl.h>
50102e6817SAlexander V. Chernikov #include <sys/systm.h>
51102e6817SAlexander V. Chernikov #include <sys/uio.h>
52102e6817SAlexander V. Chernikov #include <sys/bus.h>
53102e6817SAlexander V. Chernikov 
54102e6817SAlexander V. Chernikov #include <machine/cpu.h>
55102e6817SAlexander V. Chernikov #include <machine/stdarg.h>
56102e6817SAlexander V. Chernikov 
57102e6817SAlexander V. Chernikov #include <vm/uma.h>
58102e6817SAlexander V. Chernikov #include <vm/vm.h>
59102e6817SAlexander V. Chernikov 
60102e6817SAlexander V. Chernikov #include <ddb/ddb.h>
61102e6817SAlexander V. Chernikov 
62102e6817SAlexander V. Chernikov STAILQ_HEAD(devq, dev_event_info);
63102e6817SAlexander V. Chernikov 
64102e6817SAlexander V. Chernikov static struct dev_softc {
65102e6817SAlexander V. Chernikov 	int		inuse;
66102e6817SAlexander V. Chernikov 	int		nonblock;
67102e6817SAlexander V. Chernikov 	int		queued;
68102e6817SAlexander V. Chernikov 	int		async;
69102e6817SAlexander V. Chernikov 	struct mtx	mtx;
70102e6817SAlexander V. Chernikov 	struct cv	cv;
71102e6817SAlexander V. Chernikov 	struct selinfo	sel;
72102e6817SAlexander V. Chernikov 	struct devq	devq;
73102e6817SAlexander V. Chernikov 	struct sigio	*sigio;
74102e6817SAlexander V. Chernikov 	uma_zone_t	zone;
75102e6817SAlexander V. Chernikov } devsoftc;
76102e6817SAlexander V. Chernikov 
77102e6817SAlexander V. Chernikov /*
78102e6817SAlexander V. Chernikov  * This design allows only one reader for /dev/devctl.  This is not desirable
79102e6817SAlexander V. Chernikov  * in the long run, but will get a lot of hair out of this implementation.
80102e6817SAlexander V. Chernikov  * Maybe we should make this device a clonable device.
81102e6817SAlexander V. Chernikov  *
82102e6817SAlexander V. Chernikov  * Also note: we specifically do not attach a device to the device_t tree
83102e6817SAlexander V. Chernikov  * to avoid potential chicken and egg problems.  One could argue that all
84102e6817SAlexander V. Chernikov  * of this belongs to the root node.
85102e6817SAlexander V. Chernikov  */
86102e6817SAlexander V. Chernikov 
87102e6817SAlexander V. Chernikov #define DEVCTL_DEFAULT_QUEUE_LEN 1000
88102e6817SAlexander V. Chernikov static int sysctl_devctl_queue(SYSCTL_HANDLER_ARGS);
89102e6817SAlexander V. Chernikov static int devctl_queue_length = DEVCTL_DEFAULT_QUEUE_LEN;
90102e6817SAlexander V. Chernikov SYSCTL_PROC(_hw_bus, OID_AUTO, devctl_queue, CTLTYPE_INT | CTLFLAG_RWTUN |
91102e6817SAlexander V. Chernikov     CTLFLAG_MPSAFE, NULL, 0, sysctl_devctl_queue, "I", "devctl queue length");
9297aedd33SWarner Losh static bool nomatch_enabled = true;
936437872cSIsaac Cilia Attard SYSCTL_BOOL(_hw_bus, OID_AUTO, devctl_nomatch_enabled, CTLFLAG_RWTUN,
946437872cSIsaac Cilia Attard     &nomatch_enabled, 0, "enable nomatch events");
95102e6817SAlexander V. Chernikov 
96102e6817SAlexander V. Chernikov static void devctl_attach_handler(void *arg __unused, device_t dev);
97102e6817SAlexander V. Chernikov static void devctl_detach_handler(void *arg __unused, device_t dev,
98102e6817SAlexander V. Chernikov     enum evhdev_detach state);
99102e6817SAlexander V. Chernikov static void devctl_nomatch_handler(void *arg __unused, device_t dev);
100102e6817SAlexander V. Chernikov 
101102e6817SAlexander V. Chernikov static d_open_t		devopen;
102102e6817SAlexander V. Chernikov static d_close_t	devclose;
103102e6817SAlexander V. Chernikov static d_read_t		devread;
104102e6817SAlexander V. Chernikov static d_ioctl_t	devioctl;
105102e6817SAlexander V. Chernikov static d_poll_t		devpoll;
106102e6817SAlexander V. Chernikov static d_kqfilter_t	devkqfilter;
107102e6817SAlexander V. Chernikov 
108102e6817SAlexander V. Chernikov #define DEVCTL_BUFFER (1024 - sizeof(void *))
109102e6817SAlexander V. Chernikov struct dev_event_info {
110102e6817SAlexander V. Chernikov 	STAILQ_ENTRY(dev_event_info) dei_link;
111102e6817SAlexander V. Chernikov 	char dei_data[DEVCTL_BUFFER];
112102e6817SAlexander V. Chernikov };
113102e6817SAlexander V. Chernikov 
114102e6817SAlexander V. Chernikov 
115102e6817SAlexander V. Chernikov static struct cdevsw dev_cdevsw = {
116102e6817SAlexander V. Chernikov 	.d_version =	D_VERSION,
117102e6817SAlexander V. Chernikov 	.d_open =	devopen,
118102e6817SAlexander V. Chernikov 	.d_close =	devclose,
119102e6817SAlexander V. Chernikov 	.d_read =	devread,
120102e6817SAlexander V. Chernikov 	.d_ioctl =	devioctl,
121102e6817SAlexander V. Chernikov 	.d_poll =	devpoll,
122102e6817SAlexander V. Chernikov 	.d_kqfilter =	devkqfilter,
123102e6817SAlexander V. Chernikov 	.d_name =	"devctl",
124102e6817SAlexander V. Chernikov };
125102e6817SAlexander V. Chernikov 
126102e6817SAlexander V. Chernikov static void	filt_devctl_detach(struct knote *kn);
127102e6817SAlexander V. Chernikov static int	filt_devctl_read(struct knote *kn, long hint);
128102e6817SAlexander V. Chernikov 
129*ef9ffb85SMark Johnston static const struct filterops devctl_rfiltops = {
130102e6817SAlexander V. Chernikov 	.f_isfd = 1,
131102e6817SAlexander V. Chernikov 	.f_detach = filt_devctl_detach,
132102e6817SAlexander V. Chernikov 	.f_event = filt_devctl_read,
133102e6817SAlexander V. Chernikov };
134102e6817SAlexander V. Chernikov 
135102e6817SAlexander V. Chernikov static struct cdev *devctl_dev;
136102e6817SAlexander V. Chernikov static void devaddq(const char *type, const char *what, device_t dev);
137102e6817SAlexander V. Chernikov 
138afbb26b5SBaptiste Daroussin static struct devctlbridge {
139afbb26b5SBaptiste Daroussin 	send_event_f *send_f;
140afbb26b5SBaptiste Daroussin } devctl_notify_hook = { .send_f = NULL };
141afbb26b5SBaptiste Daroussin 
142102e6817SAlexander V. Chernikov static void
devctl_init(void)143102e6817SAlexander V. Chernikov devctl_init(void)
144102e6817SAlexander V. Chernikov {
145102e6817SAlexander V. Chernikov 	int reserve;
146102e6817SAlexander V. Chernikov 	uma_zone_t z;
147102e6817SAlexander V. Chernikov 
148102e6817SAlexander V. Chernikov 	devctl_dev = make_dev_credf(MAKEDEV_ETERNAL, &dev_cdevsw, 0, NULL,
149102e6817SAlexander V. Chernikov 	    UID_ROOT, GID_WHEEL, 0600, "devctl");
150102e6817SAlexander V. Chernikov 	mtx_init(&devsoftc.mtx, "dev mtx", "devd", MTX_DEF);
151102e6817SAlexander V. Chernikov 	cv_init(&devsoftc.cv, "dev cv");
152102e6817SAlexander V. Chernikov 	STAILQ_INIT(&devsoftc.devq);
153102e6817SAlexander V. Chernikov 	knlist_init_mtx(&devsoftc.sel.si_note, &devsoftc.mtx);
154102e6817SAlexander V. Chernikov 	if (devctl_queue_length > 0) {
155102e6817SAlexander V. Chernikov 		/*
156102e6817SAlexander V. Chernikov 		 * Allocate a zone for the messages. Preallocate 2% of these for
157102e6817SAlexander V. Chernikov 		 * a reserve. Allow only devctl_queue_length slabs to cap memory
158102e6817SAlexander V. Chernikov 		 * usage.  The reserve usually allows coverage of surges of
159102e6817SAlexander V. Chernikov 		 * events during memory shortages. Normally we won't have to
160102e6817SAlexander V. Chernikov 		 * re-use events from the queue, but will in extreme shortages.
161102e6817SAlexander V. Chernikov 		 */
162102e6817SAlexander V. Chernikov 		z = devsoftc.zone = uma_zcreate("DEVCTL",
163102e6817SAlexander V. Chernikov 		    sizeof(struct dev_event_info), NULL, NULL, NULL, NULL,
164102e6817SAlexander V. Chernikov 		    UMA_ALIGN_PTR, 0);
165102e6817SAlexander V. Chernikov 		reserve = max(devctl_queue_length / 50, 100);	/* 2% reserve */
166102e6817SAlexander V. Chernikov 		uma_zone_set_max(z, devctl_queue_length);
167102e6817SAlexander V. Chernikov 		uma_zone_set_maxcache(z, 0);
168102e6817SAlexander V. Chernikov 		uma_zone_reserve(z, reserve);
169102e6817SAlexander V. Chernikov 		uma_prealloc(z, reserve);
170102e6817SAlexander V. Chernikov 	}
171102e6817SAlexander V. Chernikov 	EVENTHANDLER_REGISTER(device_attach, devctl_attach_handler,
172102e6817SAlexander V. Chernikov 	    NULL, EVENTHANDLER_PRI_LAST);
173102e6817SAlexander V. Chernikov 	EVENTHANDLER_REGISTER(device_detach, devctl_detach_handler,
174102e6817SAlexander V. Chernikov 	    NULL, EVENTHANDLER_PRI_LAST);
175102e6817SAlexander V. Chernikov 	EVENTHANDLER_REGISTER(device_nomatch, devctl_nomatch_handler,
176102e6817SAlexander V. Chernikov 	    NULL, EVENTHANDLER_PRI_LAST);
177102e6817SAlexander V. Chernikov }
178102e6817SAlexander V. Chernikov SYSINIT(devctl_init, SI_SUB_DRIVERS, SI_ORDER_SECOND, devctl_init, NULL);
179102e6817SAlexander V. Chernikov 
180102e6817SAlexander V. Chernikov /*
181102e6817SAlexander V. Chernikov  * A device was added to the tree.  We are called just after it successfully
182102e6817SAlexander V. Chernikov  * attaches (that is, probe and attach success for this device).  No call
183102e6817SAlexander V. Chernikov  * is made if a device is merely parented into the tree.  See devnomatch
184102e6817SAlexander V. Chernikov  * if probe fails.  If attach fails, no notification is sent (but maybe
185102e6817SAlexander V. Chernikov  * we should have a different message for this).
186102e6817SAlexander V. Chernikov  */
187102e6817SAlexander V. Chernikov static void
devctl_attach_handler(void * arg __unused,device_t dev)188102e6817SAlexander V. Chernikov devctl_attach_handler(void *arg __unused, device_t dev)
189102e6817SAlexander V. Chernikov {
190102e6817SAlexander V. Chernikov 	devaddq("+", device_get_nameunit(dev), dev);
191102e6817SAlexander V. Chernikov }
192102e6817SAlexander V. Chernikov 
193102e6817SAlexander V. Chernikov /*
194102e6817SAlexander V. Chernikov  * A device was removed from the tree.  We are called just before this
195102e6817SAlexander V. Chernikov  * happens.
196102e6817SAlexander V. Chernikov  */
197102e6817SAlexander V. Chernikov static void
devctl_detach_handler(void * arg __unused,device_t dev,enum evhdev_detach state)198102e6817SAlexander V. Chernikov devctl_detach_handler(void *arg __unused, device_t dev, enum evhdev_detach state)
199102e6817SAlexander V. Chernikov {
200102e6817SAlexander V. Chernikov 	if (state == EVHDEV_DETACH_COMPLETE)
201102e6817SAlexander V. Chernikov 		devaddq("-", device_get_nameunit(dev), dev);
202102e6817SAlexander V. Chernikov }
203102e6817SAlexander V. Chernikov 
204102e6817SAlexander V. Chernikov /*
205102e6817SAlexander V. Chernikov  * Called when there's no match for this device.  This is only called
206102e6817SAlexander V. Chernikov  * the first time that no match happens, so we don't keep getting this
207102e6817SAlexander V. Chernikov  * message.  Should that prove to be undesirable, we can change it.
208102e6817SAlexander V. Chernikov  * This is called when all drivers that can attach to a given bus
209102e6817SAlexander V. Chernikov  * decline to accept this device.  Other errors may not be detected.
210102e6817SAlexander V. Chernikov  */
211102e6817SAlexander V. Chernikov static void
devctl_nomatch_handler(void * arg __unused,device_t dev)212102e6817SAlexander V. Chernikov devctl_nomatch_handler(void *arg __unused, device_t dev)
213102e6817SAlexander V. Chernikov {
2146437872cSIsaac Cilia Attard 	if (nomatch_enabled)
215102e6817SAlexander V. Chernikov 		devaddq("?", "", dev);
216102e6817SAlexander V. Chernikov }
217102e6817SAlexander V. Chernikov 
218102e6817SAlexander V. Chernikov static int
devopen(struct cdev * dev,int oflags,int devtype,struct thread * td)219102e6817SAlexander V. Chernikov devopen(struct cdev *dev, int oflags, int devtype, struct thread *td)
220102e6817SAlexander V. Chernikov {
221102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
222102e6817SAlexander V. Chernikov 	if (devsoftc.inuse) {
223102e6817SAlexander V. Chernikov 		mtx_unlock(&devsoftc.mtx);
224102e6817SAlexander V. Chernikov 		return (EBUSY);
225102e6817SAlexander V. Chernikov 	}
226102e6817SAlexander V. Chernikov 	/* move to init */
227102e6817SAlexander V. Chernikov 	devsoftc.inuse = 1;
228102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
229102e6817SAlexander V. Chernikov 	return (0);
230102e6817SAlexander V. Chernikov }
231102e6817SAlexander V. Chernikov 
232102e6817SAlexander V. Chernikov static int
devclose(struct cdev * dev,int fflag,int devtype,struct thread * td)233102e6817SAlexander V. Chernikov devclose(struct cdev *dev, int fflag, int devtype, struct thread *td)
234102e6817SAlexander V. Chernikov {
235102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
236102e6817SAlexander V. Chernikov 	devsoftc.inuse = 0;
237102e6817SAlexander V. Chernikov 	devsoftc.nonblock = 0;
238102e6817SAlexander V. Chernikov 	devsoftc.async = 0;
239102e6817SAlexander V. Chernikov 	cv_broadcast(&devsoftc.cv);
240102e6817SAlexander V. Chernikov 	funsetown(&devsoftc.sigio);
241102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
242102e6817SAlexander V. Chernikov 	return (0);
243102e6817SAlexander V. Chernikov }
244102e6817SAlexander V. Chernikov 
245102e6817SAlexander V. Chernikov /*
246102e6817SAlexander V. Chernikov  * The read channel for this device is used to report changes to
247102e6817SAlexander V. Chernikov  * userland in realtime.  We are required to free the data as well as
248102e6817SAlexander V. Chernikov  * the n1 object because we allocate them separately.  Also note that
249102e6817SAlexander V. Chernikov  * we return one record at a time.  If you try to read this device a
250102e6817SAlexander V. Chernikov  * character at a time, you will lose the rest of the data.  Listening
251102e6817SAlexander V. Chernikov  * programs are expected to cope.
252102e6817SAlexander V. Chernikov  */
253102e6817SAlexander V. Chernikov static int
devread(struct cdev * dev,struct uio * uio,int ioflag)254102e6817SAlexander V. Chernikov devread(struct cdev *dev, struct uio *uio, int ioflag)
255102e6817SAlexander V. Chernikov {
256102e6817SAlexander V. Chernikov 	struct dev_event_info *n1;
257102e6817SAlexander V. Chernikov 	int rv;
258102e6817SAlexander V. Chernikov 
259102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
260102e6817SAlexander V. Chernikov 	while (STAILQ_EMPTY(&devsoftc.devq)) {
261102e6817SAlexander V. Chernikov 		if (devsoftc.nonblock) {
262102e6817SAlexander V. Chernikov 			mtx_unlock(&devsoftc.mtx);
263102e6817SAlexander V. Chernikov 			return (EAGAIN);
264102e6817SAlexander V. Chernikov 		}
265102e6817SAlexander V. Chernikov 		rv = cv_wait_sig(&devsoftc.cv, &devsoftc.mtx);
266102e6817SAlexander V. Chernikov 		if (rv) {
267102e6817SAlexander V. Chernikov 			/*
268102e6817SAlexander V. Chernikov 			 * Need to translate ERESTART to EINTR here? -- jake
269102e6817SAlexander V. Chernikov 			 */
270102e6817SAlexander V. Chernikov 			mtx_unlock(&devsoftc.mtx);
271102e6817SAlexander V. Chernikov 			return (rv);
272102e6817SAlexander V. Chernikov 		}
273102e6817SAlexander V. Chernikov 	}
274102e6817SAlexander V. Chernikov 	n1 = STAILQ_FIRST(&devsoftc.devq);
275102e6817SAlexander V. Chernikov 	STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link);
276102e6817SAlexander V. Chernikov 	devsoftc.queued--;
277102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
278102e6817SAlexander V. Chernikov 	rv = uiomove(n1->dei_data, strlen(n1->dei_data), uio);
279102e6817SAlexander V. Chernikov 	uma_zfree(devsoftc.zone, n1);
280102e6817SAlexander V. Chernikov 	return (rv);
281102e6817SAlexander V. Chernikov }
282102e6817SAlexander V. Chernikov 
283102e6817SAlexander V. Chernikov static	int
devioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)284102e6817SAlexander V. Chernikov devioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag, struct thread *td)
285102e6817SAlexander V. Chernikov {
286102e6817SAlexander V. Chernikov 	switch (cmd) {
287102e6817SAlexander V. Chernikov 	case FIONBIO:
288102e6817SAlexander V. Chernikov 		if (*(int*)data)
289102e6817SAlexander V. Chernikov 			devsoftc.nonblock = 1;
290102e6817SAlexander V. Chernikov 		else
291102e6817SAlexander V. Chernikov 			devsoftc.nonblock = 0;
292102e6817SAlexander V. Chernikov 		return (0);
293102e6817SAlexander V. Chernikov 	case FIOASYNC:
294102e6817SAlexander V. Chernikov 		if (*(int*)data)
295102e6817SAlexander V. Chernikov 			devsoftc.async = 1;
296102e6817SAlexander V. Chernikov 		else
297102e6817SAlexander V. Chernikov 			devsoftc.async = 0;
298102e6817SAlexander V. Chernikov 		return (0);
299102e6817SAlexander V. Chernikov 	case FIOSETOWN:
300102e6817SAlexander V. Chernikov 		return fsetown(*(int *)data, &devsoftc.sigio);
301102e6817SAlexander V. Chernikov 	case FIOGETOWN:
302102e6817SAlexander V. Chernikov 		*(int *)data = fgetown(&devsoftc.sigio);
303102e6817SAlexander V. Chernikov 		return (0);
304102e6817SAlexander V. Chernikov 
305102e6817SAlexander V. Chernikov 		/* (un)Support for other fcntl() calls. */
306102e6817SAlexander V. Chernikov 	case FIOCLEX:
307102e6817SAlexander V. Chernikov 	case FIONCLEX:
308102e6817SAlexander V. Chernikov 	case FIONREAD:
309102e6817SAlexander V. Chernikov 	default:
310102e6817SAlexander V. Chernikov 		break;
311102e6817SAlexander V. Chernikov 	}
312102e6817SAlexander V. Chernikov 	return (ENOTTY);
313102e6817SAlexander V. Chernikov }
314102e6817SAlexander V. Chernikov 
315102e6817SAlexander V. Chernikov static	int
devpoll(struct cdev * dev,int events,struct thread * td)316102e6817SAlexander V. Chernikov devpoll(struct cdev *dev, int events, struct thread *td)
317102e6817SAlexander V. Chernikov {
318102e6817SAlexander V. Chernikov 	int	revents = 0;
319102e6817SAlexander V. Chernikov 
320102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
321102e6817SAlexander V. Chernikov 	if (events & (POLLIN | POLLRDNORM)) {
322102e6817SAlexander V. Chernikov 		if (!STAILQ_EMPTY(&devsoftc.devq))
323102e6817SAlexander V. Chernikov 			revents = events & (POLLIN | POLLRDNORM);
324102e6817SAlexander V. Chernikov 		else
325102e6817SAlexander V. Chernikov 			selrecord(td, &devsoftc.sel);
326102e6817SAlexander V. Chernikov 	}
327102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
328102e6817SAlexander V. Chernikov 
329102e6817SAlexander V. Chernikov 	return (revents);
330102e6817SAlexander V. Chernikov }
331102e6817SAlexander V. Chernikov 
332102e6817SAlexander V. Chernikov static int
devkqfilter(struct cdev * dev,struct knote * kn)333102e6817SAlexander V. Chernikov devkqfilter(struct cdev *dev, struct knote *kn)
334102e6817SAlexander V. Chernikov {
335102e6817SAlexander V. Chernikov 	int error;
336102e6817SAlexander V. Chernikov 
337102e6817SAlexander V. Chernikov 	if (kn->kn_filter == EVFILT_READ) {
338102e6817SAlexander V. Chernikov 		kn->kn_fop = &devctl_rfiltops;
339102e6817SAlexander V. Chernikov 		knlist_add(&devsoftc.sel.si_note, kn, 0);
340102e6817SAlexander V. Chernikov 		error = 0;
341102e6817SAlexander V. Chernikov 	} else
342102e6817SAlexander V. Chernikov 		error = EINVAL;
343102e6817SAlexander V. Chernikov 	return (error);
344102e6817SAlexander V. Chernikov }
345102e6817SAlexander V. Chernikov 
346102e6817SAlexander V. Chernikov static void
filt_devctl_detach(struct knote * kn)347102e6817SAlexander V. Chernikov filt_devctl_detach(struct knote *kn)
348102e6817SAlexander V. Chernikov {
349102e6817SAlexander V. Chernikov 	knlist_remove(&devsoftc.sel.si_note, kn, 0);
350102e6817SAlexander V. Chernikov }
351102e6817SAlexander V. Chernikov 
352102e6817SAlexander V. Chernikov static int
filt_devctl_read(struct knote * kn,long hint)353102e6817SAlexander V. Chernikov filt_devctl_read(struct knote *kn, long hint)
354102e6817SAlexander V. Chernikov {
355102e6817SAlexander V. Chernikov 	kn->kn_data = devsoftc.queued;
356102e6817SAlexander V. Chernikov 	return (kn->kn_data != 0);
357102e6817SAlexander V. Chernikov }
358102e6817SAlexander V. Chernikov 
359102e6817SAlexander V. Chernikov /**
360102e6817SAlexander V. Chernikov  * @brief Return whether the userland process is running
361102e6817SAlexander V. Chernikov  */
362102e6817SAlexander V. Chernikov bool
devctl_process_running(void)363102e6817SAlexander V. Chernikov devctl_process_running(void)
364102e6817SAlexander V. Chernikov {
365102e6817SAlexander V. Chernikov 	return (devsoftc.inuse == 1);
366102e6817SAlexander V. Chernikov }
367102e6817SAlexander V. Chernikov 
368102e6817SAlexander V. Chernikov static struct dev_event_info *
devctl_alloc_dei(void)369102e6817SAlexander V. Chernikov devctl_alloc_dei(void)
370102e6817SAlexander V. Chernikov {
371102e6817SAlexander V. Chernikov 	struct dev_event_info *dei = NULL;
372102e6817SAlexander V. Chernikov 
373102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
374102e6817SAlexander V. Chernikov 	if (devctl_queue_length == 0)
375102e6817SAlexander V. Chernikov 		goto out;
376102e6817SAlexander V. Chernikov 	dei = uma_zalloc(devsoftc.zone, M_NOWAIT);
377102e6817SAlexander V. Chernikov 	if (dei == NULL)
378102e6817SAlexander V. Chernikov 		dei = uma_zalloc(devsoftc.zone, M_NOWAIT | M_USE_RESERVE);
379102e6817SAlexander V. Chernikov 	if (dei == NULL) {
380102e6817SAlexander V. Chernikov 		/*
381102e6817SAlexander V. Chernikov 		 * Guard against no items in the queue. Normally, this won't
382102e6817SAlexander V. Chernikov 		 * happen, but if lots of events happen all at once and there's
383102e6817SAlexander V. Chernikov 		 * a chance we're out of allocated space but none have yet been
384102e6817SAlexander V. Chernikov 		 * queued when we get here, leaving nothing to steal. This can
385102e6817SAlexander V. Chernikov 		 * also happen with error injection. Fail safe by returning
386102e6817SAlexander V. Chernikov 		 * NULL in that case..
387102e6817SAlexander V. Chernikov 		 */
388102e6817SAlexander V. Chernikov 		if (devsoftc.queued == 0)
389102e6817SAlexander V. Chernikov 			goto out;
390102e6817SAlexander V. Chernikov 		dei = STAILQ_FIRST(&devsoftc.devq);
391102e6817SAlexander V. Chernikov 		STAILQ_REMOVE_HEAD(&devsoftc.devq, dei_link);
392102e6817SAlexander V. Chernikov 		devsoftc.queued--;
393102e6817SAlexander V. Chernikov 	}
394102e6817SAlexander V. Chernikov 	MPASS(dei != NULL);
395102e6817SAlexander V. Chernikov 	*dei->dei_data = '\0';
396102e6817SAlexander V. Chernikov out:
397102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
398102e6817SAlexander V. Chernikov 	return (dei);
399102e6817SAlexander V. Chernikov }
400102e6817SAlexander V. Chernikov 
401102e6817SAlexander V. Chernikov static struct dev_event_info *
devctl_alloc_dei_sb(struct sbuf * sb)402102e6817SAlexander V. Chernikov devctl_alloc_dei_sb(struct sbuf *sb)
403102e6817SAlexander V. Chernikov {
404102e6817SAlexander V. Chernikov 	struct dev_event_info *dei;
405102e6817SAlexander V. Chernikov 
406102e6817SAlexander V. Chernikov 	dei = devctl_alloc_dei();
407102e6817SAlexander V. Chernikov 	if (dei != NULL)
408102e6817SAlexander V. Chernikov 		sbuf_new(sb, dei->dei_data, sizeof(dei->dei_data), SBUF_FIXEDLEN);
409102e6817SAlexander V. Chernikov 	return (dei);
410102e6817SAlexander V. Chernikov }
411102e6817SAlexander V. Chernikov 
412102e6817SAlexander V. Chernikov static void
devctl_free_dei(struct dev_event_info * dei)413102e6817SAlexander V. Chernikov devctl_free_dei(struct dev_event_info *dei)
414102e6817SAlexander V. Chernikov {
415102e6817SAlexander V. Chernikov 	uma_zfree(devsoftc.zone, dei);
416102e6817SAlexander V. Chernikov }
417102e6817SAlexander V. Chernikov 
418102e6817SAlexander V. Chernikov static void
devctl_queue(struct dev_event_info * dei)419102e6817SAlexander V. Chernikov devctl_queue(struct dev_event_info *dei)
420102e6817SAlexander V. Chernikov {
421102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
422102e6817SAlexander V. Chernikov 	STAILQ_INSERT_TAIL(&devsoftc.devq, dei, dei_link);
423102e6817SAlexander V. Chernikov 	devsoftc.queued++;
424102e6817SAlexander V. Chernikov 	cv_broadcast(&devsoftc.cv);
425102e6817SAlexander V. Chernikov 	KNOTE_LOCKED(&devsoftc.sel.si_note, 0);
426102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
427102e6817SAlexander V. Chernikov 	selwakeup(&devsoftc.sel);
428102e6817SAlexander V. Chernikov 	if (devsoftc.async && devsoftc.sigio != NULL)
429102e6817SAlexander V. Chernikov 		pgsigio(&devsoftc.sigio, SIGIO, 0);
430102e6817SAlexander V. Chernikov }
431102e6817SAlexander V. Chernikov 
432102e6817SAlexander V. Chernikov /**
433102e6817SAlexander V. Chernikov  * @brief Send a 'notification' to userland, using standard ways
434102e6817SAlexander V. Chernikov  */
435102e6817SAlexander V. Chernikov void
devctl_notify(const char * system,const char * subsystem,const char * type,const char * data)436102e6817SAlexander V. Chernikov devctl_notify(const char *system, const char *subsystem, const char *type,
437102e6817SAlexander V. Chernikov     const char *data)
438102e6817SAlexander V. Chernikov {
439102e6817SAlexander V. Chernikov 	struct dev_event_info *dei;
440102e6817SAlexander V. Chernikov 	struct sbuf sb;
441102e6817SAlexander V. Chernikov 
442102e6817SAlexander V. Chernikov 	if (system == NULL || subsystem == NULL || type == NULL)
443102e6817SAlexander V. Chernikov 		return;
444afbb26b5SBaptiste Daroussin 	if (devctl_notify_hook.send_f != NULL)
445afbb26b5SBaptiste Daroussin 		devctl_notify_hook.send_f(system, subsystem, type, data);
446102e6817SAlexander V. Chernikov 	dei = devctl_alloc_dei_sb(&sb);
447102e6817SAlexander V. Chernikov 	if (dei == NULL)
448102e6817SAlexander V. Chernikov 		return;
449102e6817SAlexander V. Chernikov 	sbuf_cpy(&sb, "!system=");
450102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, system);
451102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, " subsystem=");
452102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, subsystem);
453102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, " type=");
454102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, type);
455102e6817SAlexander V. Chernikov 	if (data != NULL) {
456102e6817SAlexander V. Chernikov 		sbuf_putc(&sb, ' ');
457102e6817SAlexander V. Chernikov 		sbuf_cat(&sb, data);
458102e6817SAlexander V. Chernikov 	}
459102e6817SAlexander V. Chernikov 	sbuf_putc(&sb, '\n');
460102e6817SAlexander V. Chernikov 	if (sbuf_finish(&sb) != 0)
461102e6817SAlexander V. Chernikov 		devctl_free_dei(dei);	/* overflow -> drop it */
462102e6817SAlexander V. Chernikov 	else
463102e6817SAlexander V. Chernikov 		devctl_queue(dei);
464102e6817SAlexander V. Chernikov }
465102e6817SAlexander V. Chernikov 
466102e6817SAlexander V. Chernikov /*
467102e6817SAlexander V. Chernikov  * Common routine that tries to make sending messages as easy as possible.
468102e6817SAlexander V. Chernikov  * We allocate memory for the data, copy strings into that, but do not
469102e6817SAlexander V. Chernikov  * free it unless there's an error.  The dequeue part of the driver should
470102e6817SAlexander V. Chernikov  * free the data.  We don't send data when the device is disabled.  We do
471102e6817SAlexander V. Chernikov  * send data, even when we have no listeners, because we wish to avoid
472102e6817SAlexander V. Chernikov  * races relating to startup and restart of listening applications.
473102e6817SAlexander V. Chernikov  *
474102e6817SAlexander V. Chernikov  * devaddq is designed to string together the type of event, with the
475102e6817SAlexander V. Chernikov  * object of that event, plus the plug and play info and location info
476102e6817SAlexander V. Chernikov  * for that event.  This is likely most useful for devices, but less
477102e6817SAlexander V. Chernikov  * useful for other consumers of this interface.  Those should use
478102e6817SAlexander V. Chernikov  * the devctl_notify() interface instead.
479102e6817SAlexander V. Chernikov  *
480102e6817SAlexander V. Chernikov  * Output:
481102e6817SAlexander V. Chernikov  *	${type}${what} at $(location dev) $(pnp-info dev) on $(parent dev)
482102e6817SAlexander V. Chernikov  */
483102e6817SAlexander V. Chernikov static void
devaddq(const char * type,const char * what,device_t dev)484102e6817SAlexander V. Chernikov devaddq(const char *type, const char *what, device_t dev)
485102e6817SAlexander V. Chernikov {
486102e6817SAlexander V. Chernikov 	struct dev_event_info *dei;
487102e6817SAlexander V. Chernikov 	const char *parstr;
488102e6817SAlexander V. Chernikov 	struct sbuf sb;
489afbb26b5SBaptiste Daroussin 	size_t beginlen;
490102e6817SAlexander V. Chernikov 
491102e6817SAlexander V. Chernikov 	dei = devctl_alloc_dei_sb(&sb);
492102e6817SAlexander V. Chernikov 	if (dei == NULL)
493102e6817SAlexander V. Chernikov 		return;
494102e6817SAlexander V. Chernikov 	sbuf_cpy(&sb, type);
495102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, what);
496102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, " at ");
497afbb26b5SBaptiste Daroussin 	beginlen = sbuf_len(&sb);
498102e6817SAlexander V. Chernikov 
499102e6817SAlexander V. Chernikov 	/* Add in the location */
500102e6817SAlexander V. Chernikov 	bus_child_location(dev, &sb);
501102e6817SAlexander V. Chernikov 	sbuf_putc(&sb, ' ');
502102e6817SAlexander V. Chernikov 
503102e6817SAlexander V. Chernikov 	/* Add in pnpinfo */
504102e6817SAlexander V. Chernikov 	bus_child_pnpinfo(dev, &sb);
505102e6817SAlexander V. Chernikov 
506102e6817SAlexander V. Chernikov 	/* Get the parent of this device, or / if high enough in the tree. */
507102e6817SAlexander V. Chernikov 	if (device_get_parent(dev) == NULL)
508102e6817SAlexander V. Chernikov 		parstr = ".";	/* Or '/' ? */
509102e6817SAlexander V. Chernikov 	else
510102e6817SAlexander V. Chernikov 		parstr = device_get_nameunit(device_get_parent(dev));
511102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, " on ");
512102e6817SAlexander V. Chernikov 	sbuf_cat(&sb, parstr);
513102e6817SAlexander V. Chernikov 	sbuf_putc(&sb, '\n');
514102e6817SAlexander V. Chernikov 	if (sbuf_finish(&sb) != 0)
515102e6817SAlexander V. Chernikov 		goto bad;
516afbb26b5SBaptiste Daroussin 	if (devctl_notify_hook.send_f != NULL) {
517afbb26b5SBaptiste Daroussin 		const char *t;
518afbb26b5SBaptiste Daroussin 
519afbb26b5SBaptiste Daroussin 		switch (*type) {
520afbb26b5SBaptiste Daroussin 		case '+':
521afbb26b5SBaptiste Daroussin 			t = "ATTACH";
522afbb26b5SBaptiste Daroussin 			break;
523afbb26b5SBaptiste Daroussin 		case '-':
524afbb26b5SBaptiste Daroussin 			t = "DETACH";
525afbb26b5SBaptiste Daroussin 			break;
526afbb26b5SBaptiste Daroussin 		default:
527afbb26b5SBaptiste Daroussin 			t = "NOMATCH";
528afbb26b5SBaptiste Daroussin 			break;
529afbb26b5SBaptiste Daroussin 		}
530afbb26b5SBaptiste Daroussin 		devctl_notify_hook.send_f("device",
531afbb26b5SBaptiste Daroussin 		    what, t, sbuf_data(&sb) + beginlen);
532afbb26b5SBaptiste Daroussin 	}
533102e6817SAlexander V. Chernikov 	devctl_queue(dei);
534102e6817SAlexander V. Chernikov 	return;
535102e6817SAlexander V. Chernikov bad:
536102e6817SAlexander V. Chernikov 	devctl_free_dei(dei);
537102e6817SAlexander V. Chernikov }
538102e6817SAlexander V. Chernikov 
539102e6817SAlexander V. Chernikov static int
sysctl_devctl_queue(SYSCTL_HANDLER_ARGS)540102e6817SAlexander V. Chernikov sysctl_devctl_queue(SYSCTL_HANDLER_ARGS)
541102e6817SAlexander V. Chernikov {
542102e6817SAlexander V. Chernikov 	int q, error;
543102e6817SAlexander V. Chernikov 
544102e6817SAlexander V. Chernikov 	q = devctl_queue_length;
545102e6817SAlexander V. Chernikov 	error = sysctl_handle_int(oidp, &q, 0, req);
546102e6817SAlexander V. Chernikov 	if (error || !req->newptr)
547102e6817SAlexander V. Chernikov 		return (error);
548102e6817SAlexander V. Chernikov 	if (q < 0)
549102e6817SAlexander V. Chernikov 		return (EINVAL);
550102e6817SAlexander V. Chernikov 
551102e6817SAlexander V. Chernikov 	/*
552102e6817SAlexander V. Chernikov 	 * When set as a tunable, we've not yet initialized the mutex.
553102e6817SAlexander V. Chernikov 	 * It is safe to just assign to devctl_queue_length and return
554102e6817SAlexander V. Chernikov 	 * as we're racing no one. We'll use whatever value set in
555102e6817SAlexander V. Chernikov 	 * devinit.
556102e6817SAlexander V. Chernikov 	 */
557102e6817SAlexander V. Chernikov 	if (!mtx_initialized(&devsoftc.mtx)) {
558102e6817SAlexander V. Chernikov 		devctl_queue_length = q;
559102e6817SAlexander V. Chernikov 		return (0);
560102e6817SAlexander V. Chernikov 	}
561102e6817SAlexander V. Chernikov 
562102e6817SAlexander V. Chernikov 	/*
563102e6817SAlexander V. Chernikov 	 * XXX It's hard to grow or shrink the UMA zone. Only allow
564102e6817SAlexander V. Chernikov 	 * disabling the queue size for the moment until underlying
565102e6817SAlexander V. Chernikov 	 * UMA issues can be sorted out.
566102e6817SAlexander V. Chernikov 	 */
567102e6817SAlexander V. Chernikov 	if (q != 0)
568102e6817SAlexander V. Chernikov 		return (EINVAL);
569102e6817SAlexander V. Chernikov 	if (q == devctl_queue_length)
570102e6817SAlexander V. Chernikov 		return (0);
571102e6817SAlexander V. Chernikov 	mtx_lock(&devsoftc.mtx);
572102e6817SAlexander V. Chernikov 	devctl_queue_length = 0;
573102e6817SAlexander V. Chernikov 	uma_zdestroy(devsoftc.zone);
574102e6817SAlexander V. Chernikov 	devsoftc.zone = 0;
575102e6817SAlexander V. Chernikov 	mtx_unlock(&devsoftc.mtx);
576102e6817SAlexander V. Chernikov 	return (0);
577102e6817SAlexander V. Chernikov }
578102e6817SAlexander V. Chernikov 
579102e6817SAlexander V. Chernikov /**
580102e6817SAlexander V. Chernikov  * @brief safely quotes strings that might have double quotes in them.
581102e6817SAlexander V. Chernikov  *
582102e6817SAlexander V. Chernikov  * The devctl protocol relies on quoted strings having matching quotes.
583102e6817SAlexander V. Chernikov  * This routine quotes any internal quotes so the resulting string
584102e6817SAlexander V. Chernikov  * is safe to pass to snprintf to construct, for example pnp info strings.
585102e6817SAlexander V. Chernikov  *
586102e6817SAlexander V. Chernikov  * @param sb	sbuf to place the characters into
587102e6817SAlexander V. Chernikov  * @param src	Original buffer.
588102e6817SAlexander V. Chernikov  */
589102e6817SAlexander V. Chernikov void
devctl_safe_quote_sb(struct sbuf * sb,const char * src)590102e6817SAlexander V. Chernikov devctl_safe_quote_sb(struct sbuf *sb, const char *src)
591102e6817SAlexander V. Chernikov {
592102e6817SAlexander V. Chernikov 	while (*src != '\0') {
593102e6817SAlexander V. Chernikov 		if (*src == '"' || *src == '\\')
594102e6817SAlexander V. Chernikov 			sbuf_putc(sb, '\\');
595102e6817SAlexander V. Chernikov 		sbuf_putc(sb, *src++);
596102e6817SAlexander V. Chernikov 	}
597102e6817SAlexander V. Chernikov }
598afbb26b5SBaptiste Daroussin 
599afbb26b5SBaptiste Daroussin void
devctl_set_notify_hook(send_event_f * hook)600afbb26b5SBaptiste Daroussin devctl_set_notify_hook(send_event_f *hook)
601afbb26b5SBaptiste Daroussin {
602afbb26b5SBaptiste Daroussin 	devctl_notify_hook.send_f = hook;
603afbb26b5SBaptiste Daroussin }
604afbb26b5SBaptiste Daroussin 
605afbb26b5SBaptiste Daroussin void
devctl_unset_notify_hook(void)606afbb26b5SBaptiste Daroussin devctl_unset_notify_hook(void)
607afbb26b5SBaptiste Daroussin {
608afbb26b5SBaptiste Daroussin 	devctl_notify_hook.send_f = NULL;
609afbb26b5SBaptiste Daroussin }
610afbb26b5SBaptiste Daroussin 
611