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/event.h>
32 #include <sys/thread.h>
33 #include <sys/thread2.h>
34 #include <sys/hotplug.h>
36 #define HOTPLUG_MAXEVENTS 16
40 static d_open_t hotplugopen;
41 static d_close_t hotplugclose;
42 static d_read_t hotplugread;
43 static d_kqfilter_t hotplugkqfilter;
45 static void hotplugfiltdetach(struct knote *);
46 static int hotplugfilt(struct knote *, long);
48 static struct dev_ops hotplug_ops = {
49 { "hotplug", CDEV_MAJOR, D_KQFILTER },
50 .d_open = hotplugopen,
51 .d_close = hotplugclose,
52 .d_read = hotplugread,
53 .d_kqfilter = hotplugkqfilter
56 struct hotplug_event_info {
57 struct hotplug_event *he;
58 TAILQ_ENTRY(hotplug_event_info) hei_link;
61 TAILQ_HEAD(hpq, hotplug_event_info);
63 static struct hotplug_softc
71 void (*old_devfs_node_added)(struct hotplug_device *hpdev);
72 void (*old_devfs_node_removed)(struct hotplug_device *hpdev);
75 extern void (*devfs_node_added)(struct hotplug_device *hpdev);
76 extern void (*devfs_node_removed)(struct hotplug_device *hpdev);
78 void hotplug_devfs_node_added(struct hotplug_device *hpdev);
79 void hotplug_devfs_node_removed(struct hotplug_device *hpdev);
81 static int hotplug_get_event(struct hotplug_event *he);
82 static int hotplug_put_event(struct hotplug_event *he);
84 static int hotplug_uninit(void);
85 static int hotplug_init(void);
88 hotplugopen(struct dev_open_args *ap)
97 hotplugclose(struct dev_close_args *ap)
100 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
102 lockmgr(&hpsc.lock, LK_RELEASE);
106 static struct filterops hotplugfiltops =
107 { FILTEROP_ISFD, NULL, hotplugfiltdetach, hotplugfilt };
110 hotplugkqfilter(struct dev_kqfilter_args *ap)
112 struct knote *kn = ap->a_kn;
117 switch (kn->kn_filter) {
119 kn->kn_fop = &hotplugfiltops;
122 ap->a_result = EOPNOTSUPP;
126 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
127 klist = &hpsc.kq.ki_note;
128 knote_insert(klist, kn);
129 lockmgr(&hpsc.lock, LK_RELEASE);
135 hotplugfiltdetach(struct knote *kn)
139 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
140 klist = &hpsc.kq.ki_note;
141 knote_remove(klist, kn);
142 lockmgr(&hpsc.lock, LK_RELEASE);
146 hotplugfilt(struct knote *kn, long hint)
150 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
151 if (!TAILQ_EMPTY(&hpsc.queue))
153 lockmgr(&hpsc.lock, LK_RELEASE);
159 hotplug_get_event(struct hotplug_event *he)
161 struct hotplug_event_info *hei;
163 /* shouldn't get there */
164 if(TAILQ_EMPTY(&hpsc.queue))
167 /* we are under hotplugread() lock here */
168 hei = TAILQ_FIRST(&hpsc.queue);
169 memcpy(he, hei->he, sizeof(struct hotplug_event));
170 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
171 kfree(hei->he, M_DEVBUF);
172 kfree(hei, M_DEVBUF);
177 hotplugread(struct dev_read_args *ap)
179 struct uio *uio = ap->a_uio;
180 struct hotplug_event *he;
183 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
184 while(TAILQ_EMPTY(&hpsc.queue)) {
185 tsleep_interlock(&hpsc, PCATCH);
186 lockmgr(&hpsc.lock, LK_RELEASE);
187 rv = tsleep(&hpsc, PCATCH | PINTERLOCKED, "hotplug", 0);
189 lockmgr(&hpsc.lock, LK_RELEASE);
193 he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
194 if(hotplug_get_event(he) == 0) {
195 rv = uiomove((caddr_t)he, sizeof(struct hotplug_event), uio);
198 lockmgr(&hpsc.lock, LK_RELEASE);
203 hotplug_put_event(struct hotplug_event *he)
205 struct hotplug_event_info *hei = NULL;
207 if (hpsc.qcount == HOTPLUG_MAXEVENTS && hpsc.opened) {
208 kprintf("hotplug: event lost, queue full\n");
211 hei = kmalloc(sizeof(struct hotplug_event_info), M_DEVBUF, M_WAITOK);
212 hei->he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
213 memcpy(hei->he, he, sizeof(struct hotplug_event));
214 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
215 TAILQ_INSERT_TAIL(&hpsc.queue, hei, hei_link);
218 lockmgr(&hpsc.lock, LK_RELEASE);
219 KNOTE(&hpsc.kq.ki_note, 0);
224 hotplug_devfs_node_added(struct hotplug_device *hpdev) {
225 struct hotplug_event he;
229 if(!hpdev->dev || !hpsc.opened)
231 class = hpdev->dev->si_ops->head.flags;
233 he.he_type = HOTPLUG_DEVAT;
234 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
235 strlcpy(he.he_devname, name, sizeof(he.he_devname));
236 hotplug_put_event(&he);
240 hotplug_devfs_node_removed(struct hotplug_device *hpdev) {
241 struct hotplug_event he;
245 if(!hpdev->dev || !hpsc.opened)
247 class = hpdev->dev->si_ops->head.flags;
249 he.he_type = HOTPLUG_DEVDT;
250 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
251 strlcpy(he.he_devname, name, sizeof(he.he_devname));
252 hotplug_put_event(&he);
258 hpsc.dev = make_dev(&hotplug_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hotplug");
260 lockinit(&hpsc.lock, "hotplug mtx", 0, 0);
261 TAILQ_INIT(&hpsc.queue);
263 hpsc.old_devfs_node_added = devfs_node_added;
264 hpsc.old_devfs_node_removed = devfs_node_removed;
265 devfs_node_added = hotplug_devfs_node_added;
266 devfs_node_removed = hotplug_devfs_node_removed;
273 struct hotplug_event_info *hei;
277 devfs_node_added = hpsc.old_devfs_node_added;
278 devfs_node_removed = hpsc.old_devfs_node_removed;
279 /* Free the entire tail queue. */
280 while ((hei = TAILQ_FIRST(&hpsc.queue))) {
281 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
282 kfree(hei->he, M_DEVBUF);
283 kfree(hei, M_DEVBUF);
286 /* The tail queue should now be empty. */
287 if (!TAILQ_EMPTY(&hpsc.queue))
288 kprintf("hotplug: queue not empty!\n");
289 destroy_dev(hpsc.dev);
294 hotplug_modevh(struct module *m, int what, void *arg __unused)
300 error = hotplug_init();
303 error = hotplug_uninit();
312 static moduledata_t hotplug_mod = {
318 DECLARE_MODULE(hotplug, hotplug_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);