kernel - Remove D_KQFILTER flag
[dragonfly.git] / sys / dev / misc / hotplug / hotplug.c
CommitLineData
115f9a72
AP
1/*
2 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
3 *
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.
7 *
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.
15 */
16
17/*
18 * Device attachment and detachment notifications.
19 */
20
21#include <sys/conf.h>
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>
29#include <sys/lock.h>
96b15e7f 30#include <sys/event.h>
115f9a72
AP
31#include <sys/uio.h>
32#include <sys/thread.h>
33#include <sys/thread2.h>
34#include <sys/hotplug.h>
35
36#define HOTPLUG_MAXEVENTS 16
37
38#define CDEV_MAJOR 82
39
40static d_open_t hotplugopen;
41static d_close_t hotplugclose;
42static d_read_t hotplugread;
96b15e7f
SG
43static d_kqfilter_t hotplugkqfilter;
44
45static void hotplugfiltdetach(struct knote *);
46static int hotplugfilt(struct knote *, long);
115f9a72
AP
47
48static struct dev_ops hotplug_ops = {
d4b8aec4 49 { "hotplug", CDEV_MAJOR, 0 },
115f9a72
AP
50 .d_open = hotplugopen,
51 .d_close = hotplugclose,
52 .d_read = hotplugread,
96b15e7f 53 .d_kqfilter = hotplugkqfilter
115f9a72
AP
54};
55
56struct hotplug_event_info {
57 struct hotplug_event *he;
58 TAILQ_ENTRY(hotplug_event_info) hei_link;
59};
60
61TAILQ_HEAD(hpq, hotplug_event_info);
62
63static struct hotplug_softc
64{
65 cdev_t dev;
66 struct lock lock;
67 int opened;
68 int qcount;
69 struct hpq queue;
5b22f1a7 70 struct kqinfo kq;
115f9a72
AP
71 void (*old_devfs_node_added)(struct hotplug_device *hpdev);
72 void (*old_devfs_node_removed)(struct hotplug_device *hpdev);
73} hpsc;
74
75extern void (*devfs_node_added)(struct hotplug_device *hpdev);
76extern void (*devfs_node_removed)(struct hotplug_device *hpdev);
77
78void hotplug_devfs_node_added(struct hotplug_device *hpdev);
79void hotplug_devfs_node_removed(struct hotplug_device *hpdev);
80
81static int hotplug_get_event(struct hotplug_event *he);
82static int hotplug_put_event(struct hotplug_event *he);
83
84static int hotplug_uninit(void);
85static int hotplug_init(void);
86
87static int
88hotplugopen(struct dev_open_args *ap)
89{
90 if (hpsc.opened)
91 return (EBUSY);
92 hpsc.opened = 1;
93 return 0;
94}
95
96static int
97hotplugclose(struct dev_close_args *ap)
98{
99 hpsc.opened = 0;
100 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
101 wakeup(&hpsc);
102 lockmgr(&hpsc.lock, LK_RELEASE);
103 return 0;
104}
105
96b15e7f 106static struct filterops hotplugfiltops =
4c91dbc9 107 { FILTEROP_ISFD, NULL, hotplugfiltdetach, hotplugfilt };
96b15e7f
SG
108
109static int
110hotplugkqfilter(struct dev_kqfilter_args *ap)
111{
112 struct knote *kn = ap->a_kn;
113 struct klist *klist;
114
115 ap->a_result = 0;
116
117 switch (kn->kn_filter) {
118 case EVFILT_READ:
119 kn->kn_fop = &hotplugfiltops;
120 break;
121 default:
b287d649 122 ap->a_result = EOPNOTSUPP;
96b15e7f
SG
123 return (0);
124 }
125
126 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
5b22f1a7
SG
127 klist = &hpsc.kq.ki_note;
128 knote_insert(klist, kn);
96b15e7f
SG
129 lockmgr(&hpsc.lock, LK_RELEASE);
130
131 return (0);
132}
133
134static void
135hotplugfiltdetach(struct knote *kn)
136{
137 struct klist *klist;
138
139 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
5b22f1a7
SG
140 klist = &hpsc.kq.ki_note;
141 knote_remove(klist, kn);
96b15e7f
SG
142 lockmgr(&hpsc.lock, LK_RELEASE);
143}
144
145static int
146hotplugfilt(struct knote *kn, long hint)
147{
148 int ready = 0;
149
150 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
151 if (!TAILQ_EMPTY(&hpsc.queue))
152 ready = 1;
153 lockmgr(&hpsc.lock, LK_RELEASE);
154
155 return (ready);
156}
157
115f9a72
AP
158int
159hotplug_get_event(struct hotplug_event *he)
160{
161 struct hotplug_event_info *hei;
162
163 /* shouldn't get there */
164 if(TAILQ_EMPTY(&hpsc.queue))
165 return EINVAL;
166 hpsc.qcount--;
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);
173 return (0);
174}
175
176static int
177hotplugread(struct dev_read_args *ap)
178{
179 struct uio *uio = ap->a_uio;
180 struct hotplug_event *he;
181 int rv = EINVAL;
182
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);
188 if(rv) {
189 lockmgr(&hpsc.lock, LK_RELEASE);
190 return (rv);
191 }
192 }
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);
196 kfree(he, M_DEVBUF);
197 }
198 lockmgr(&hpsc.lock, LK_RELEASE);
199 return (rv);
200}
201
202static int
203hotplug_put_event(struct hotplug_event *he)
204{
205 struct hotplug_event_info *hei = NULL;
206
207 if (hpsc.qcount == HOTPLUG_MAXEVENTS && hpsc.opened) {
208 kprintf("hotplug: event lost, queue full\n");
209 return (1);
210 }
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);
216 hpsc.qcount++;
217 wakeup(&hpsc);
218 lockmgr(&hpsc.lock, LK_RELEASE);
5b22f1a7 219 KNOTE(&hpsc.kq.ki_note, 0);
115f9a72
AP
220 return (0);
221}
222
223void
224hotplug_devfs_node_added(struct hotplug_device *hpdev) {
225 struct hotplug_event he;
226 u_int class;
227 char *name;
228
229 if(!hpdev->dev || !hpsc.opened)
230 return;
231 class = hpdev->dev->si_ops->head.flags;
232 name = hpdev->name;
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);
237}
238
239void
240hotplug_devfs_node_removed(struct hotplug_device *hpdev) {
241 struct hotplug_event he;
242 u_int class;
243 char *name;
244
245 if(!hpdev->dev || !hpsc.opened)
246 return;
247 class = hpdev->dev->si_ops->head.flags;
248 name = hpdev->name;
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);
253}
254
255static int
256hotplug_init()
257{
258 hpsc.dev = make_dev(&hotplug_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hotplug");
259 hpsc.qcount = 0;
260 lockinit(&hpsc.lock, "hotplug mtx", 0, 0);
261 TAILQ_INIT(&hpsc.queue);
262 /* setup handlers */
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;
267 return 0;
268}
269
270static int
271hotplug_uninit()
272{
273 struct hotplug_event_info *hei;
274
275 if(hpsc.opened)
276 return EBUSY;
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);
284 }
285
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);
290 return 0;
291}
292
293static int
294hotplug_modevh(struct module *m, int what, void *arg __unused)
295{
296 int error;
297
298 switch (what) {
299 case MOD_LOAD:
300 error = hotplug_init();
301 break;
302 case MOD_UNLOAD:
303 error = hotplug_uninit();
304 break;
305 default:
306 error = EINVAL;
307 break;
308 }
309 return (error);
310}
311
312static moduledata_t hotplug_mod = {
313 "hotplug",
314 hotplug_modevh,
315 NULL,
316};
317
318DECLARE_MODULE(hotplug, hotplug_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);