1 /* $OpenBSD: vldcp.c,v 1.26 2024/05/14 08:26:13 jsg Exp $ */
2 /*
3 * Copyright (c) 2009, 2012 Mark Kettenis
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 #include <sys/param.h>
19 #include <sys/conf.h>
20 #include <sys/device.h>
21 #include <sys/malloc.h>
22 #include <sys/proc.h>
23 #include <sys/systm.h>
24
25 #include <machine/autoconf.h>
26 #include <machine/hypervisor.h>
27 #include <machine/mdesc.h>
28
29 #include <uvm/uvm_extern.h>
30
31 #include <sparc64/dev/cbusvar.h>
32 #include <sparc64/dev/ldcvar.h>
33
34 #ifdef VLDCP_DEBUG
35 #define DPRINTF(x) printf x
36 #else
37 #define DPRINTF(x)
38 #endif
39
40 #include <sys/ioccom.h>
41
42 struct hv_io {
43 uint64_t hi_cookie;
44 void *hi_addr;
45 size_t hi_len;
46 };
47
48 #define HVIOCREAD _IOW('h', 0, struct hv_io)
49 #define HVIOCWRITE _IOW('h', 1, struct hv_io)
50
51 #define VLDCP_TX_ENTRIES 128
52 #define VLDCP_RX_ENTRIES 128
53
54 struct vldcp_softc {
55 struct device sc_dv;
56 bus_space_tag_t sc_bustag;
57 bus_dma_tag_t sc_dmatag;
58
59 uint64_t sc_tx_ino;
60 uint64_t sc_rx_ino;
61 void *sc_tx_ih;
62 void *sc_rx_ih;
63
64 struct ldc_conn sc_lc;
65
66 struct selinfo sc_rsel;
67 struct selinfo sc_wsel;
68 };
69
70 int vldcp_match(struct device *, void *, void *);
71 void vldcp_attach(struct device *, struct device *, void *);
72 void filt_vldcprdetach(struct knote *);
73 void filt_vldcpwdetach(struct knote *);
74 int filt_vldcpread(struct knote *, long);
75 int vldcpkqfilter(dev_t, struct knote *);
76
77 const struct cfattach vldcp_ca = {
78 sizeof(struct vldcp_softc), vldcp_match, vldcp_attach
79 };
80
81 struct cfdriver vldcp_cd = {
82 NULL, "vldcp", DV_DULL
83 };
84
85 int vldcp_tx_intr(void *);
86 int vldcp_rx_intr(void *);
87
88 /*
89 * We attach to certain well-known channels. These are assigned fixed
90 * device minor device numbers through their index in the table below.
91 * So "hvctl" gets minor 0, "spds" gets minor 1, etc. etc.
92 *
93 * We also attach to the domain services channels. These are named
94 * "ldom-<guestname>" and get assigned a device minor starting at
95 * VLDC_LDOM_OFFSET.
96 */
97 #define VLDC_NUM_SERVICES 64
98 #define VLDC_LDOM_OFFSET 32
99 int vldc_num_ldoms;
100
101 struct vldc_svc {
102 const char *vs_name;
103 struct vldcp_softc *vs_sc;
104 };
105
106 struct vldc_svc vldc_svc[VLDC_NUM_SERVICES] = {
107 { "hvctl" },
108 { "spds" },
109 { NULL }
110 };
111
112 int
vldcp_match(struct device * parent,void * match,void * aux)113 vldcp_match(struct device *parent, void *match, void *aux)
114 {
115 struct cbus_attach_args *ca = aux;
116 struct vldc_svc *svc;
117
118 for (svc = vldc_svc; svc->vs_name != NULL; svc++)
119 if (strcmp(ca->ca_name, svc->vs_name) == 0)
120 return (1);
121
122 if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
123 strcmp(ca->ca_name, "ldom-primary") != 0)
124 return (1);
125
126 return (0);
127 }
128
129 void
vldcp_attach(struct device * parent,struct device * self,void * aux)130 vldcp_attach(struct device *parent, struct device *self, void *aux)
131 {
132 struct vldcp_softc *sc = (struct vldcp_softc *)self;
133 struct cbus_attach_args *ca = aux;
134 struct vldc_svc *svc;
135 struct ldc_conn *lc;
136
137 sc->sc_bustag = ca->ca_bustag;
138 sc->sc_dmatag = ca->ca_dmatag;
139 sc->sc_tx_ino = ca->ca_tx_ino;
140 sc->sc_rx_ino = ca->ca_rx_ino;
141
142 printf(": ivec 0x%llx, 0x%llx", sc->sc_tx_ino, sc->sc_rx_ino);
143
144 /*
145 * Un-configure queues before registering interrupt handlers,
146 * such that we dont get any stale LDC packets or events.
147 */
148 hv_ldc_tx_qconf(ca->ca_id, 0, 0);
149 hv_ldc_rx_qconf(ca->ca_id, 0, 0);
150
151 sc->sc_tx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_tx_ino,
152 IPL_TTY, 0, vldcp_tx_intr, sc, sc->sc_dv.dv_xname);
153 sc->sc_rx_ih = bus_intr_establish(ca->ca_bustag, sc->sc_rx_ino,
154 IPL_TTY, 0, vldcp_rx_intr, sc, sc->sc_dv.dv_xname);
155 if (sc->sc_tx_ih == NULL || sc->sc_rx_ih == NULL) {
156 printf(", can't establish interrupt\n");
157 return;
158 }
159
160 lc = &sc->sc_lc;
161 lc->lc_id = ca->ca_id;
162 lc->lc_sc = sc;
163
164 lc->lc_txq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_TX_ENTRIES);
165 if (lc->lc_txq == NULL) {
166 printf(", can't allocate tx queue\n");
167 return;
168 }
169
170 lc->lc_rxq = ldc_queue_alloc(sc->sc_dmatag, VLDCP_RX_ENTRIES);
171 if (lc->lc_rxq == NULL) {
172 printf(", can't allocate rx queue\n");
173 goto free_txqueue;
174 }
175 lc->lc_rx_state = LDC_CHANNEL_INIT;
176
177 for (svc = vldc_svc; svc->vs_name != NULL; svc++) {
178 if (strcmp(ca->ca_name, svc->vs_name) == 0) {
179 svc->vs_sc = sc;
180 break;
181 }
182 }
183
184 if (strncmp(ca->ca_name, "ldom-", 5) == 0 &&
185 strcmp(ca->ca_name, "ldom-primary") != 0) {
186 int minor = VLDC_LDOM_OFFSET + vldc_num_ldoms++;
187 if (minor < nitems(vldc_svc))
188 vldc_svc[minor].vs_sc = sc;
189 }
190
191 printf(" channel \"%s\"\n", ca->ca_name);
192 return;
193
194 #if 0
195 free_rxqueue:
196 ldc_queue_free(sc->sc_dmatag, lc->lc_rxq);
197 #endif
198 free_txqueue:
199 ldc_queue_free(sc->sc_dmatag, lc->lc_txq);
200 }
201
202 int
vldcp_tx_intr(void * arg)203 vldcp_tx_intr(void *arg)
204 {
205 struct vldcp_softc *sc = arg;
206 struct ldc_conn *lc = &sc->sc_lc;
207 uint64_t tx_head, tx_tail, tx_state;
208 int err;
209
210 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
211 if (err != H_EOK) {
212 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
213 return (0);
214 }
215
216 if (tx_state != lc->lc_tx_state) {
217 switch (tx_state) {
218 case LDC_CHANNEL_DOWN:
219 DPRINTF(("%s: Tx link down\n", __func__));
220 break;
221 case LDC_CHANNEL_UP:
222 DPRINTF(("%s: Tx link up\n", __func__));
223 break;
224 case LDC_CHANNEL_RESET:
225 DPRINTF(("%s: Tx link reset\n", __func__));
226 break;
227 }
228 lc->lc_tx_state = tx_state;
229 }
230
231 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
232 selwakeup(&sc->sc_wsel);
233 wakeup(lc->lc_txq);
234 return (1);
235 }
236
237 int
vldcp_rx_intr(void * arg)238 vldcp_rx_intr(void *arg)
239 {
240 struct vldcp_softc *sc = arg;
241 struct ldc_conn *lc = &sc->sc_lc;
242 uint64_t rx_head, rx_tail, rx_state;
243 int err;
244
245 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
246 if (err != H_EOK) {
247 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
248 return (0);
249 }
250
251 if (rx_state != lc->lc_rx_state) {
252 switch (rx_state) {
253 case LDC_CHANNEL_DOWN:
254 DPRINTF(("%s: Rx link down\n", __func__));
255 if (rx_head == rx_tail)
256 break;
257 /* Discard and ack pending I/O. */
258 DPRINTF(("setting rx qhead to %llx\n", rx_tail));
259 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
260 if (err == H_EOK)
261 break;
262 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
263 break;
264 case LDC_CHANNEL_UP:
265 DPRINTF(("%s: Rx link up\n", __func__));
266 break;
267 case LDC_CHANNEL_RESET:
268 DPRINTF(("%s: Rx link reset\n", __func__));
269 if (rx_head == rx_tail)
270 break;
271 /* Discard and ack pending I/O. */
272 DPRINTF(("setting rx qhead to %llx\n", rx_tail));
273 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_tail);
274 if (err == H_EOK)
275 break;
276 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
277 break;
278 }
279 lc->lc_rx_state = rx_state;
280 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
281 INTR_DISABLED);
282 selwakeup(&sc->sc_rsel);
283 wakeup(lc->lc_rxq);
284 return (1);
285 }
286
287 if (rx_head == rx_tail)
288 return (0);
289
290 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
291 selwakeup(&sc->sc_rsel);
292 wakeup(lc->lc_rxq);
293 return (1);
294 }
295
296 cdev_decl(vldcp);
297 struct vldcp_softc *vldcp_lookup(dev_t);
298
299 struct vldcp_softc *
vldcp_lookup(dev_t dev)300 vldcp_lookup(dev_t dev)
301 {
302 struct vldcp_softc *sc = NULL;
303
304 if (minor(dev) < nitems(vldc_svc))
305 sc = vldc_svc[minor(dev)].vs_sc;
306
307 if (sc)
308 device_ref(&sc->sc_dv);
309
310 return (sc);
311 }
312
313 int
vldcpopen(dev_t dev,int flag,int mode,struct proc * p)314 vldcpopen(dev_t dev, int flag, int mode, struct proc *p)
315 {
316 struct vldcp_softc *sc;
317 struct ldc_conn *lc;
318 uint64_t rx_head, rx_tail, rx_state;
319 int err;
320
321 sc = vldcp_lookup(dev);
322 if (sc == NULL)
323 return (ENXIO);
324 lc = &sc->sc_lc;
325
326 err = hv_ldc_tx_qconf(lc->lc_id,
327 lc->lc_txq->lq_map->dm_segs[0].ds_addr, lc->lc_txq->lq_nentries);
328 if (err != H_EOK)
329 printf("%s: hv_ldc_tx_qconf %d\n", __func__, err);
330
331 err = hv_ldc_rx_qconf(lc->lc_id,
332 lc->lc_rxq->lq_map->dm_segs[0].ds_addr, lc->lc_rxq->lq_nentries);
333 if (err != H_EOK)
334 printf("%s: hv_ldc_rx_qconf %d\n", __func__, err);
335
336 /* Clear a pending channel reset. */
337 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
338 if (err != H_EOK)
339 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
340
341 device_unref(&sc->sc_dv);
342 return (0);
343 }
344
345 int
vldcpclose(dev_t dev,int flag,int mode,struct proc * p)346 vldcpclose(dev_t dev, int flag, int mode, struct proc *p)
347 {
348 struct vldcp_softc *sc;
349
350 sc = vldcp_lookup(dev);
351 if (sc == NULL)
352 return (ENXIO);
353
354 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino, INTR_DISABLED);
355 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino, INTR_DISABLED);
356
357 hv_ldc_tx_qconf(sc->sc_lc.lc_id, 0, 0);
358 hv_ldc_rx_qconf(sc->sc_lc.lc_id, 0, 0);
359
360 device_unref(&sc->sc_dv);
361 return (0);
362 }
363
364 int
vldcpread(dev_t dev,struct uio * uio,int ioflag)365 vldcpread(dev_t dev, struct uio *uio, int ioflag)
366 {
367 struct vldcp_softc *sc;
368 struct ldc_conn *lc;
369 uint64_t rx_head, rx_tail, rx_state;
370 int err, ret;
371 int s;
372
373 sc = vldcp_lookup(dev);
374 if (sc == NULL)
375 return (ENXIO);
376 lc = &sc->sc_lc;
377
378 if (uio->uio_resid != 64) {
379 device_unref(&sc->sc_dv);
380 return (EINVAL);
381 }
382
383 s = spltty();
384 retry:
385 err = hv_ldc_rx_get_state(lc->lc_id, &rx_head, &rx_tail, &rx_state);
386 if (err != H_EOK) {
387 splx(s);
388 printf("%s: hv_ldc_rx_get_state %d\n", __func__, err);
389 device_unref(&sc->sc_dv);
390 return (EIO);
391 }
392
393 DPRINTF(("rx head %llx, rx tail %llx, state %lld\n", rx_head, rx_tail, rx_state));
394
395 if (rx_state != LDC_CHANNEL_UP) {
396 splx(s);
397 device_unref(&sc->sc_dv);
398 return (EIO);
399 }
400
401 if (rx_head == rx_tail) {
402 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
403 INTR_ENABLED);
404 ret = tsleep_nsec(lc->lc_rxq, PWAIT | PCATCH, "hvrd", INFSLP);
405 if (ret) {
406 splx(s);
407 device_unref(&sc->sc_dv);
408 return (ret);
409 }
410 goto retry;
411 }
412
413 ret = uiomove(lc->lc_rxq->lq_va + rx_head, 64, uio);
414
415 rx_head += 64;
416 rx_head &= ((lc->lc_rxq->lq_nentries * 64) - 1);
417 err = hv_ldc_rx_set_qhead(lc->lc_id, rx_head);
418 if (err != H_EOK)
419 printf("%s: hv_ldc_rx_set_qhead %d\n", __func__, err);
420
421 splx(s);
422 device_unref(&sc->sc_dv);
423 return (ret);
424 }
425
426 int
vldcpwrite(dev_t dev,struct uio * uio,int ioflag)427 vldcpwrite(dev_t dev, struct uio *uio, int ioflag)
428 {
429 struct vldcp_softc *sc;
430 struct ldc_conn *lc;
431 uint64_t tx_head, tx_tail, tx_state;
432 uint64_t next_tx_tail;
433 int err, ret;
434 int s;
435
436 sc = vldcp_lookup(dev);
437 if (sc == NULL)
438 return (ENXIO);
439 lc = &sc->sc_lc;
440
441 if (uio->uio_resid != 64) {
442 device_unref(&sc->sc_dv);
443 return (EINVAL);
444 }
445
446 s = spltty();
447 retry:
448 err = hv_ldc_tx_get_state(lc->lc_id, &tx_head, &tx_tail, &tx_state);
449 if (err != H_EOK) {
450 splx(s);
451 printf("%s: hv_ldc_tx_get_state %d\n", __func__, err);
452 device_unref(&sc->sc_dv);
453 return (EIO);
454 }
455
456 if (tx_state != LDC_CHANNEL_UP) {
457 splx(s);
458 device_unref(&sc->sc_dv);
459 return (EIO);
460 }
461
462 DPRINTF(("tx head %llx, tx tail %llx\n", tx_head, tx_tail));
463
464 next_tx_tail = tx_tail + 64;
465 next_tx_tail &= ((lc->lc_txq->lq_nentries * 64) - 1);
466
467 if (tx_head == next_tx_tail) {
468 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
469 INTR_ENABLED);
470 ret = tsleep_nsec(lc->lc_txq, PWAIT | PCATCH, "hvwr", INFSLP);
471 if (ret) {
472 splx(s);
473 device_unref(&sc->sc_dv);
474 return (ret);
475 }
476 goto retry;
477 }
478 splx(s);
479
480 ret = uiomove(lc->lc_txq->lq_va + tx_tail, 64, uio);
481
482 err = hv_ldc_tx_set_qtail(lc->lc_id, next_tx_tail);
483 if (err != H_EOK) {
484 printf("%s: hv_ldc_tx_set_qtail: %d\n", __func__, err);
485 device_unref(&sc->sc_dv);
486 return (EIO);
487 }
488
489 device_unref(&sc->sc_dv);
490 return (ret);
491 }
492
493 int
vldcpioctl(dev_t dev,u_long cmd,caddr_t data,int flag,struct proc * p)494 vldcpioctl(dev_t dev, u_long cmd, caddr_t data, int flag, struct proc *p)
495 {
496 struct vldcp_softc *sc;
497 struct ldc_conn *lc;
498 struct hv_io *hi = (struct hv_io *)data;
499 paddr_t pa, offset;
500 psize_t nbytes;
501 caddr_t buf;
502 size_t size;
503 int err;
504
505 sc = vldcp_lookup(dev);
506 if (sc == NULL)
507 return (ENXIO);
508 lc = &sc->sc_lc;
509
510 switch (cmd) {
511 case HVIOCREAD:
512 case HVIOCWRITE:
513 break;
514 default:
515 device_unref(&sc->sc_dv);
516 return (ENOTTY);
517 }
518
519 buf = malloc(PAGE_SIZE, M_DEVBUF, M_WAITOK);
520
521 switch(cmd) {
522 case HVIOCREAD:
523 size = hi->hi_len;
524 offset = 0;
525 while (size > 0) {
526 pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
527 nbytes = min(PAGE_SIZE, size);
528 err = hv_ldc_copy(lc->lc_id, LDC_COPY_IN,
529 hi->hi_cookie + offset, pa, nbytes, &nbytes);
530 if (err != H_EOK) {
531 printf("hv_ldc_copy %d\n", err);
532 free(buf, M_DEVBUF, 0);
533 device_unref(&sc->sc_dv);
534 return (EINVAL);
535 }
536 err = copyout(buf, (caddr_t)hi->hi_addr + offset, nbytes);
537 if (err) {
538 free(buf, M_DEVBUF, 0);
539 device_unref(&sc->sc_dv);
540 return (err);
541 }
542 size -= nbytes;
543 offset += nbytes;
544 }
545 break;
546 case HVIOCWRITE:
547 size = hi->hi_len;
548 offset = 0;
549 while (size > 0) {
550 pmap_extract(pmap_kernel(), (vaddr_t)buf, &pa);
551 nbytes = min(PAGE_SIZE, size);
552 err = copyin((caddr_t)hi->hi_addr + offset, buf, nbytes);
553 if (err) {
554 free(buf, M_DEVBUF, 0);
555 device_unref(&sc->sc_dv);
556 return (err);
557 }
558 err = hv_ldc_copy(lc->lc_id, LDC_COPY_OUT,
559 hi->hi_cookie + offset, pa, nbytes, &nbytes);
560 if (err != H_EOK) {
561 printf("hv_ldc_copy %d\n", err);
562 free(buf, M_DEVBUF, 0);
563 device_unref(&sc->sc_dv);
564 return (EINVAL);
565 }
566 size -= nbytes;
567 offset += nbytes;
568 }
569 break;
570
571 }
572
573 free(buf, M_DEVBUF, 0);
574
575 device_unref(&sc->sc_dv);
576 return (0);
577 }
578
579 void
filt_vldcprdetach(struct knote * kn)580 filt_vldcprdetach(struct knote *kn)
581 {
582 struct vldcp_softc *sc = (void *)kn->kn_hook;
583 int s;
584
585 s = spltty();
586 klist_remove_locked(&sc->sc_rsel.si_note, kn);
587 splx(s);
588 }
589
590 void
filt_vldcpwdetach(struct knote * kn)591 filt_vldcpwdetach(struct knote *kn)
592 {
593 struct vldcp_softc *sc = (void *)kn->kn_hook;
594 int s;
595
596 s = spltty();
597 klist_remove_locked(&sc->sc_wsel.si_note, kn);
598 splx(s);
599 }
600
601 int
filt_vldcpread(struct knote * kn,long hint)602 filt_vldcpread(struct knote *kn, long hint)
603 {
604 struct vldcp_softc *sc = (void *)kn->kn_hook;
605 struct ldc_conn *lc = &sc->sc_lc;
606 uint64_t head, tail, avail, state;
607 int s, err;
608
609 s = spltty();
610 err = hv_ldc_rx_get_state(lc->lc_id, &head, &tail, &state);
611 if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
612 avail = (tail - head) / sizeof(struct ldc_pkt) +
613 lc->lc_rxq->lq_nentries;
614 avail %= lc->lc_rxq->lq_nentries;
615 kn->kn_data = avail;
616 } else {
617 cbus_intr_setenabled(sc->sc_bustag, sc->sc_rx_ino,
618 INTR_ENABLED);
619 kn->kn_data = 0;
620 }
621 splx(s);
622
623 return (kn->kn_data > 0);
624 }
625
626 int
filt_vldcwrite(struct knote * kn,long hint)627 filt_vldcwrite(struct knote *kn, long hint)
628 {
629 struct vldcp_softc *sc = (void *)kn->kn_hook;
630 struct ldc_conn *lc = &sc->sc_lc;
631 uint64_t head, tail, avail, state;
632 int s, err;
633
634 s = spltty();
635 err = hv_ldc_tx_get_state(lc->lc_id, &head, &tail, &state);
636 if (err == 0 && state == LDC_CHANNEL_UP && head != tail) {
637 avail = (head - tail) / sizeof(struct ldc_pkt) +
638 lc->lc_txq->lq_nentries - 1;
639 avail %= lc->lc_txq->lq_nentries;
640 kn->kn_data = avail;
641 } else {
642 cbus_intr_setenabled(sc->sc_bustag, sc->sc_tx_ino,
643 INTR_ENABLED);
644 kn->kn_data = 0;
645 }
646 splx(s);
647
648 return (kn->kn_data > 0);
649 }
650
651 const struct filterops vldcpread_filtops = {
652 .f_flags = FILTEROP_ISFD,
653 .f_attach = NULL,
654 .f_detach = filt_vldcprdetach,
655 .f_event = filt_vldcpread,
656 };
657
658 const struct filterops vldcpwrite_filtops = {
659 .f_flags = FILTEROP_ISFD,
660 .f_attach = NULL,
661 .f_detach = filt_vldcpwdetach,
662 .f_event = filt_vldcwrite,
663 };
664
665 int
vldcpkqfilter(dev_t dev,struct knote * kn)666 vldcpkqfilter(dev_t dev, struct knote *kn)
667 {
668 struct vldcp_softc *sc;
669 struct klist *klist;
670 int s;
671
672 sc = vldcp_lookup(dev);
673 if (sc == NULL)
674 return (ENXIO);
675
676 switch (kn->kn_filter) {
677 case EVFILT_READ:
678 klist = &sc->sc_rsel.si_note;
679 kn->kn_fop = &vldcpread_filtops;
680 break;
681 case EVFILT_WRITE:
682 klist = &sc->sc_wsel.si_note;
683 kn->kn_fop = &vldcpwrite_filtops;
684 break;
685
686 default:
687 device_unref(&sc->sc_dv);
688 return (EINVAL);
689 }
690
691 kn->kn_hook = sc;
692
693 s = spltty();
694 klist_insert_locked(klist, kn);
695 splx(s);
696
697 device_unref(&sc->sc_dv);
698 return (0);
699 }
700