1 /*
2 * Copyright (c) 2026 Abdelkader Boudih <freebsd@seuros.com>
3 * Copyright (c) 2008 Robert Nagy <robert@openbsd.org>
4 * Copyright (c) 2008 Marcus Glocker <mglocker@openbsd.org>
5 *
6 * SPDX-License-Identifier: BSD-2-Clause AND ISC
7 *
8 * The cdev, kqfilter and buffer-pool handling are derived from uvideo(4).
9 */
10
11 #include <sys/param.h>
12 #include <sys/systm.h>
13 #include <sys/bus.h>
14 #include <sys/conf.h>
15 #include <sys/fcntl.h>
16 #include <sys/kernel.h>
17 #include <sys/lock.h>
18 #include <sys/rwlock.h>
19 #include <sys/malloc.h>
20 #include <sys/module.h>
21 #include <sys/mutex.h>
22 #include <sys/poll.h>
23 #include <sys/proc.h>
24 #include <sys/selinfo.h>
25 #include <sys/sx.h>
26 #include <sys/uio.h>
27 #include <sys/videoio.h>
28
29 #include <vm/vm.h>
30 #include <vm/vm_extern.h>
31 #include <vm/vm_kern.h>
32 #include <vm/vm_object.h>
33 #include <vm/vm_page.h>
34 #include <vm/vm_pager.h>
35 #include <vm/vm_param.h>
36 #include <vm/pmap.h>
37 #include <vm/vm_map.h>
38 #include <vm/uma.h>
39
40 #include <dev/video/video_internal.h>
41
42 #include "video_if.h"
43
44 MALLOC_DEFINE(M_VIDEO, "video", "video(4) framework");
45
46 #define VIDEO_UNIT_RETRIES 32
47
48 static struct unrhdr *video_unrhdr;
49
50 static d_open_t video_open;
51 static d_close_t video_close;
52 static d_read_t video_read;
53 static d_ioctl_t video_ioctl;
54 static d_poll_t video_poll;
55 static d_kqfilter_t video_kqfilter;
56 static d_mmap_single_t video_mmap_single;
57
58 static struct cdevsw video_cdevsw = {
59 .d_version = D_VERSION,
60 .d_open = video_open,
61 .d_close = video_close,
62 .d_read = video_read,
63 .d_ioctl = video_ioctl,
64 .d_poll = video_poll,
65 .d_kqfilter = video_kqfilter,
66 .d_mmap_single = video_mmap_single,
67 .d_name = "video",
68 };
69
70 static struct video_buf_pool *
video_pool_alloc(u_int nbufs,size_t buf_size)71 video_pool_alloc(u_int nbufs, size_t buf_size)
72 {
73 struct video_buf_pool *pool;
74 struct video_buf *vb;
75 size_t map_size;
76 vm_object_t obj;
77 vm_offset_t kva;
78 u_int i;
79 int error;
80
81 buf_size = round_page(buf_size);
82 if (buf_size == 0 || nbufs == 0 || nbufs > VIDEO_MAX_BUFFERS)
83 return (NULL);
84 if (SIZE_MAX / nbufs < buf_size)
85 return (NULL);
86 map_size = (size_t)nbufs * buf_size;
87
88 /*
89 * Use phys_pager_allocate() rather than a bare vm_object_allocate():
90 * the latter leaves un_pager.phys.ops NULL, which faults when the VM
91 * system calls phys_pager_getpages() from vm_map_wire() or from a
92 * userspace fault on the mapping.
93 */
94 obj = phys_pager_allocate(NULL, &default_phys_pg_ops, NULL, map_size,
95 VM_PROT_ALL, 0, curthread->td_ucred);
96 if (obj == NULL)
97 return (NULL);
98
99 kva = vm_map_min(kernel_map);
100 error = vm_map_find(kernel_map, obj, 0, &kva, map_size, 0,
101 VMFS_OPTIMAL_SPACE, VM_PROT_READ | VM_PROT_WRITE,
102 VM_PROT_READ | VM_PROT_WRITE, 0);
103 if (error != KERN_SUCCESS) {
104 vm_object_deallocate(obj);
105 return (NULL);
106 }
107 error = vm_map_wire(kernel_map, kva, kva + map_size,
108 VM_MAP_WIRE_SYSTEM | VM_MAP_WIRE_NOHOLES);
109 if (error != KERN_SUCCESS) {
110 vm_map_remove(kernel_map, kva, kva + map_size);
111 return (NULL);
112 }
113
114 pool = malloc(sizeof(*pool), M_VIDEO, M_WAITOK | M_ZERO);
115 pool->obj = obj;
116 pool->kva = kva;
117 pool->nbufs = nbufs;
118 pool->buf_size = buf_size;
119 pool->map_size = map_size;
120
121 for (i = 0; i < nbufs; i++) {
122 vb = &pool->bufs[i];
123 vb->pool = pool;
124 vb->index = i;
125 vb->state = VB_IDLE;
126 vb->length = buf_size;
127 vb->buf = (uint8_t *)kva + (size_t)i * buf_size;
128 }
129 return (pool);
130 }
131
132 static void
video_pool_free(struct video_buf_pool * pool)133 video_pool_free(struct video_buf_pool *pool)
134 {
135
136 /*
137 * Removing the kernel mapping drops the map's reference on the
138 * object. Any surviving userspace mapping holds its own reference,
139 * so the frames stay alive until the last one goes away.
140 */
141 vm_map_remove(kernel_map, pool->kva, pool->kva + pool->map_size);
142 free(pool, M_VIDEO);
143 }
144
145 static void
video_pool_detach(struct video_device * vd)146 video_pool_detach(struct video_device *vd)
147 {
148 struct video_buf_pool *pool;
149
150 mtx_lock(&vd->mtx);
151 if (vd->owner != NULL) {
152 vd->owner->read_buf = NULL;
153 vd->owner->read_offset = 0;
154 }
155 while (vd->readers > 0)
156 msleep(&vd->readers, &vd->mtx, 0, "vdrain", 0);
157 pool = vd->pool;
158 vd->pool = NULL;
159 STAILQ_INIT(&vd->queued);
160 STAILQ_INIT(&vd->done);
161 mtx_unlock(&vd->mtx);
162
163 if (pool != NULL)
164 video_pool_free(pool);
165 }
166
167 static void
video_file_dtor(void * arg)168 video_file_dtor(void *arg)
169 {
170 struct video_file *vf = arg;
171 struct video_device *vd = vf->vd;
172
173 sx_xlock(&vd->cfg_sx);
174
175 if (vf->is_owner) {
176 if (vd->streaming) {
177 mtx_lock(&vd->mtx);
178 vd->stopping = true;
179 mtx_unlock(&vd->mtx);
180
181 VIDEO_STOP_STREAM(vd->dev);
182
183 mtx_lock(&vd->mtx);
184 vd->streaming = false;
185 vd->stopping = false;
186 STAILQ_INIT(&vd->queued);
187 STAILQ_INIT(&vd->done);
188 mtx_unlock(&vd->mtx);
189 }
190
191 video_pool_detach(vd);
192
193 mtx_lock(&vd->mtx);
194 vd->owner = NULL;
195 mtx_unlock(&vd->mtx);
196 vd->mode = VMODE_NONE;
197
198 VIDEO_CLOSE(vd->dev);
199 }
200
201 sx_xunlock(&vd->cfg_sx);
202
203 free(vf, M_VIDEO);
204 }
205
206 static int
video_open(struct cdev * dev,int flags,int fmt,struct thread * td)207 video_open(struct cdev *dev, int flags, int fmt, struct thread *td)
208 {
209 struct video_device *vd = dev->si_drv1;
210 struct video_file *vf;
211
212 if (vd == NULL || vd->dying)
213 return (ENXIO);
214
215 vf = malloc(sizeof(*vf), M_VIDEO, M_WAITOK | M_ZERO);
216 vf->vd = vd;
217
218 return (devfs_set_cdevpriv(vf, video_file_dtor));
219 }
220
221 static int
video_close(struct cdev * dev,int flags,int fmt,struct thread * td)222 video_close(struct cdev *dev, int flags, int fmt, struct thread *td)
223 {
224
225 return (0);
226 }
227
228 static int
video_claim_owner(struct video_device * vd,struct video_file * vf)229 video_claim_owner(struct video_device *vd, struct video_file *vf)
230 {
231 int error;
232
233 sx_assert(&vd->cfg_sx, SA_XLOCKED);
234
235 if (vd->dying)
236 return (ENXIO);
237 if (vd->owner == vf)
238 return (0);
239 if (vd->owner != NULL)
240 return (EBUSY);
241
242 error = VIDEO_OPEN(vd->dev);
243 if (error != 0)
244 return (error);
245
246 mtx_lock(&vd->mtx);
247 vd->owner = vf;
248 mtx_unlock(&vd->mtx);
249 vf->is_owner = true;
250 return (0);
251 }
252
253 struct video_buf *
video_buf_acquire(struct video_device * vd)254 video_buf_acquire(struct video_device *vd)
255 {
256 struct video_buf *vb;
257
258 mtx_lock(&vd->mtx);
259 vb = STAILQ_FIRST(&vd->queued);
260 if (vb != NULL) {
261 STAILQ_REMOVE_HEAD(&vd->queued, entry);
262 vb->state = VB_ACTIVE;
263 vb->bytesused = 0;
264 }
265 mtx_unlock(&vd->mtx);
266 return (vb);
267 }
268
269 int
video_buf_write(struct video_buf * vb,size_t offset,const void * src,size_t len)270 video_buf_write(struct video_buf *vb, size_t offset, const void *src,
271 size_t len)
272 {
273
274 if (offset > vb->length || len > vb->length - offset)
275 return (ENOSPC);
276
277 memcpy((uint8_t *)vb->buf + offset, src, len);
278 return (0);
279 }
280
281 void
video_buf_done(struct video_buf * vb,size_t bytesused,uint32_t sequence)282 video_buf_done(struct video_buf *vb, size_t bytesused, uint32_t sequence)
283 {
284 struct video_device *vd;
285 bool overrun;
286
287 overrun = bytesused > vb->length;
288 if (overrun)
289 bytesused = 0;
290
291 vd = vb->vd;
292 mtx_lock(&vd->mtx);
293 if (vb->state != VB_ACTIVE) {
294 mtx_unlock(&vd->mtx);
295 return;
296 }
297 vb->bytesused = bytesused;
298 vb->sequence = sequence;
299 getmicrouptime(&vb->timestamp);
300 vb->flags = V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
301 if (overrun)
302 vb->flags |= V4L2_BUF_FLAG_ERROR;
303 vb->state = VB_DONE;
304 STAILQ_INSERT_TAIL(&vd->done, vb, entry);
305 selwakeup(&vd->sel);
306 wakeup(&vd->done);
307 mtx_unlock(&vd->mtx);
308 }
309
310 void
video_buf_error(struct video_buf * vb)311 video_buf_error(struct video_buf *vb)
312 {
313 struct video_device *vd = vb->vd;
314
315 mtx_lock(&vd->mtx);
316 if (vb->state != VB_ACTIVE) {
317 mtx_unlock(&vd->mtx);
318 return;
319 }
320 vb->bytesused = 0;
321 vb->flags = V4L2_BUF_FLAG_ERROR;
322 getmicrouptime(&vb->timestamp);
323 vb->state = VB_ERROR;
324 STAILQ_INSERT_TAIL(&vd->done, vb, entry);
325 selwakeup(&vd->sel);
326 wakeup(&vd->done);
327 mtx_unlock(&vd->mtx);
328 }
329
330 static int
video_querycap(struct video_device * vd,struct v4l2_capability * cap)331 video_querycap(struct video_device *vd, struct v4l2_capability *cap)
332 {
333 struct video_caps vc;
334 int error;
335
336 memset(&vc, 0, sizeof(vc));
337 error = VIDEO_QUERYCAP(vd->dev, &vc);
338 if (error != 0)
339 return (error);
340
341 memset(cap, 0, sizeof(*cap));
342 strlcpy((char *)cap->driver, vc.driver, sizeof(cap->driver));
343 strlcpy((char *)cap->card, vc.card, sizeof(cap->card));
344 strlcpy((char *)cap->bus_info, vc.bus_info, sizeof(cap->bus_info));
345 cap->version = vc.version;
346 cap->device_caps = vc.capabilities;
347 cap->capabilities = vc.capabilities | V4L2_CAP_DEVICE_CAPS;
348 return (0);
349 }
350
351 static int
video_enum_fmt(struct video_device * vd,struct v4l2_fmtdesc * fd)352 video_enum_fmt(struct video_device *vd, struct v4l2_fmtdesc *fd)
353 {
354 struct video_format vf;
355 int error;
356
357 if (fd->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
358 return (EINVAL);
359
360 memset(&vf, 0, sizeof(vf));
361 error = VIDEO_ENUM_FORMAT(vd->dev, fd->index, &vf);
362 if (error != 0)
363 return (error);
364
365 fd->pixelformat = vf.pixelformat;
366 fd->flags = vf.flags;
367 strlcpy((char *)fd->description, vf.description,
368 sizeof(fd->description));
369 return (0);
370 }
371
372 static int
video_g_fmt(struct video_device * vd,struct v4l2_format * f)373 video_g_fmt(struct video_device *vd, struct v4l2_format *f)
374 {
375 struct video_format vf;
376 int error;
377
378 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
379 return (EINVAL);
380
381 memset(&vf, 0, sizeof(vf));
382 error = VIDEO_GET_FORMAT(vd->dev, &vf);
383 if (error != 0)
384 return (error);
385
386 memset(&f->fmt.pix, 0, sizeof(f->fmt.pix));
387 f->fmt.pix.width = vf.width;
388 f->fmt.pix.height = vf.height;
389 f->fmt.pix.pixelformat = vf.pixelformat;
390 f->fmt.pix.field = vf.field;
391 f->fmt.pix.bytesperline = vf.bytesperline;
392 f->fmt.pix.sizeimage = vf.sizeimage;
393 f->fmt.pix.colorspace = vf.colorspace;
394 f->fmt.pix.xfer_func = vf.xfer_func;
395 f->fmt.pix.ycbcr_enc = vf.ycbcr_enc;
396 f->fmt.pix.flags = vf.flags;
397 return (0);
398 }
399
400 static int
video_s_fmt(struct video_device * vd,struct video_file * vf,struct v4l2_format * f)401 video_s_fmt(struct video_device *vd, struct video_file *vf,
402 struct v4l2_format *f)
403 {
404 struct video_format fmt;
405 int error;
406
407 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
408 return (EINVAL);
409
410 sx_xlock(&vd->cfg_sx);
411 error = video_claim_owner(vd, vf);
412 if (error != 0) {
413 sx_xunlock(&vd->cfg_sx);
414 return (error);
415 }
416 if (vd->streaming) {
417 sx_xunlock(&vd->cfg_sx);
418 return (EBUSY);
419 }
420
421 memset(&fmt, 0, sizeof(fmt));
422 fmt.width = f->fmt.pix.width;
423 fmt.height = f->fmt.pix.height;
424 fmt.pixelformat = f->fmt.pix.pixelformat;
425 fmt.field = f->fmt.pix.field;
426
427 error = VIDEO_SET_FORMAT(vd->dev, &fmt);
428 if (error == 0) {
429 VIDEO_GET_FORMAT(vd->dev, &vd->format);
430
431 f->fmt.pix.width = vd->format.width;
432 f->fmt.pix.height = vd->format.height;
433 f->fmt.pix.pixelformat = vd->format.pixelformat;
434 f->fmt.pix.field = vd->format.field;
435 f->fmt.pix.bytesperline = vd->format.bytesperline;
436 f->fmt.pix.sizeimage = vd->format.sizeimage;
437 f->fmt.pix.colorspace = vd->format.colorspace;
438 }
439 sx_xunlock(&vd->cfg_sx);
440 return (error);
441 }
442
443 static int
video_try_fmt(struct video_device * vd,struct v4l2_format * f)444 video_try_fmt(struct video_device *vd, struct v4l2_format *f)
445 {
446 struct video_format fmt;
447 int error;
448
449 if (f->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
450 return (EINVAL);
451 memset(&fmt, 0, sizeof(fmt));
452 fmt.width = f->fmt.pix.width;
453 fmt.height = f->fmt.pix.height;
454 fmt.pixelformat = f->fmt.pix.pixelformat;
455 fmt.field = f->fmt.pix.field;
456
457 error = VIDEO_TRY_FORMAT(vd->dev, &fmt);
458 if (error == 0) {
459 f->fmt.pix.width = fmt.width;
460 f->fmt.pix.height = fmt.height;
461 f->fmt.pix.pixelformat = fmt.pixelformat;
462 f->fmt.pix.field = fmt.field;
463 f->fmt.pix.bytesperline = fmt.bytesperline;
464 f->fmt.pix.sizeimage = fmt.sizeimage;
465 f->fmt.pix.colorspace = fmt.colorspace;
466 }
467 return (error);
468 }
469
470 static int
video_reqbufs(struct video_device * vd,struct video_file * vf,struct v4l2_requestbuffers * rb)471 video_reqbufs(struct video_device *vd, struct video_file *vf,
472 struct v4l2_requestbuffers *rb)
473 {
474 struct video_buf_pool *pool;
475 int error;
476
477 if (rb->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
478 return (EINVAL);
479 if (rb->memory != V4L2_MEMORY_MMAP)
480 return (EINVAL);
481
482 sx_xlock(&vd->cfg_sx);
483 error = video_claim_owner(vd, vf);
484 if (error != 0) {
485 sx_xunlock(&vd->cfg_sx);
486 return (error);
487 }
488 if (vd->streaming) {
489 sx_xunlock(&vd->cfg_sx);
490 return (EBUSY);
491 }
492
493 video_pool_detach(vd);
494
495 if (rb->count == 0) {
496 vd->mode = VMODE_NONE;
497 sx_xunlock(&vd->cfg_sx);
498 rb->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;
499 return (0);
500 }
501
502 if (rb->count > VIDEO_MAX_BUFFERS)
503 rb->count = VIDEO_MAX_BUFFERS;
504 if (rb->count < 2)
505 rb->count = 2;
506
507 if (vd->format.sizeimage == 0) {
508 error = VIDEO_GET_FORMAT(vd->dev, &vd->format);
509 if (error != 0 || vd->format.sizeimage == 0) {
510 sx_xunlock(&vd->cfg_sx);
511 return (error != 0 ? error : EINVAL);
512 }
513 }
514
515 pool = video_pool_alloc(rb->count, vd->format.sizeimage);
516 if (pool == NULL) {
517 sx_xunlock(&vd->cfg_sx);
518 return (ENOMEM);
519 }
520 mtx_lock(&vd->mtx);
521 vd->pool = pool;
522 mtx_unlock(&vd->mtx);
523
524 vd->mode = VMODE_MMAP;
525 rb->capabilities = V4L2_BUF_CAP_SUPPORTS_MMAP;
526 sx_xunlock(&vd->cfg_sx);
527 return (0);
528 }
529
530 static int
video_querybuf(struct video_device * vd,struct video_file * vf,struct v4l2_buffer * b)531 video_querybuf(struct video_device *vd, struct video_file *vf,
532 struct v4l2_buffer *b)
533 {
534 struct video_buf *vb;
535
536 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
537 return (EINVAL);
538
539 mtx_lock(&vd->mtx);
540 if (vd->owner != vf) {
541 mtx_unlock(&vd->mtx);
542 return (EPERM);
543 }
544 if (vd->pool == NULL || b->index >= vd->pool->nbufs) {
545 mtx_unlock(&vd->mtx);
546 return (EINVAL);
547 }
548
549 vb = &vd->pool->bufs[b->index];
550 b->memory = V4L2_MEMORY_MMAP;
551 b->length = vb->length;
552 b->m.offset = vb->index * vd->pool->buf_size;
553 b->bytesused = vb->bytesused;
554 b->field = V4L2_FIELD_NONE;
555 b->timestamp = vb->timestamp;
556 b->sequence = vb->sequence;
557 b->flags = 0;
558
559 switch (vb->state) {
560 case VB_QUEUED:
561 case VB_ACTIVE:
562 b->flags |= V4L2_BUF_FLAG_QUEUED;
563 break;
564 case VB_DONE:
565 b->flags |= V4L2_BUF_FLAG_DONE;
566 break;
567 case VB_ERROR:
568 b->flags |= V4L2_BUF_FLAG_DONE | V4L2_BUF_FLAG_ERROR;
569 break;
570 default:
571 break;
572 }
573 b->flags |= V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
574 mtx_unlock(&vd->mtx);
575 return (0);
576 }
577
578 static int
video_qbuf(struct video_device * vd,struct video_file * vf,struct v4l2_buffer * b)579 video_qbuf(struct video_device *vd, struct video_file *vf,
580 struct v4l2_buffer *b)
581 {
582 struct video_buf *vb;
583
584 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
585 return (EINVAL);
586 if (b->memory != V4L2_MEMORY_MMAP)
587 return (EINVAL);
588
589 mtx_lock(&vd->mtx);
590 if (vd->owner != vf) {
591 mtx_unlock(&vd->mtx);
592 return (EPERM);
593 }
594 if (vd->pool == NULL || vd->mode != VMODE_MMAP ||
595 b->index >= vd->pool->nbufs) {
596 mtx_unlock(&vd->mtx);
597 return (EINVAL);
598 }
599
600 vb = &vd->pool->bufs[b->index];
601 if (vb->state != VB_IDLE) {
602 mtx_unlock(&vd->mtx);
603 return (EINVAL);
604 }
605
606 vb->state = VB_QUEUED;
607 vb->bytesused = 0;
608 vb->vd = vd;
609 STAILQ_INSERT_TAIL(&vd->queued, vb, entry);
610 mtx_unlock(&vd->mtx);
611
612 b->flags = V4L2_BUF_FLAG_QUEUED | V4L2_BUF_FLAG_MAPPED;
613 return (0);
614 }
615
616 static int
video_dqbuf(struct video_device * vd,struct video_file * vf,struct v4l2_buffer * b,int flags)617 video_dqbuf(struct video_device *vd, struct video_file *vf,
618 struct v4l2_buffer *b, int flags)
619 {
620 struct video_buf *vb;
621 int error;
622
623 if (b->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
624 return (EINVAL);
625 if (b->memory != V4L2_MEMORY_MMAP)
626 return (EINVAL);
627
628 mtx_lock(&vd->mtx);
629 if (vd->owner != vf) {
630 mtx_unlock(&vd->mtx);
631 return (EPERM);
632 }
633 while (STAILQ_EMPTY(&vd->done)) {
634 if (!vd->streaming || vd->stopping) {
635 mtx_unlock(&vd->mtx);
636 return (EINVAL);
637 }
638 if (flags & O_NONBLOCK) {
639 mtx_unlock(&vd->mtx);
640 return (EAGAIN);
641 }
642 error = msleep(&vd->done, &vd->mtx, PCATCH, "vdqbuf", 0);
643 if (error != 0) {
644 mtx_unlock(&vd->mtx);
645 return (error);
646 }
647 }
648
649 vb = STAILQ_FIRST(&vd->done);
650 STAILQ_REMOVE_HEAD(&vd->done, entry);
651
652 b->index = vb->index;
653 b->bytesused = vb->bytesused;
654 b->field = V4L2_FIELD_NONE;
655 b->timestamp = vb->timestamp;
656 b->sequence = vb->sequence;
657 b->memory = V4L2_MEMORY_MMAP;
658 b->m.offset = vb->index * vd->pool->buf_size;
659 b->length = vb->length;
660 b->flags = V4L2_BUF_FLAG_MAPPED | V4L2_BUF_FLAG_DONE |
661 V4L2_BUF_FLAG_TIMESTAMP_MONOTONIC;
662 if (vb->flags & V4L2_BUF_FLAG_ERROR)
663 b->flags |= V4L2_BUF_FLAG_ERROR;
664
665 vb->state = VB_IDLE;
666 mtx_unlock(&vd->mtx);
667
668 return (0);
669 }
670
671 static int
video_streamon(struct video_device * vd,struct video_file * vf,int * type)672 video_streamon(struct video_device *vd, struct video_file *vf, int *type)
673 {
674 int error;
675
676 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
677 return (EINVAL);
678
679 sx_xlock(&vd->cfg_sx);
680 error = video_claim_owner(vd, vf);
681 if (error != 0) {
682 sx_xunlock(&vd->cfg_sx);
683 return (error);
684 }
685 if (vd->streaming) {
686 sx_xunlock(&vd->cfg_sx);
687 return (0);
688 }
689 if (vd->pool == NULL || vd->mode != VMODE_MMAP) {
690 sx_xunlock(&vd->cfg_sx);
691 return (EINVAL);
692 }
693
694 error = VIDEO_START_STREAM(vd->dev);
695 if (error != 0) {
696 sx_xunlock(&vd->cfg_sx);
697 return (error);
698 }
699
700 mtx_lock(&vd->mtx);
701 vd->streaming = true;
702 mtx_unlock(&vd->mtx);
703
704 sx_xunlock(&vd->cfg_sx);
705 return (0);
706 }
707
708 static int
video_streamoff(struct video_device * vd,struct video_file * vf,int * type)709 video_streamoff(struct video_device *vd, struct video_file *vf, int *type)
710 {
711 struct video_buf *vb;
712 u_int i;
713
714 if (*type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
715 return (EINVAL);
716
717 sx_xlock(&vd->cfg_sx);
718 if (vd->owner != vf) {
719 sx_xunlock(&vd->cfg_sx);
720 return (EBUSY);
721 }
722 if (!vd->streaming) {
723 sx_xunlock(&vd->cfg_sx);
724 return (0);
725 }
726
727 mtx_lock(&vd->mtx);
728 vd->stopping = true;
729 wakeup(&vd->done);
730 mtx_unlock(&vd->mtx);
731
732 VIDEO_STOP_STREAM(vd->dev);
733
734 mtx_lock(&vd->mtx);
735 vd->streaming = false;
736 vd->stopping = false;
737 STAILQ_INIT(&vd->queued);
738 STAILQ_INIT(&vd->done);
739 for (i = 0; i < vd->pool->nbufs; i++) {
740 vb = &vd->pool->bufs[i];
741 vb->state = VB_IDLE;
742 vb->bytesused = 0;
743 vb->vd = NULL;
744 }
745 vf->read_buf = NULL;
746 vf->read_offset = 0;
747 mtx_unlock(&vd->mtx);
748
749 sx_xunlock(&vd->cfg_sx);
750 return (0);
751 }
752
753 static int
video_g_parm(struct video_device * vd,struct v4l2_streamparm * sp)754 video_g_parm(struct video_device *vd, struct v4l2_streamparm *sp)
755 {
756 struct video_fract fract;
757 int error;
758
759 if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
760 return (EINVAL);
761 memset(&fract, 0, sizeof(fract));
762 error = VIDEO_GET_PARM(vd->dev, &fract);
763 if (error != 0)
764 return (error);
765
766 memset(&sp->parm.capture, 0, sizeof(sp->parm.capture));
767 sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
768 sp->parm.capture.timeperframe.numerator = fract.numerator;
769 sp->parm.capture.timeperframe.denominator = fract.denominator;
770 return (0);
771 }
772
773 static int
video_s_parm(struct video_device * vd,struct v4l2_streamparm * sp)774 video_s_parm(struct video_device *vd, struct v4l2_streamparm *sp)
775 {
776 struct video_fract fract;
777 int error;
778
779 if (sp->type != V4L2_BUF_TYPE_VIDEO_CAPTURE)
780 return (EINVAL);
781 fract.numerator = sp->parm.capture.timeperframe.numerator;
782 fract.denominator = sp->parm.capture.timeperframe.denominator;
783
784 error = VIDEO_SET_PARM(vd->dev, &fract);
785 if (error != 0)
786 return (error);
787
788 sp->parm.capture.capability = V4L2_CAP_TIMEPERFRAME;
789 sp->parm.capture.timeperframe.numerator = fract.numerator;
790 sp->parm.capture.timeperframe.denominator = fract.denominator;
791 return (0);
792 }
793
794 static int
video_queryctrl(struct video_device * vd,struct v4l2_queryctrl * qc)795 video_queryctrl(struct video_device *vd, struct v4l2_queryctrl *qc)
796 {
797 struct video_control_desc desc;
798 int error;
799
800 memset(&desc, 0, sizeof(desc));
801 desc.id = qc->id;
802 error = VIDEO_QUERY_CONTROL(vd->dev, &desc);
803 if (error != 0)
804 return (error);
805
806 qc->id = desc.id;
807 qc->type = desc.type;
808 strlcpy((char *)qc->name, desc.name, sizeof(qc->name));
809 qc->minimum = desc.minimum;
810 qc->maximum = desc.maximum;
811 qc->step = desc.step;
812 qc->default_value = desc.default_value;
813 qc->flags = desc.flags;
814 return (0);
815 }
816
817 static int
video_g_ctrl(struct video_device * vd,struct v4l2_control * c)818 video_g_ctrl(struct video_device *vd, struct v4l2_control *c)
819 {
820 struct video_control vc;
821 int error;
822
823 vc.id = c->id;
824 vc.value = 0;
825 error = VIDEO_GET_CONTROL(vd->dev, &vc);
826 if (error == 0)
827 c->value = vc.value;
828 return (error);
829 }
830
831 static int
video_s_ctrl(struct video_device * vd,struct v4l2_control * c)832 video_s_ctrl(struct video_device *vd, struct v4l2_control *c)
833 {
834 struct video_control vc;
835
836 vc.id = c->id;
837 vc.value = c->value;
838 return (VIDEO_SET_CONTROL(vd->dev, &vc));
839 }
840
841 static int
video_enum_framesizes(struct video_device * vd,struct v4l2_frmsizeenum * fs)842 video_enum_framesizes(struct video_device *vd, struct v4l2_frmsizeenum *fs)
843 {
844 struct video_frmsizeenum vfs;
845 int error;
846
847 memset(&vfs, 0, sizeof(vfs));
848 vfs.index = fs->index;
849 vfs.pixelformat = fs->pixel_format;
850 error = VIDEO_ENUM_FRAMESIZES(vd->dev, &vfs);
851 if (error != 0)
852 return (error);
853
854 fs->type = vfs.type;
855 if (vfs.type == V4L2_FRMSIZE_TYPE_DISCRETE) {
856 fs->discrete.width = vfs.discrete.width;
857 fs->discrete.height = vfs.discrete.height;
858 } else {
859 fs->stepwise.min_width = vfs.stepwise.min_width;
860 fs->stepwise.max_width = vfs.stepwise.max_width;
861 fs->stepwise.step_width = vfs.stepwise.step_width;
862 fs->stepwise.min_height = vfs.stepwise.min_height;
863 fs->stepwise.max_height = vfs.stepwise.max_height;
864 fs->stepwise.step_height = vfs.stepwise.step_height;
865 }
866 return (0);
867 }
868
869 static int
video_enum_frameintervals(struct video_device * vd,struct v4l2_frmivalenum * fi)870 video_enum_frameintervals(struct video_device *vd,
871 struct v4l2_frmivalenum *fi)
872 {
873 struct video_frmivalenum vfi;
874 int error;
875
876 memset(&vfi, 0, sizeof(vfi));
877 vfi.index = fi->index;
878 vfi.pixelformat = fi->pixel_format;
879 vfi.width = fi->width;
880 vfi.height = fi->height;
881 error = VIDEO_ENUM_FRAMEINTERVALS(vd->dev, &vfi);
882 if (error != 0)
883 return (error);
884
885 fi->type = vfi.type;
886 if (vfi.type == V4L2_FRMIVAL_TYPE_DISCRETE) {
887 fi->discrete.numerator = vfi.discrete.numerator;
888 fi->discrete.denominator = vfi.discrete.denominator;
889 } else {
890 fi->stepwise.min.numerator = vfi.stepwise.min.numerator;
891 fi->stepwise.min.denominator = vfi.stepwise.min.denominator;
892 fi->stepwise.max.numerator = vfi.stepwise.max.numerator;
893 fi->stepwise.max.denominator = vfi.stepwise.max.denominator;
894 fi->stepwise.step.numerator = vfi.stepwise.step.numerator;
895 fi->stepwise.step.denominator = vfi.stepwise.step.denominator;
896 }
897 return (0);
898 }
899
900 static int
video_enum_input(struct video_device * vd,struct v4l2_input * inp)901 video_enum_input(struct video_device *vd, struct v4l2_input *inp)
902 {
903 struct video_input vi;
904 int error;
905
906 memset(&vi, 0, sizeof(vi));
907 error = VIDEO_ENUM_INPUT(vd->dev, inp->index, &vi);
908 if (error != 0)
909 return (error);
910
911 strlcpy((char *)inp->name, vi.name, sizeof(inp->name));
912 inp->type = vi.type;
913 return (0);
914 }
915
916 static int
video_g_input(struct video_device * vd,int * index)917 video_g_input(struct video_device *vd, int *index)
918 {
919 uint32_t idx;
920 int error;
921
922 error = VIDEO_GET_INPUT(vd->dev, &idx);
923 if (error == 0)
924 *index = idx;
925 return (error);
926 }
927
928 static int
video_s_input(struct video_device * vd,int * index)929 video_s_input(struct video_device *vd, int *index)
930 {
931
932 return (VIDEO_SET_INPUT(vd->dev, *index));
933 }
934
935 static int
video_ioctl(struct cdev * dev,u_long cmd,caddr_t data,int fflag,struct thread * td)936 video_ioctl(struct cdev *dev, u_long cmd, caddr_t data, int fflag,
937 struct thread *td)
938 {
939 struct video_device *vd = dev->si_drv1;
940 struct video_file *vf;
941 int error;
942
943 if (vd == NULL || vd->dying)
944 return (ENXIO);
945
946 error = devfs_get_cdevpriv((void **)&vf);
947 if (error != 0)
948 return (error);
949
950 switch (cmd) {
951 case VIDIOC_QUERYCAP:
952 return (video_querycap(vd, (struct v4l2_capability *)data));
953 case VIDIOC_ENUM_FMT:
954 return (video_enum_fmt(vd, (struct v4l2_fmtdesc *)data));
955 case VIDIOC_G_FMT:
956 return (video_g_fmt(vd, (struct v4l2_format *)data));
957 case VIDIOC_S_FMT:
958 return (video_s_fmt(vd, vf, (struct v4l2_format *)data));
959 case VIDIOC_TRY_FMT:
960 return (video_try_fmt(vd, (struct v4l2_format *)data));
961 case VIDIOC_REQBUFS:
962 return (video_reqbufs(vd, vf,
963 (struct v4l2_requestbuffers *)data));
964 case VIDIOC_QUERYBUF:
965 return (video_querybuf(vd, vf, (struct v4l2_buffer *)data));
966 case VIDIOC_QBUF:
967 return (video_qbuf(vd, vf, (struct v4l2_buffer *)data));
968 case VIDIOC_DQBUF:
969 return (video_dqbuf(vd, vf, (struct v4l2_buffer *)data,
970 fflag));
971 case VIDIOC_STREAMON:
972 return (video_streamon(vd, vf, (int *)data));
973 case VIDIOC_STREAMOFF:
974 return (video_streamoff(vd, vf, (int *)data));
975 case VIDIOC_G_PARM:
976 return (video_g_parm(vd, (struct v4l2_streamparm *)data));
977 case VIDIOC_S_PARM:
978 return (video_s_parm(vd, (struct v4l2_streamparm *)data));
979 case VIDIOC_QUERYCTRL:
980 return (video_queryctrl(vd, (struct v4l2_queryctrl *)data));
981 case VIDIOC_G_CTRL:
982 return (video_g_ctrl(vd, (struct v4l2_control *)data));
983 case VIDIOC_S_CTRL:
984 return (video_s_ctrl(vd, (struct v4l2_control *)data));
985 case VIDIOC_ENUMINPUT:
986 return (video_enum_input(vd, (struct v4l2_input *)data));
987 case VIDIOC_G_INPUT:
988 return (video_g_input(vd, (int *)data));
989 case VIDIOC_S_INPUT:
990 return (video_s_input(vd, (int *)data));
991 case VIDIOC_ENUM_FRAMESIZES:
992 return (video_enum_framesizes(vd,
993 (struct v4l2_frmsizeenum *)data));
994 case VIDIOC_ENUM_FRAMEINTERVALS:
995 return (video_enum_frameintervals(vd,
996 (struct v4l2_frmivalenum *)data));
997 default:
998 return (ENOTTY);
999 }
1000 }
1001
1002 static int
video_read(struct cdev * dev,struct uio * uio,int ioflag)1003 video_read(struct cdev *dev, struct uio *uio, int ioflag)
1004 {
1005 struct video_device *vd = dev->si_drv1;
1006 struct video_buf_pool *pool;
1007 struct video_file *vf;
1008 struct video_buf *vb;
1009 uint8_t *src;
1010 size_t bytesused, chunk, offset;
1011 int error;
1012
1013 if (vd == NULL || vd->dying)
1014 return (ENXIO);
1015
1016 error = devfs_get_cdevpriv((void **)&vf);
1017 if (error != 0)
1018 return (error);
1019
1020 mtx_lock(&vd->mtx);
1021 while (vf->reading) {
1022 error = msleep(&vf->reading, &vd->mtx, PCATCH, "vrdser", 0);
1023 if (error != 0) {
1024 mtx_unlock(&vd->mtx);
1025 return (error);
1026 }
1027 }
1028 vf->reading = true;
1029 mtx_unlock(&vd->mtx);
1030
1031 sx_xlock(&vd->cfg_sx);
1032 error = video_claim_owner(vd, vf);
1033 if (error != 0) {
1034 sx_xunlock(&vd->cfg_sx);
1035 goto out;
1036 }
1037
1038 if (!vd->streaming) {
1039 if (vd->mode == VMODE_MMAP) {
1040 sx_xunlock(&vd->cfg_sx);
1041 error = EBUSY;
1042 goto out;
1043 }
1044
1045 if (vd->format.sizeimage == 0) {
1046 error = VIDEO_GET_FORMAT(vd->dev, &vd->format);
1047 if (error != 0 || vd->format.sizeimage == 0) {
1048 sx_xunlock(&vd->cfg_sx);
1049 if (error == 0)
1050 error = EIO;
1051 goto out;
1052 }
1053 }
1054
1055 if (vd->pool == NULL) {
1056 pool = video_pool_alloc(VIDEO_READ_BUFFERS,
1057 vd->format.sizeimage);
1058 if (pool == NULL) {
1059 sx_xunlock(&vd->cfg_sx);
1060 error = ENOMEM;
1061 goto out;
1062 }
1063 mtx_lock(&vd->mtx);
1064 vd->pool = pool;
1065 mtx_unlock(&vd->mtx);
1066 }
1067 vd->mode = VMODE_READ;
1068
1069 mtx_lock(&vd->mtx);
1070 for (u_int i = 0; i < vd->pool->nbufs; i++) {
1071 vb = &vd->pool->bufs[i];
1072 vb->state = VB_QUEUED;
1073 vb->vd = vd;
1074 STAILQ_INSERT_TAIL(&vd->queued, vb, entry);
1075 }
1076 mtx_unlock(&vd->mtx);
1077
1078 error = VIDEO_START_STREAM(vd->dev);
1079 if (error != 0) {
1080 video_pool_detach(vd);
1081 vd->mode = VMODE_NONE;
1082 sx_xunlock(&vd->cfg_sx);
1083 goto out;
1084 }
1085
1086 mtx_lock(&vd->mtx);
1087 vd->streaming = true;
1088 mtx_unlock(&vd->mtx);
1089 }
1090 sx_xunlock(&vd->cfg_sx);
1091
1092 mtx_lock(&vd->mtx);
1093 while (vf->read_buf == NULL) {
1094 if (!STAILQ_EMPTY(&vd->done)) {
1095 vf->read_buf = STAILQ_FIRST(&vd->done);
1096 STAILQ_REMOVE_HEAD(&vd->done, entry);
1097 vf->read_offset = 0;
1098 break;
1099 }
1100 if (!vd->streaming || vd->stopping) {
1101 mtx_unlock(&vd->mtx);
1102 error = EIO;
1103 goto out;
1104 }
1105 if (ioflag & O_NONBLOCK) {
1106 mtx_unlock(&vd->mtx);
1107 error = EAGAIN;
1108 goto out;
1109 }
1110 error = msleep(&vd->done, &vd->mtx, PCATCH, "vread", 0);
1111 if (error != 0) {
1112 mtx_unlock(&vd->mtx);
1113 goto out;
1114 }
1115 }
1116
1117 vb = vf->read_buf;
1118 bytesused = vb->bytesused;
1119 offset = vf->read_offset;
1120 chunk = 0;
1121 if (uio->uio_resid > 0 && offset < bytesused) {
1122 chunk = MIN((size_t)uio->uio_resid, bytesused - offset);
1123 src = (uint8_t *)vb->buf + offset;
1124 vd->readers++;
1125 mtx_unlock(&vd->mtx);
1126
1127 error = uiomove(src, chunk, uio);
1128
1129 mtx_lock(&vd->mtx);
1130 if (--vd->readers == 0)
1131 wakeup(&vd->readers);
1132 if (error != 0) {
1133 mtx_unlock(&vd->mtx);
1134 goto out;
1135 }
1136 }
1137
1138 if (vf->read_buf == vb) {
1139 vf->read_offset = offset + chunk;
1140 if (vf->read_offset >= bytesused) {
1141 vf->read_buf = NULL;
1142 vf->read_offset = 0;
1143 vb->state = VB_QUEUED;
1144 vb->bytesused = 0;
1145 STAILQ_INSERT_TAIL(&vd->queued, vb, entry);
1146 }
1147 }
1148 mtx_unlock(&vd->mtx);
1149
1150 error = 0;
1151 out:
1152 mtx_lock(&vd->mtx);
1153 vf->reading = false;
1154 wakeup(&vf->reading);
1155 mtx_unlock(&vd->mtx);
1156 return (error);
1157 }
1158
1159 static int
video_poll(struct cdev * dev,int events,struct thread * td)1160 video_poll(struct cdev *dev, int events, struct thread *td)
1161 {
1162 struct video_device *vd = dev->si_drv1;
1163 int revents = 0;
1164
1165 if (vd == NULL || vd->dying)
1166 return (POLLHUP);
1167
1168 mtx_lock(&vd->mtx);
1169 if (events & (POLLIN | POLLRDNORM)) {
1170 if (!STAILQ_EMPTY(&vd->done))
1171 revents |= events & (POLLIN | POLLRDNORM);
1172 else
1173 selrecord(td, &vd->sel);
1174 }
1175 mtx_unlock(&vd->mtx);
1176 return (revents);
1177 }
1178
1179 static void video_kqfilter_detach(struct knote *kn);
1180 static int video_kqfilter_event(struct knote *kn, long hint);
1181
1182 static const struct filterops video_read_filterops = {
1183 .f_isfd = 1,
1184 .f_detach = video_kqfilter_detach,
1185 .f_event = video_kqfilter_event,
1186 };
1187
1188 static void
video_kqfilter_detach(struct knote * kn)1189 video_kqfilter_detach(struct knote *kn)
1190 {
1191 struct video_device *vd = kn->kn_hook;
1192
1193 knlist_remove(&vd->sel.si_note, kn, 0);
1194 }
1195
1196 static int
video_kqfilter_event(struct knote * kn,long hint)1197 video_kqfilter_event(struct knote *kn, long hint)
1198 {
1199 struct video_device *vd = kn->kn_hook;
1200
1201 if (!STAILQ_EMPTY(&vd->done)) {
1202 kn->kn_data = 1;
1203 return (1);
1204 }
1205 return (0);
1206 }
1207
1208 static int
video_kqfilter(struct cdev * dev,struct knote * kn)1209 video_kqfilter(struct cdev *dev, struct knote *kn)
1210 {
1211 struct video_device *vd = dev->si_drv1;
1212
1213 if (vd == NULL || vd->dying)
1214 return (ENXIO);
1215
1216 switch (kn->kn_filter) {
1217 case EVFILT_READ:
1218 kn->kn_fop = &video_read_filterops;
1219 kn->kn_hook = vd;
1220 knlist_add(&vd->sel.si_note, kn, 0);
1221 return (0);
1222 default:
1223 return (EINVAL);
1224 }
1225 }
1226
1227 static int
video_mmap_single(struct cdev * dev,vm_ooffset_t * offset,vm_size_t size,vm_object_t * objp,int nprot)1228 video_mmap_single(struct cdev *dev, vm_ooffset_t *offset, vm_size_t size,
1229 vm_object_t *objp, int nprot)
1230 {
1231 struct video_device *vd = dev->si_drv1;
1232 struct video_file *vf;
1233 int error;
1234
1235 if (vd == NULL || vd->dying)
1236 return (ENXIO);
1237
1238 error = devfs_get_cdevpriv((void **)&vf);
1239 if (error != 0)
1240 return (error);
1241
1242 sx_slock(&vd->cfg_sx);
1243 if (vd->owner != vf || vd->pool == NULL || vd->mode != VMODE_MMAP) {
1244 sx_sunlock(&vd->cfg_sx);
1245 return (EINVAL);
1246 }
1247
1248 if (*offset >= vd->pool->map_size ||
1249 size > vd->pool->map_size - *offset) {
1250 sx_sunlock(&vd->cfg_sx);
1251 return (EINVAL);
1252 }
1253
1254 /*
1255 * Hand out a reference to the object backing the whole pool; the
1256 * offset selects which buffer within it gets mapped. The VM system
1257 * tracks the mapping lifetime through the object reference count,
1258 * so no per-mapping bookkeeping is needed here.
1259 */
1260 vm_object_reference(vd->pool->obj);
1261 *objp = vd->pool->obj;
1262 sx_sunlock(&vd->cfg_sx);
1263
1264 return (0);
1265 }
1266
1267 int
video_register(device_t dev,struct video_device ** vdp)1268 video_register(device_t dev, struct video_device **vdp)
1269 {
1270 struct make_dev_args args;
1271 struct video_device *vd;
1272 int taken[VIDEO_UNIT_RETRIES];
1273 int collisions, error, unit;
1274
1275 vd = malloc(sizeof(*vd), M_VIDEO, M_WAITOK | M_ZERO);
1276 vd->dev = dev;
1277
1278 sx_init(&vd->cfg_sx, "video_cfg");
1279 mtx_init(&vd->mtx, "video_mtx", NULL, MTX_DEF);
1280 STAILQ_INIT(&vd->queued);
1281 STAILQ_INIT(&vd->done);
1282 knlist_init_mtx(&vd->sel.si_note, &vd->mtx);
1283
1284 error = EEXIST;
1285 for (collisions = 0; collisions < VIDEO_UNIT_RETRIES; collisions++) {
1286 taken[collisions] = unit = alloc_unr(video_unrhdr);
1287
1288 make_dev_args_init(&args);
1289 args.mda_flags = MAKEDEV_CHECKNAME | MAKEDEV_WAITOK;
1290 args.mda_devsw = &video_cdevsw;
1291 args.mda_uid = UID_ROOT;
1292 args.mda_gid = GID_VIDEO;
1293 args.mda_mode = 0660;
1294 args.mda_unit = unit;
1295 args.mda_si_drv1 = vd;
1296
1297 error = make_dev_s(&args, &vd->cdev, "video%d", unit);
1298 if (error != EEXIST)
1299 break;
1300 }
1301
1302 while (collisions > 0)
1303 free_unr(video_unrhdr, taken[--collisions]);
1304
1305 if (error != 0) {
1306 free_unr(video_unrhdr, unit);
1307 device_printf(dev, "failed to create a /dev/video node: %d\n",
1308 error);
1309 knlist_destroy(&vd->sel.si_note);
1310 mtx_destroy(&vd->mtx);
1311 sx_destroy(&vd->cfg_sx);
1312 free(vd, M_VIDEO);
1313 return (error);
1314 }
1315 vd->unit = unit;
1316
1317 *vdp = vd;
1318 device_printf(dev, "registered as /dev/video%d\n", unit);
1319 return (0);
1320 }
1321
1322 void
video_unregister(struct video_device * vd)1323 video_unregister(struct video_device *vd)
1324 {
1325
1326 sx_xlock(&vd->cfg_sx);
1327 vd->dying = true;
1328
1329 if (vd->streaming) {
1330 mtx_lock(&vd->mtx);
1331 vd->stopping = true;
1332 wakeup(&vd->done);
1333 mtx_unlock(&vd->mtx);
1334
1335 VIDEO_STOP_STREAM(vd->dev);
1336
1337 mtx_lock(&vd->mtx);
1338 vd->streaming = false;
1339 vd->stopping = false;
1340 STAILQ_INIT(&vd->queued);
1341 STAILQ_INIT(&vd->done);
1342 mtx_unlock(&vd->mtx);
1343 }
1344
1345 sx_xunlock(&vd->cfg_sx);
1346
1347 destroy_dev(vd->cdev);
1348 free_unr(video_unrhdr, vd->unit);
1349
1350 video_pool_detach(vd);
1351
1352 knlist_destroy(&vd->sel.si_note);
1353 mtx_destroy(&vd->mtx);
1354 sx_destroy(&vd->cfg_sx);
1355
1356 free(vd, M_VIDEO);
1357 }
1358
1359 static int
video_modevent(module_t mod,int type,void * data)1360 video_modevent(module_t mod, int type, void *data)
1361 {
1362
1363 switch (type) {
1364 case MOD_LOAD:
1365 video_unrhdr = new_unrhdr(0, INT_MAX, NULL);
1366 return (0);
1367 case MOD_UNLOAD:
1368 delete_unrhdr(video_unrhdr);
1369 return (0);
1370 default:
1371 return (EOPNOTSUPP);
1372 }
1373 }
1374
1375 static moduledata_t video_mod = {
1376 "video",
1377 video_modevent,
1378 NULL,
1379 };
1380
1381 DECLARE_MODULE(video, video_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
1382 MODULE_VERSION(video, 1);
1383