jcs's openbsd hax
openbsd
1/* $OpenBSD: hotplug.c,v 1.25 2024/12/30 02:46:00 guenther Exp $ */
2/*
3 * Copyright (c) 2004 Alexander Yurchenko <grange@openbsd.org>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18/*
19 * Device attachment and detachment notifications.
20 */
21
22#include <sys/param.h>
23#include <sys/systm.h>
24#include <sys/device.h>
25#include <sys/event.h>
26#include <sys/fcntl.h>
27#include <sys/hotplug.h>
28#include <sys/ioctl.h>
29#include <sys/mutex.h>
30#include <sys/vnode.h>
31
32#define HOTPLUG_MAXEVENTS 64
33
34/*
35 * Locks used to protect struct members and global data
36 * M hotplug_mtx
37 */
38
39static struct mutex hotplug_mtx = MUTEX_INITIALIZER(IPL_MPFLOOR);
40
41static int opened;
42static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
43static int evqueue_head, evqueue_tail, evqueue_count; /* [M] */
44static struct klist hotplug_klist; /* [M] */
45
46void filt_hotplugrdetach(struct knote *);
47int filt_hotplugread(struct knote *, long);
48int filt_hotplugmodify(struct kevent *, struct knote *);
49int filt_hotplugprocess(struct knote *, struct kevent *);
50
51const struct filterops hotplugread_filtops = {
52 .f_flags = FILTEROP_ISFD | FILTEROP_MPSAFE,
53 .f_attach = NULL,
54 .f_detach = filt_hotplugrdetach,
55 .f_event = filt_hotplugread,
56 .f_modify = filt_hotplugmodify,
57 .f_process = filt_hotplugprocess,
58};
59
60#define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
61
62
63int hotplug_put_event(struct hotplug_event *);
64int hotplug_get_event(struct hotplug_event *);
65
66void hotplugattach(int);
67
68void
69hotplugattach(int count)
70{
71 opened = 0;
72 evqueue_head = 0;
73 evqueue_tail = 0;
74 evqueue_count = 0;
75
76 klist_init_mutex(&hotplug_klist, &hotplug_mtx);
77}
78
79void
80hotplug_device_attach(enum devclass class, char *name)
81{
82 struct hotplug_event he;
83
84 he.he_type = HOTPLUG_DEVAT;
85 he.he_devclass = class;
86 strlcpy(he.he_devname, name, sizeof(he.he_devname));
87 hotplug_put_event(&he);
88}
89
90void
91hotplug_device_detach(enum devclass class, char *name)
92{
93 struct hotplug_event he;
94
95 he.he_type = HOTPLUG_DEVDT;
96 he.he_devclass = class;
97 strlcpy(he.he_devname, name, sizeof(he.he_devname));
98 hotplug_put_event(&he);
99}
100
101int
102hotplug_put_event(struct hotplug_event *he)
103{
104 mtx_enter(&hotplug_mtx);
105 if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
106 mtx_leave(&hotplug_mtx);
107 printf("hotplug: event lost, queue full\n");
108 return (1);
109 }
110
111 evqueue[evqueue_head] = *he;
112 evqueue_head = EVQUEUE_NEXT(evqueue_head);
113 if (evqueue_count == HOTPLUG_MAXEVENTS)
114 evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
115 else
116 evqueue_count++;
117 knote_locked(&hotplug_klist, 0);
118 wakeup(&evqueue);
119 mtx_leave(&hotplug_mtx);
120 return (0);
121}
122
123int
124hotplug_get_event(struct hotplug_event *he)
125{
126 if (evqueue_count == 0)
127 return (1);
128
129 *he = evqueue[evqueue_tail];
130 evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
131 evqueue_count--;
132 return (0);
133}
134
135int
136hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
137{
138 if (minor(dev) != 0)
139 return (ENXIO);
140 if ((flag & FWRITE))
141 return (EPERM);
142 if (opened)
143 return (EBUSY);
144 opened = 1;
145 return (0);
146}
147
148int
149hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
150{
151 struct hotplug_event he;
152
153 mtx_enter(&hotplug_mtx);
154 while (hotplug_get_event(&he) == 0)
155 continue;
156 mtx_leave(&hotplug_mtx);
157 klist_invalidate(&hotplug_klist);
158 opened = 0;
159 return (0);
160}
161
162int
163hotplugread(dev_t dev, struct uio *uio, int flags)
164{
165 struct hotplug_event he;
166 int error;
167
168 if (uio->uio_resid != sizeof(he))
169 return (EINVAL);
170
171 mtx_enter(&hotplug_mtx);
172 while (hotplug_get_event(&he)) {
173 if (flags & IO_NDELAY) {
174 mtx_leave(&hotplug_mtx);
175 return (EAGAIN);
176 }
177
178 error = msleep_nsec(&evqueue, &hotplug_mtx, PRIBIO | PCATCH,
179 "htplev", INFSLP);
180 if (error) {
181 mtx_leave(&hotplug_mtx);
182 return (error);
183 }
184 }
185 mtx_leave(&hotplug_mtx);
186
187 return (uiomove(&he, sizeof(he), uio));
188}
189
190int
191hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
192{
193 switch (cmd) {
194 case FIOASYNC:
195 /* ignore */
196 default:
197 return (ENOTTY);
198 }
199
200 return (0);
201}
202
203int
204hotplugkqfilter(dev_t dev, struct knote *kn)
205{
206 switch (kn->kn_filter) {
207 case EVFILT_READ:
208 kn->kn_fop = &hotplugread_filtops;
209 break;
210 default:
211 return (EINVAL);
212 }
213
214 klist_insert(&hotplug_klist, kn);
215 return (0);
216}
217
218void
219filt_hotplugrdetach(struct knote *kn)
220{
221 klist_remove(&hotplug_klist, kn);
222}
223
224int
225filt_hotplugread(struct knote *kn, long hint)
226{
227 kn->kn_data = evqueue_count;
228
229 return (evqueue_count > 0);
230}
231
232int
233filt_hotplugmodify(struct kevent *kev, struct knote *kn)
234{
235 int active;
236
237 mtx_enter(&hotplug_mtx);
238 active = knote_modify(kev, kn);
239 mtx_leave(&hotplug_mtx);
240
241 return (active);
242}
243
244int
245filt_hotplugprocess(struct knote *kn, struct kevent *kev)
246{
247 int active;
248
249 mtx_enter(&hotplug_mtx);
250 active = knote_process(kn, kev);
251 mtx_leave(&hotplug_mtx);
252
253 return (active);
254}