Pass hotplug_device, not cdev_t.
[dragonfly.git] / sys / dev / misc / hotplug / hotplug.c
CommitLineData
c6289b98
AP
1#include <sys/conf.h>
2#include <sys/param.h>
3#include <sys/systm.h>
4#include <sys/malloc.h>
5#include <sys/kernel.h>
6#include <sys/module.h>
7#include <sys/sysctl.h>
8#include <sys/device.h>
9#include <sys/lock.h>
10#include <sys/selinfo.h>
11#include <sys/poll.h>
12#include <sys/uio.h>
13#include <sys/thread.h>
14#include <sys/thread2.h>
15#include <sys/hotplug.h>
16
17#define HOTPLUG_MAXEVENTS 16
18
19#define CDEV_MAJOR 82
20
21static d_open_t hotplugopen;
22static d_close_t hotplugclose;
23static d_read_t hotplugread;
24static d_poll_t hotplugpoll;
25
26static struct dev_ops hotplug_ops = {
27 { "hotplug", CDEV_MAJOR, 0 },
28 .d_open = hotplugopen,
29 .d_close = hotplugclose,
30 .d_read = hotplugread,
31 .d_poll = hotplugpoll,
32};
33
34struct hotplug_event_info {
35 struct hotplug_event *he;
36 TAILQ_ENTRY(hotplug_event_info) hei_link;
37};
38
39TAILQ_HEAD(hpq, hotplug_event_info);
40
41static struct hotplug_softc
42{
43 cdev_t dev;
44 struct lock lock;
45 int opened;
46 int qcount;
47 struct hpq queue;
48 struct selinfo sel;
e3bf5370
AP
49 void (*old_devfs_node_added)(struct hotplug_device *hpdev);
50 void (*old_devfs_node_removed)(struct hotplug_device *hpdev);
c6289b98
AP
51} hpsc;
52
e3bf5370
AP
53extern void (*devfs_node_added)(struct hotplug_device *hpdev);
54extern void (*devfs_node_removed)(struct hotplug_device *hpdev);
c6289b98 55
e3bf5370
AP
56void hotplug_devfs_node_added(struct hotplug_device *hpdev);
57void hotplug_devfs_node_removed(struct hotplug_device *hpdev);
c6289b98
AP
58
59static int hotplug_get_event(struct hotplug_event *he);
60static int hotplug_put_event(struct hotplug_event *he);
61
62static int hotplug_uninit(void);
63static int hotplug_init(void);
64
65static int
66hotplugopen(struct dev_open_args *ap)
67{
68 if (hpsc.opened)
69 return (EBUSY);
70 hpsc.opened = 1;
71 return 0;
72}
73
74static int
75hotplugclose(struct dev_close_args *ap)
76{
77 hpsc.opened = 0;
78 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
79 wakeup(&hpsc);
80 lockmgr(&hpsc.lock, LK_RELEASE);
81 return 0;
82}
83
84static int
85hotplugpoll(struct dev_poll_args *ap)
86{
87 int revents = 0;
88
89 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
90 if (ap->a_events & (POLLIN | POLLRDNORM)) {
91 if (!TAILQ_EMPTY(&hpsc.queue))
92 revents = ap->a_events & (POLLIN | POLLRDNORM);
93 else
94 selrecord(curthread, &hpsc.sel);
95 }
96 lockmgr(&hpsc.lock, LK_RELEASE);
97
98 ap->a_events = revents;
99 return (0);
100}
101
102int
103hotplug_get_event(struct hotplug_event *he)
104{
105 struct hotplug_event_info *hei;
106
107 /* shouldn't get there */
108 if(TAILQ_EMPTY(&hpsc.queue))
109 return EINVAL;
110 hpsc.qcount--;
111 /* we are under hotplugread() lock here */
112 hei = TAILQ_FIRST(&hpsc.queue);
113 memcpy(he, hei->he, sizeof(struct hotplug_event));
114 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
115 kfree(hei->he, M_DEVBUF);
116 kfree(hei, M_DEVBUF);
117 return (0);
118}
119
120static int
121hotplugread(struct dev_read_args *ap)
122{
123 struct uio *uio = ap->a_uio;
124 struct hotplug_event *he;
125 int rv = EINVAL;
126
127 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
128 while(TAILQ_EMPTY(&hpsc.queue)) {
129 tsleep_interlock(&hpsc, PCATCH);
130 lockmgr(&hpsc.lock, LK_RELEASE);
131 rv = tsleep(&hpsc, PCATCH | PINTERLOCKED, "hotplug", 0);
132 if(rv) {
133 lockmgr(&hpsc.lock, LK_RELEASE);
134 return (rv);
135 }
136 }
137 he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
138 if(hotplug_get_event(he) == 0) {
139 rv = uiomove((caddr_t)he, sizeof(struct hotplug_event), uio);
140 kfree(he, M_DEVBUF);
141 }
142 lockmgr(&hpsc.lock, LK_RELEASE);
143 return (rv);
144}
145
146static int
147hotplug_put_event(struct hotplug_event *he)
148{
149 struct hotplug_event_info *hei = NULL;
150
151 if (hpsc.qcount == HOTPLUG_MAXEVENTS && hpsc.opened) {
152 kprintf("hotplug: event lost, queue full\n");
153 return (1);
154 }
155 hei = kmalloc(sizeof(struct hotplug_event_info), M_DEVBUF, M_WAITOK);
156 hei->he = kmalloc(sizeof(struct hotplug_event), M_DEVBUF, M_WAITOK);
157 memcpy(hei->he, he, sizeof(struct hotplug_event));
158 lockmgr(&hpsc.lock, LK_EXCLUSIVE);
159 TAILQ_INSERT_TAIL(&hpsc.queue, hei, hei_link);
160 hpsc.qcount++;
161 wakeup(&hpsc);
162 lockmgr(&hpsc.lock, LK_RELEASE);
163 selwakeup(&hpsc.sel);
164 return (0);
165}
166
167void
e3bf5370 168hotplug_devfs_node_added(struct hotplug_device *hpdev) {
c6289b98
AP
169 struct hotplug_event he;
170 u_int class;
171 char *name;
172
e3bf5370 173 if(!hpdev->dev || !hpsc.opened)
c6289b98 174 return;
e3bf5370
AP
175 class = hpdev->dev->si_ops->head.flags;
176 name = hpdev->name;
c6289b98
AP
177 he.he_type = HOTPLUG_DEVAT;
178 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
179 strlcpy(he.he_devname, name, sizeof(he.he_devname));
180 hotplug_put_event(&he);
181}
182
183void
e3bf5370 184hotplug_devfs_node_removed(struct hotplug_device *hpdev) {
c6289b98
AP
185 struct hotplug_event he;
186 u_int class;
187 char *name;
188
e3bf5370 189 if(!hpdev->dev || !hpsc.opened)
c6289b98 190 return;
e3bf5370
AP
191 class = hpdev->dev->si_ops->head.flags;
192 name = hpdev->name;
c6289b98
AP
193 he.he_type = HOTPLUG_DEVDT;
194 he.he_devclass = ((class == D_TTY) ? DV_TTY : ((class == D_TAPE) ? DV_TAPE : ((class == D_DISK) ? DV_DISK : DV_DULL)));
195 strlcpy(he.he_devname, name, sizeof(he.he_devname));
196 hotplug_put_event(&he);
197}
198
199static int
200hotplug_init()
201{
202 hpsc.dev = make_dev(&hotplug_ops, 0, UID_ROOT, GID_WHEEL, 0600, "hotplug");
203 hpsc.qcount = 0;
204 lockinit(&hpsc.lock, "hotplug mtx", 0, 0);
205 TAILQ_INIT(&hpsc.queue);
206 /* setup handlers */
207 hpsc.old_devfs_node_added = devfs_node_added;
208 hpsc.old_devfs_node_removed = devfs_node_removed;
209 devfs_node_added = hotplug_devfs_node_added;
210 devfs_node_removed = hotplug_devfs_node_removed;
211 return 0;
212}
213
214static int
215hotplug_uninit()
216{
217 struct hotplug_event_info *hei;
218
219 if(hpsc.opened)
220 return EBUSY;
221 devfs_node_added = hpsc.old_devfs_node_added;
222 devfs_node_removed = hpsc.old_devfs_node_removed;
223 /* Free the entire tail queue. */
224 while ((hei = TAILQ_FIRST(&hpsc.queue))) {
225 TAILQ_REMOVE(&hpsc.queue, hei, hei_link);
226 kfree(hei->he, M_DEVBUF);
227 kfree(hei, M_DEVBUF);
228 }
229
230 /* The tail queue should now be empty. */
231 if (!TAILQ_EMPTY(&hpsc.queue))
232 kprintf("hotplug: queue not empty!\n");
233 destroy_dev(hpsc.dev);
234 return 0;
235}
236
237static int
238hotplug_modevh(struct module *m, int what, void *arg __unused)
239{
240 int error;
241
242 switch (what) {
243 case MOD_LOAD:
244 error = hotplug_init();
245 break;
246 case MOD_UNLOAD:
247 error = hotplug_uninit();
248 break;
249 default:
250 error = EINVAL;
251 break;
252 }
253 return (error);
254}
255
256static moduledata_t hotplug_mod = {
257 "hotplug",
258 hotplug_modevh,
259 NULL,
260};
261
262DECLARE_MODULE(hotplug, hotplug_mod, SI_SUB_PSEUDO, SI_ORDER_ANY);