2 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 * Device attachment and detachment notifications.
22 #include <sys/param.h>
23 #include <sys/systm.h>
24 #include <sys/malloc.h>
25 #include <sys/kernel.h>
26 #include <sys/module.h>
27 #include <sys/sysctl.h>
28 #include <sys/device.h>
30 #include <sys/selinfo.h>
31 #include <sys/event.h>
33 #include <sys/thread.h>
34 #include <sys/thread2.h>
35 #include <sys/hotplug.h>
37 #define HOTPLUG_MAXEVENTS 16
41 static d_open_t hotplugopen;
42 static d_close_t hotplugclose;
43 static d_read_t hotplugread;
44 static d_kqfilter_t hotplugkqfilter;
46 static void hotplugfiltdetach(struct knote *);
47 static int hotplugfilt(struct knote *, long);
49 static struct dev_ops hotplug_ops = {
50 { "hotplug", CDEV_MAJOR, D_KQFILTER },
51 .d_open = hotplugopen,
52 .d_close = hotplugclose,
53 .d_read = hotplugread,
54 .d_kqfilter = hotplugkqfilter
57 struct hotplug_event_info {
58 struct hotplug_event *he;
59 TAILQ_ENTRY(hotplug_event_info) hei_link;
62 TAILQ_HEAD(hpq, hotplug_event_info);
64 static struct hotplug_softc
72 void (*old_devfs_node_added)(struct hotplug_device *hpdev);
73 void (*old_devfs_node_removed)(struct hotplug_device *hpdev);
76 extern void (*devfs_node_added)(struct hotplug_device *hpdev);
77 extern void (*devfs_node_removed)(struct hotplug_device *hpdev);
79 void hotplug_devfs_node_added(struct hotplug_device *hpdev);
80 void hotplug_devfs_node_removed(struct hotplug_device *hpdev);
82 static int hotplug_get_event(struct hotplug_event *he);
83 static int hotplug_put_event(struct hotplug_event *he);
85 static int hotplug_uninit(void);
86 static int hotplug_init(void);
89 hotplugopen(struct dev_open_args *ap)
98 hotplugclose(struct dev_close_args *ap)
101 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
103 lockmgr(&hpsc.lock, LK_RELEASE);
107 static struct filterops hotplugfiltops =
108 { 1, NULL, hotplugfiltdetach, hotplugfilt };
111 hotplugkqfilter(struct dev_kqfilter_args *ap)
113 struct knote *kn = ap->a_kn;
118 switch (kn->kn_filter) {
120 kn->kn_fop = &hotplugfiltops;
123 ap->a_result = EOPNOTSUPP;
127 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
129 klist = &hpsc.sel.si_note;
130 SLIST_INSERT_HEAD(klist, kn, kn_selnext);
132 lockmgr(&hpsc.lock, LK_RELEASE);
138 hotplugfiltdetach(struct knote *kn)
142 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
144 klist = &hpsc.sel.si_note;
145 SLIST_REMOVE(klist, kn, knote, kn_selnext);
147 lockmgr(&hpsc.lock, LK_RELEASE);
151 hotplugfilt(struct knote *kn, long hint)
155 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
156 if (!TAILQ_EMPTY(&hpsc.queue))
158 lockmgr(&hpsc.lock, LK_RELEASE);
164 hotplug_get_event(struct hotplug_event *he)
166 struct hotplug_event_info *hei;
168 /* shouldn't get there */
169 if(TAILQ_EMPTY(&hpsc.queue))
172 /* we are under hotplugread() lock here */
173 hei = TAILQ_FIRST(&hpsc.queue);
174 memcpy(he, hei->he, sizeof(struct hotplug_event));
175 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
176 kfree(hei->he, M_DEVBUF);
177 kfree(hei, M_DEVBUF);
182 hotplugread(struct dev_read_args *ap)
184 struct uio *uio = ap->a_uio;
185 struct hotplug_event *he;
188 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
189 while(TAILQ_EMPTY(&hpsc.queue)) {
190 tsleep_interlock(&hpsc, PCATCH);
191 lockmgr(&hpsc.lock, LK_RELEASE);
192 rv = tsleep(&hpsc, PCATCH | PINTERLOCKED, "hotplug", 0);
194 lockmgr(&hpsc.lock, LK_RELEASE);
198 he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
199 if(hotplug_get_event(he) == 0) {
200 rv = uiomove((caddr_t)he, sizeof(struct hotplug_event), uio);
203 lockmgr(&hpsc.lock, LK_RELEASE);
208 hotplug_put_event(struct hotplug_event *he)
210 struct hotplug_event_info *hei = NULL;
212 if (hpsc.qcount == HOTPLUG_MAXEVENTS && hpsc.opened) {
213 kprintf("hotplug: event lost, queue full\n");
216 hei = kmalloc(sizeof(struct hotplug_event_info), M_DEVBUF, M_WAITOK);
217 hei->he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
218 memcpy(hei->he, he, sizeof(struct hotplug_event));
219 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
220 TAILQ_INSERT_TAIL(&hpsc.queue, hei, hei_link);
223 lockmgr(&hpsc.lock, LK_RELEASE);
224 selwakeup(&hpsc.sel);
229 hotplug_devfs_node_added(struct hotplug_device *hpdev) {
230 struct hotplug_event he;
234 if(!hpdev->dev || !hpsc.opened)
236 class = hpdev->dev->si_ops->head.flags;
238 he.he_type = HOTPLUG_DEVAT;
239 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
240 strlcpy(he.he_devname, name, sizeof(he.he_devname));
241 hotplug_put_event(&he);
245 hotplug_devfs_node_removed(struct hotplug_device *hpdev) {
246 struct hotplug_event he;
250 if(!hpdev->dev || !hpsc.opened)
252 class = hpdev->dev->si_ops->head.flags;
254 he.he_type = HOTPLUG_DEVDT;
255 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
256 strlcpy(he.he_devname, name, sizeof(he.he_devname));
257 hotplug_put_event(&he);
263 hpsc.dev = make_dev(&hotplug_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hotplug");
265 lockinit(&hpsc.lock, "hotplug mtx", 0, 0);
266 TAILQ_INIT(&hpsc.queue);
268 hpsc.old_devfs_node_added = devfs_node_added;
269 hpsc.old_devfs_node_removed = devfs_node_removed;
270 devfs_node_added = hotplug_devfs_node_added;
271 devfs_node_removed = hotplug_devfs_node_removed;
278 struct hotplug_event_info *hei;
282 devfs_node_added = hpsc.old_devfs_node_added;
283 devfs_node_removed = hpsc.old_devfs_node_removed;
284 /* Free the entire tail queue. */
285 while ((hei = TAILQ_FIRST(&hpsc.queue))) {
286 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
287 kfree(hei->he, M_DEVBUF);
288 kfree(hei, M_DEVBUF);
291 /* The tail queue should now be empty. */
292 if (!TAILQ_EMPTY(&hpsc.queue))
293 kprintf("hotplug: queue not empty!\n");
294 destroy_dev(hpsc.dev);
299 hotplug_modevh(struct module *m, int what, void *arg __unused)
305 error = hotplug_init();
308 error = hotplug_uninit();
317 static moduledata_t hotplug_mod = {
323 DECLARE_MODULE(hotplug, hotplug_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);