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