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