video(4)
Source: sys/dev/video/video.c, video.h, video_if.m,
video_internal.h, sys/sys/videoio.h, sys/modules/video/.
Derived from OpenBSD’s uvideo(4) framework layer.
What it is
Section titled “What it is”The layer between a camera driver and userland. Drivers register a
device and implement a small kobj ops table; the framework owns
/dev/videoN, the V4L2 ioctl surface, the buffer pool, mmap, read(2),
poll and kqueue. A driver never sees a v4l2 struct and never sees a user
page.
Two backends register with it today: uvideo(4) for USB Video Class, and fwcam(4) for FireWire IIDC cameras.
The contract
Section titled “The contract”Six methods are mandatory: querycap, enum_format, get_format,
set_format, start_stream, stop_stream. Everything else defaults to
ENOTTY. One contract line carries the whole design:
stop_stream must quiesce the provider before returning. Every
teardown path in the framework leans on it.
The driver-side data path is four calls, safe from completion context:
vb = video_buf_acquire(vd); /* NULL: nothing queued, drop frame */video_buf_write(vb, 0, buf, len);video_buf_done(vb, len, seq++); /* or video_buf_error(vb) */Buffer state machine
Section titled “Buffer state machine”Buffers live on two intrusive lists, queued (awaiting hardware) and
done (awaiting the consumer). An ACTIVE buffer is on neither: the
driver owns it.
video_buf_done and video_buf_error are no-ops when the buffer is not
ACTIVE. That guard, plus the quiesce contract, is the whole
race-with-STREAMOFF story: by the time the framework resets buffer
states, no driver can still be holding one.
An empty queued list at acquire time means userland fell behind; the
frame is dropped silently and only the driver counts it.
Ownership and modes
Section titled “Ownership and modes”The first file descriptor to set a format, request buffers, stream, or
read becomes the exclusive owner; that is when the driver’s open
method runs, not at open(2). Everyone else can still query.
Modes are mutually exclusive per device:
| Mode | Entered by | Buffers |
|---|---|---|
| mmap | REQBUFS count > 0 | 2-8, user queues and dequeues |
| read | first read(2) | 3, autoqueued, requeued on consumption |
Read mode is fully self-driving: first read starts the stream, every fully-consumed buffer goes straight back to QUEUED, partial reads keep an offset. Continuous capture with zero buffer management.
The one vm_object
Section titled “The one vm_object”The pool is a single wired OBJT_PHYS object created through
phys_pager_allocate, and mmap_single hands out references to that
whole object; the V4L2 m.offset picks the buffer inside it. Two
consequences:
- A bare
vm_object_allocatewould leave the phys pager ops NULL and fault on first touch, which is why the comment in the source insists on the allocation path. - Userland mappings hold real object references, so mapped frames stay valid after close and even after driver detach. Teardown only removes the kernel mapping.
Locking
Section titled “Locking”Two locks, strict order cfg_sx then mtx. The sx serializes
configuration and is held around driver method calls, so backends may
sleep. The mutex protects the lists, states and wakeups, and is the only
lock the video_buf_* calls take, which is what makes them
completion-context safe. read(2) bumps a readers count and drops the
mutex for uiomove; pool teardown drains that count before freeing, so
a pool never dies mid-copy.
Unregister sets a dying flag first, force-stops the stream, then
destroy_dev drains every cdevsw thread before memory goes away.
Seeing it on hardware
Section titled “Seeing it on hardware”kldload video uvideo # or fwcamls /dev/video*crucible (iMac17,1) is the working uvideo box; fwcam adds FireWire
cameras to the same /dev/videoN surface.