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>
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_poll_t hotplugpoll;
46 static struct dev_ops hotplug_ops = {
47 { "hotplug", CDEV_MAJOR, 0 },
48 .d_open = hotplugopen,
49 .d_close = hotplugclose,
50 .d_read = hotplugread,
51 .d_poll = hotplugpoll,
54 struct hotplug_event_info {
55 struct hotplug_event *he;
56 TAILQ_ENTRY(hotplug_event_info) hei_link;
59 TAILQ_HEAD(hpq, hotplug_event_info);
61 static struct hotplug_softc
69 void (*old_devfs_node_added)(struct hotplug_device *hpdev);
70 void (*old_devfs_node_removed)(struct hotplug_device *hpdev);
73 extern void (*devfs_node_added)(struct hotplug_device *hpdev);
74 extern void (*devfs_node_removed)(struct hotplug_device *hpdev);
76 void hotplug_devfs_node_added(struct hotplug_device *hpdev);
77 void hotplug_devfs_node_removed(struct hotplug_device *hpdev);
79 static int hotplug_get_event(struct hotplug_event *he);
80 static int hotplug_put_event(struct hotplug_event *he);
82 static int hotplug_uninit(void);
83 static int hotplug_init(void);
86 hotplugopen(struct dev_open_args *ap)
95 hotplugclose(struct dev_close_args *ap)
98 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
100 lockmgr(&hpsc.lock, LK_RELEASE);
105 hotplugpoll(struct dev_poll_args *ap)
109 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
110 if (ap->a_events & (POLLIN | POLLRDNORM)) {
111 if (!TAILQ_EMPTY(&hpsc.queue))
112 revents = ap->a_events & (POLLIN | POLLRDNORM);
114 selrecord(curthread, &hpsc.sel);
116 lockmgr(&hpsc.lock, LK_RELEASE);
118 ap->a_events = revents;
123 hotplug_get_event(struct hotplug_event *he)
125 struct hotplug_event_info *hei;
127 /* shouldn't get there */
128 if(TAILQ_EMPTY(&hpsc.queue))
131 /* we are under hotplugread() lock here */
132 hei = TAILQ_FIRST(&hpsc.queue);
133 memcpy(he, hei->he, sizeof(struct hotplug_event));
134 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
135 kfree(hei->he, M_DEVBUF);
136 kfree(hei, M_DEVBUF);
141 hotplugread(struct dev_read_args *ap)
143 struct uio *uio = ap->a_uio;
144 struct hotplug_event *he;
147 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
148 while(TAILQ_EMPTY(&hpsc.queue)) {
149 tsleep_interlock(&hpsc, PCATCH);
150 lockmgr(&hpsc.lock, LK_RELEASE);
151 rv = tsleep(&hpsc, PCATCH | PINTERLOCKED, "hotplug", 0);
153 lockmgr(&hpsc.lock, LK_RELEASE);
157 he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
158 if(hotplug_get_event(he) == 0) {
159 rv = uiomove((caddr_t)he, sizeof(struct hotplug_event), uio);
162 lockmgr(&hpsc.lock, LK_RELEASE);
167 hotplug_put_event(struct hotplug_event *he)
169 struct hotplug_event_info *hei = NULL;
171 if (hpsc.qcount == HOTPLUG_MAXEVENTS && hpsc.opened) {
172 kprintf("hotplug: event lost, queue full\n");
175 hei = kmalloc(sizeof(struct hotplug_event_info), M_DEVBUF, M_WAITOK);
176 hei->he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
177 memcpy(hei->he, he, sizeof(struct hotplug_event));
178 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
179 TAILQ_INSERT_TAIL(&hpsc.queue, hei, hei_link);
182 lockmgr(&hpsc.lock, LK_RELEASE);
183 selwakeup(&hpsc.sel);
188 hotplug_devfs_node_added(struct hotplug_device *hpdev) {
189 struct hotplug_event he;
193 if(!hpdev->dev || !hpsc.opened)
195 class = hpdev->dev->si_ops->head.flags;
197 he.he_type = HOTPLUG_DEVAT;
198 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
199 strlcpy(he.he_devname, name, sizeof(he.he_devname));
200 hotplug_put_event(&he);
204 hotplug_devfs_node_removed(struct hotplug_device *hpdev) {
205 struct hotplug_event he;
209 if(!hpdev->dev || !hpsc.opened)
211 class = hpdev->dev->si_ops->head.flags;
213 he.he_type = HOTPLUG_DEVDT;
214 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
215 strlcpy(he.he_devname, name, sizeof(he.he_devname));
216 hotplug_put_event(&he);
222 hpsc.dev = make_dev(&hotplug_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hotplug");
224 lockinit(&hpsc.lock, "hotplug mtx", 0, 0);
225 TAILQ_INIT(&hpsc.queue);
227 hpsc.old_devfs_node_added = devfs_node_added;
228 hpsc.old_devfs_node_removed = devfs_node_removed;
229 devfs_node_added = hotplug_devfs_node_added;
230 devfs_node_removed = hotplug_devfs_node_removed;
237 struct hotplug_event_info *hei;
241 devfs_node_added = hpsc.old_devfs_node_added;
242 devfs_node_removed = hpsc.old_devfs_node_removed;
243 /* Free the entire tail queue. */
244 while ((hei = TAILQ_FIRST(&hpsc.queue))) {
245 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
246 kfree(hei->he, M_DEVBUF);
247 kfree(hei, M_DEVBUF);
250 /* The tail queue should now be empty. */
251 if (!TAILQ_EMPTY(&hpsc.queue))
252 kprintf("hotplug: queue not empty!\n");
253 destroy_dev(hpsc.dev);
258 hotplug_modevh(struct module *m, int what, void *arg __unused)
264 error = hotplug_init();
267 error = hotplug_uninit();
276 static moduledata_t hotplug_mod = {
282 DECLARE_MODULE(hotplug, hotplug_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);