FreeBSD/Linux Kernel Cross Reference
sys/dev/hotplug.c
1 /* $OpenBSD: hotplug.c,v 1.22 2022/07/02 08:50:41 visa 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/fcntl.h>
26 #include <sys/hotplug.h>
27 #include <sys/ioctl.h>
28 #include <sys/vnode.h>
29
30 #define HOTPLUG_MAXEVENTS 64
31
32 static int opened;
33 static struct hotplug_event evqueue[HOTPLUG_MAXEVENTS];
34 static int evqueue_head, evqueue_tail, evqueue_count;
35 static struct selinfo hotplug_sel;
36
37 void filt_hotplugrdetach(struct knote *);
38 int filt_hotplugread(struct knote *, long);
39
40 const struct filterops hotplugread_filtops = {
41 .f_flags = FILTEROP_ISFD,
42 .f_attach = NULL,
43 .f_detach = filt_hotplugrdetach,
44 .f_event = filt_hotplugread,
45 };
46
47 #define EVQUEUE_NEXT(p) (p == HOTPLUG_MAXEVENTS - 1 ? 0 : p + 1)
48
49
50 int hotplug_put_event(struct hotplug_event *);
51 int hotplug_get_event(struct hotplug_event *);
52
53 void hotplugattach(int);
54
55 void
56 hotplugattach(int count)
57 {
58 opened = 0;
59 evqueue_head = 0;
60 evqueue_tail = 0;
61 evqueue_count = 0;
62 }
63
64 void
65 hotplug_device_attach(enum devclass class, char *name)
66 {
67 struct hotplug_event he;
68
69 he.he_type = HOTPLUG_DEVAT;
70 he.he_devclass = class;
71 strlcpy(he.he_devname, name, sizeof(he.he_devname));
72 hotplug_put_event(&he);
73 }
74
75 void
76 hotplug_device_detach(enum devclass class, char *name)
77 {
78 struct hotplug_event he;
79
80 he.he_type = HOTPLUG_DEVDT;
81 he.he_devclass = class;
82 strlcpy(he.he_devname, name, sizeof(he.he_devname));
83 hotplug_put_event(&he);
84 }
85
86 int
87 hotplug_put_event(struct hotplug_event *he)
88 {
89 if (evqueue_count == HOTPLUG_MAXEVENTS && opened) {
90 printf("hotplug: event lost, queue full\n");
91 return (1);
92 }
93
94 evqueue[evqueue_head] = *he;
95 evqueue_head = EVQUEUE_NEXT(evqueue_head);
96 if (evqueue_count == HOTPLUG_MAXEVENTS)
97 evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
98 else
99 evqueue_count++;
100 wakeup(&evqueue);
101 selwakeup(&hotplug_sel);
102 return (0);
103 }
104
105 int
106 hotplug_get_event(struct hotplug_event *he)
107 {
108 int s;
109
110 if (evqueue_count == 0)
111 return (1);
112
113 s = splbio();
114 *he = evqueue[evqueue_tail];
115 evqueue_tail = EVQUEUE_NEXT(evqueue_tail);
116 evqueue_count--;
117 splx(s);
118 return (0);
119 }
120
121 int
122 hotplugopen(dev_t dev, int flag, int mode, struct proc *p)
123 {
124 if (minor(dev) != 0)
125 return (ENXIO);
126 if ((flag & FWRITE))
127 return (EPERM);
128 if (opened)
129 return (EBUSY);
130 opened = 1;
131 return (0);
132 }
133
134 int
135 hotplugclose(dev_t dev, int flag, int mode, struct proc *p)
136 {
137 struct hotplug_event he;
138
139 while (hotplug_get_event(&he) == 0)
140 continue;
141 opened = 0;
142 return (0);
143 }
144
145 int
146 hotplugread(dev_t dev, struct uio *uio, int flags)
147 {
148 struct hotplug_event he;
149 int error;
150
151 if (uio->uio_resid != sizeof(he))
152 return (EINVAL);
153
154 again:
155 if (hotplug_get_event(&he) == 0)
156 return (uiomove(&he, sizeof(he), uio));
157 if (flags & IO_NDELAY)
158 return (EAGAIN);
159
160 error = tsleep_nsec(&evqueue, PRIBIO | PCATCH, "htplev", INFSLP);
161 if (error)
162 return (error);
163 goto again;
164 }
165
166 int
167 hotplugioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
168 {
169 switch (cmd) {
170 case FIOASYNC:
171 /* ignore */
172 case FIONBIO:
173 /* handled in the upper fs layer */
174 break;
175 default:
176 return (ENOTTY);
177 }
178
179 return (0);
180 }
181
182 int
183 hotplugkqfilter(dev_t dev, struct knote *kn)
184 {
185 struct klist *klist;
186 int s;
187
188 switch (kn->kn_filter) {
189 case EVFILT_READ:
190 klist = &hotplug_sel.si_note;
191 kn->kn_fop = &hotplugread_filtops;
192 break;
193 default:
194 return (EINVAL);
195 }
196
197 s = splbio();
198 klist_insert_locked(klist, kn);
199 splx(s);
200 return (0);
201 }
202
203 void
204 filt_hotplugrdetach(struct knote *kn)
205 {
206 int s;
207
208 s = splbio();
209 klist_remove_locked(&hotplug_sel.si_note, kn);
210 splx(s);
211 }
212
213 int
214 filt_hotplugread(struct knote *kn, long hint)
215 {
216 kn->kn_data = evqueue_count;
217
218 return (evqueue_count > 0);
219 }
Cache object: d661713dbbbc43180f8a6b07c1126455
|