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