1#![allow(dead_code)]
10
11#[cfg(feature = "alloc")]
12extern crate alloc;
13
14use core::sync::atomic::{AtomicBool, Ordering};
15
16use spin::Mutex;
17
18use crate::error::KernelError;
19
20const VIDIOC_BASE: u32 = 0x5600;
26
27const VIDIOC_QUERYCAP: u32 = VIDIOC_BASE;
29const VIDIOC_ENUM_FMT: u32 = VIDIOC_BASE + 0x02;
31const VIDIOC_G_FMT: u32 = VIDIOC_BASE + 0x04;
33const VIDIOC_S_FMT: u32 = VIDIOC_BASE + 0x05;
35const VIDIOC_REQBUFS: u32 = VIDIOC_BASE + 0x08;
37const VIDIOC_QUERYBUF: u32 = VIDIOC_BASE + 0x09;
39const VIDIOC_QBUF: u32 = VIDIOC_BASE + 0x0F;
41const VIDIOC_DQBUF: u32 = VIDIOC_BASE + 0x11;
43const VIDIOC_STREAMON: u32 = VIDIOC_BASE + 0x12;
45const VIDIOC_STREAMOFF: u32 = VIDIOC_BASE + 0x13;
47
48const V4L2_PIX_FMT_YUYV: u32 = fourcc(b'Y', b'U', b'Y', b'V');
54const V4L2_PIX_FMT_RGB24: u32 = fourcc(b'R', b'G', b'B', b'3');
56const V4L2_PIX_FMT_BGR24: u32 = fourcc(b'B', b'G', b'R', b'3');
58const V4L2_PIX_FMT_MJPEG: u32 = fourcc(b'M', b'J', b'P', b'G');
60
61const fn fourcc(a: u8, b: u8, c: u8, d: u8) -> u32 {
63 (a as u32) | ((b as u32) << 8) | ((c as u32) << 16) | ((d as u32) << 24)
64}
65
66const V4L2_CAP_VIDEO_CAPTURE: u32 = 0x0000_0001;
72const V4L2_CAP_STREAMING: u32 = 0x0400_0000;
74const V4L2_CAP_READWRITE: u32 = 0x0100_0000;
76
77const V4L2_BUF_FLAG_MAPPED: u32 = 0x0001;
83const V4L2_BUF_FLAG_QUEUED: u32 = 0x0002;
85const V4L2_BUF_FLAG_DONE: u32 = 0x0004;
87
88const MAX_BUFFERS: usize = 4;
94const DEFAULT_WIDTH: u32 = 640;
96const DEFAULT_HEIGHT: u32 = 480;
98const YUYV_BYTES_PER_LINE: u32 = DEFAULT_WIDTH * 2;
100const YUYV_FRAME_SIZE: u32 = YUYV_BYTES_PER_LINE * DEFAULT_HEIGHT;
102const MAX_DRIVER_NAME: usize = 16;
104const MAX_CARD_NAME: usize = 32;
106const MAX_BUS_INFO: usize = 32;
108
109const NUM_COLOR_BARS: usize = 8;
111
112#[derive(Debug, Clone)]
118pub struct V4l2Capability {
119 pub driver: [u8; MAX_DRIVER_NAME],
121 pub card: [u8; MAX_CARD_NAME],
123 pub bus_info: [u8; MAX_BUS_INFO],
125 pub version: u32,
127 pub capabilities: u32,
129 pub device_caps: u32,
131}
132
133impl Default for V4l2Capability {
134 fn default() -> Self {
135 let mut cap = Self {
136 driver: [0u8; MAX_DRIVER_NAME],
137 card: [0u8; MAX_CARD_NAME],
138 bus_info: [0u8; MAX_BUS_INFO],
139 version: 0x0016_0000, capabilities: V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE,
141 device_caps: V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
142 };
143 copy_str_to_buf(&mut cap.driver, b"veridian-v4l2");
144 copy_str_to_buf(&mut cap.card, b"VeridianOS Virtual Camera");
145 copy_str_to_buf(&mut cap.bus_info, b"platform:veridian-v4l2");
146 cap
147 }
148}
149
150#[derive(Debug, Clone, Copy)]
152pub struct V4l2PixFormat {
153 pub width: u32,
155 pub height: u32,
157 pub pixelformat: u32,
159 pub bytesperline: u32,
161 pub sizeimage: u32,
163}
164
165impl Default for V4l2PixFormat {
166 fn default() -> Self {
167 Self {
168 width: DEFAULT_WIDTH,
169 height: DEFAULT_HEIGHT,
170 pixelformat: V4L2_PIX_FMT_YUYV,
171 bytesperline: YUYV_BYTES_PER_LINE,
172 sizeimage: YUYV_FRAME_SIZE,
173 }
174 }
175}
176
177#[derive(Debug, Clone, Copy)]
179pub struct V4l2FmtDesc {
180 pub index: u32,
182 pub pixelformat: u32,
184 pub description: [u8; 32],
186 pub flags: u32,
188}
189
190#[derive(Debug, Clone, Copy, PartialEq, Eq)]
192pub enum V4l2BufState {
193 Idle,
195 Queued,
197 Done,
199}
200
201#[derive(Debug, Clone, Copy)]
203pub struct V4l2Buffer {
204 pub index: u32,
206 pub state: V4l2BufState,
208 pub flags: u32,
210 pub offset: u32,
212 pub bytesused: u32,
214 pub sequence: u32,
216}
217
218impl Default for V4l2Buffer {
219 fn default() -> Self {
220 Self {
221 index: 0,
222 state: V4l2BufState::Idle,
223 flags: 0,
224 offset: 0,
225 bytesused: 0,
226 sequence: 0,
227 }
228 }
229}
230
231pub struct V4l2Device {
233 capability: V4l2Capability,
235 format: V4l2PixFormat,
237 buffers: [V4l2Buffer; MAX_BUFFERS],
239 num_buffers: u32,
241 streaming: bool,
243 frame_counter: u32,
245 queue_head: usize,
247 queue_tail: usize,
249 queued_count: usize,
251}
252
253impl Default for V4l2Device {
254 fn default() -> Self {
255 Self::new()
256 }
257}
258
259impl V4l2Device {
260 pub const fn new() -> Self {
262 const DEFAULT_BUF: V4l2Buffer = V4l2Buffer {
263 index: 0,
264 state: V4l2BufState::Idle,
265 flags: 0,
266 offset: 0,
267 bytesused: 0,
268 sequence: 0,
269 };
270 Self {
271 capability: V4l2Capability {
272 driver: [0u8; MAX_DRIVER_NAME],
273 card: [0u8; MAX_CARD_NAME],
274 bus_info: [0u8; MAX_BUS_INFO],
275 version: 0x0016_0000,
276 capabilities: V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING | V4L2_CAP_READWRITE,
277 device_caps: V4L2_CAP_VIDEO_CAPTURE | V4L2_CAP_STREAMING,
278 },
279 format: V4l2PixFormat {
280 width: DEFAULT_WIDTH,
281 height: DEFAULT_HEIGHT,
282 pixelformat: V4L2_PIX_FMT_YUYV,
283 bytesperline: YUYV_BYTES_PER_LINE,
284 sizeimage: YUYV_FRAME_SIZE,
285 },
286 buffers: [DEFAULT_BUF; MAX_BUFFERS],
287 num_buffers: 0,
288 streaming: false,
289 frame_counter: 0,
290 queue_head: 0,
291 queue_tail: 0,
292 queued_count: 0,
293 }
294 }
295
296 pub fn init(&mut self) {
298 self.capability = V4l2Capability::default();
299 self.format = V4l2PixFormat::default();
300 self.frame_counter = 0;
301 self.streaming = false;
302 self.num_buffers = 0;
303 self.queue_head = 0;
304 self.queue_tail = 0;
305 self.queued_count = 0;
306 }
307
308 pub fn query_cap(&self) -> V4l2Capability {
310 self.capability.clone()
311 }
312
313 pub fn enum_fmt(&self, index: u32) -> Option<V4l2FmtDesc> {
315 match index {
316 0 => {
317 let mut desc = V4l2FmtDesc {
318 index: 0,
319 pixelformat: V4L2_PIX_FMT_YUYV,
320 description: [0u8; 32],
321 flags: 0,
322 };
323 copy_str_to_buf(&mut desc.description, b"YUYV 4:2:2");
324 Some(desc)
325 }
326 1 => {
327 let mut desc = V4l2FmtDesc {
328 index: 1,
329 pixelformat: V4L2_PIX_FMT_RGB24,
330 description: [0u8; 32],
331 flags: 0,
332 };
333 copy_str_to_buf(&mut desc.description, b"RGB24");
334 Some(desc)
335 }
336 _ => None,
337 }
338 }
339
340 pub fn get_format(&self) -> V4l2PixFormat {
342 self.format
343 }
344
345 pub fn set_format(&mut self, fmt: V4l2PixFormat) -> Result<V4l2PixFormat, KernelError> {
347 if self.streaming {
348 return Err(KernelError::InvalidState {
349 expected: "idle",
350 actual: "busy",
351 });
352 }
353
354 let width = clamp(fmt.width, 160, 1920);
356 let height = clamp(fmt.height, 120, 1080);
357
358 let pixelformat = if fmt.pixelformat == V4L2_PIX_FMT_RGB24 {
360 V4L2_PIX_FMT_RGB24
361 } else {
362 V4L2_PIX_FMT_YUYV
363 };
364
365 let bytesperline = if pixelformat == V4L2_PIX_FMT_YUYV {
366 width.checked_mul(2).unwrap_or(width)
367 } else {
368 width.checked_mul(3).unwrap_or(width)
369 };
370
371 let sizeimage = bytesperline.checked_mul(height).unwrap_or(bytesperline);
372
373 self.format = V4l2PixFormat {
374 width,
375 height,
376 pixelformat,
377 bytesperline,
378 sizeimage,
379 };
380
381 Ok(self.format)
382 }
383
384 pub fn request_buffers(&mut self, count: u32) -> Result<u32, KernelError> {
386 if self.streaming {
387 return Err(KernelError::InvalidState {
388 expected: "idle",
389 actual: "busy",
390 });
391 }
392
393 let actual = if count > MAX_BUFFERS as u32 {
394 MAX_BUFFERS as u32
395 } else {
396 count
397 };
398
399 self.num_buffers = actual;
400 for i in 0..actual as usize {
401 self.buffers[i] = V4l2Buffer {
402 index: i as u32,
403 state: V4l2BufState::Idle,
404 flags: 0,
405 offset: (i as u32).checked_mul(self.format.sizeimage).unwrap_or(0),
406 bytesused: 0,
407 sequence: 0,
408 };
409 }
410
411 self.queue_head = 0;
412 self.queue_tail = 0;
413 self.queued_count = 0;
414
415 Ok(actual)
416 }
417
418 pub fn query_buffer(&self, index: u32) -> Option<V4l2Buffer> {
420 if index < self.num_buffers {
421 Some(self.buffers[index as usize])
422 } else {
423 None
424 }
425 }
426
427 pub fn queue_buffer(&mut self, index: u32) -> Result<(), KernelError> {
429 if index >= self.num_buffers {
430 return Err(KernelError::InvalidArgument {
431 name: "v4l2",
432 value: "invalid",
433 });
434 }
435
436 let buf = &mut self.buffers[index as usize];
437 if buf.state != V4l2BufState::Idle {
438 return Err(KernelError::InvalidState {
439 expected: "idle",
440 actual: "busy",
441 });
442 }
443
444 buf.state = V4l2BufState::Queued;
445 buf.flags = V4L2_BUF_FLAG_QUEUED;
446 self.queued_count += 1;
447 Ok(())
448 }
449
450 pub fn dequeue_buffer(&mut self) -> Result<V4l2Buffer, KernelError> {
452 if !self.streaming {
453 return Err(KernelError::InvalidState {
454 expected: "idle",
455 actual: "busy",
456 });
457 }
458
459 for i in 0..self.num_buffers as usize {
461 if self.buffers[i].state == V4l2BufState::Done {
462 self.buffers[i].state = V4l2BufState::Idle;
463 self.buffers[i].flags = 0;
464 let result = self.buffers[i];
465 return Ok(result);
466 }
467 }
468
469 Err(KernelError::WouldBlock)
471 }
472
473 pub fn stream_on(&mut self) -> Result<(), KernelError> {
475 if self.streaming {
476 return Err(KernelError::InvalidState {
477 expected: "idle",
478 actual: "busy",
479 });
480 }
481 if self.num_buffers == 0 {
482 return Err(KernelError::InvalidArgument {
483 name: "v4l2",
484 value: "invalid",
485 });
486 }
487 self.streaming = true;
488 self.frame_counter = 0;
489 Ok(())
490 }
491
492 pub fn stream_off(&mut self) -> Result<(), KernelError> {
494 self.streaming = false;
495
496 for i in 0..self.num_buffers as usize {
498 self.buffers[i].state = V4l2BufState::Idle;
499 self.buffers[i].flags = 0;
500 }
501 self.queued_count = 0;
502 Ok(())
503 }
504
505 pub fn generate_test_frame(&mut self, output: &mut [u8]) -> Result<usize, KernelError> {
510 if !self.streaming {
511 return Err(KernelError::InvalidState {
512 expected: "idle",
513 actual: "busy",
514 });
515 }
516
517 let mut buf_idx = None;
519 for i in 0..self.num_buffers as usize {
520 if self.buffers[i].state == V4l2BufState::Queued {
521 buf_idx = Some(i);
522 break;
523 }
524 }
525
526 let idx = buf_idx.ok_or(KernelError::WouldBlock)?;
527
528 let width = self.format.width as usize;
529 let height = self.format.height as usize;
530 let frame_size = width * height * 2; if output.len() < frame_size {
533 return Err(KernelError::ResourceExhausted {
534 resource: "v4l2 buffer",
535 });
536 }
537
538 generate_color_bars_yuyv(output, width, height, self.frame_counter);
541
542 self.buffers[idx].state = V4l2BufState::Done;
544 self.buffers[idx].flags = V4L2_BUF_FLAG_DONE;
545 self.buffers[idx].bytesused = frame_size as u32;
546 self.buffers[idx].sequence = self.frame_counter;
547 self.frame_counter = self.frame_counter.wrapping_add(1);
548
549 if self.queued_count > 0 {
550 self.queued_count -= 1;
551 }
552
553 Ok(frame_size)
554 }
555
556 pub fn is_streaming(&self) -> bool {
558 self.streaming
559 }
560
561 pub fn frame_counter(&self) -> u32 {
563 self.frame_counter
564 }
565
566 pub fn num_buffers(&self) -> u32 {
568 self.num_buffers
569 }
570}
571
572const COLOR_BAR_RGB: [(u8, u8, u8); NUM_COLOR_BARS] = [
579 (255, 255, 255), (255, 255, 0), (0, 255, 255), (0, 255, 0), (255, 0, 255), (255, 0, 0), (0, 0, 255), (0, 0, 0), ];
588
589fn rgb_to_y(r: u8, g: u8, b: u8) -> u8 {
594 let r32 = r as u32;
595 let g32 = g as u32;
596 let b32 = b as u32;
597 let y = ((66u32
598 .checked_mul(r32)
599 .unwrap_or(0)
600 .checked_add(129u32.checked_mul(g32).unwrap_or(0))
601 .unwrap_or(0)
602 .checked_add(25u32.checked_mul(b32).unwrap_or(0))
603 .unwrap_or(0)
604 .checked_add(128)
605 .unwrap_or(0))
606 >> 8)
607 .checked_add(16)
608 .unwrap_or(16);
609 if y > 255 {
610 255u8
611 } else {
612 y as u8
613 }
614}
615
616fn rgb_to_u(r: u8, g: u8, b: u8) -> u8 {
620 let r32 = r as i32;
621 let g32 = g as i32;
622 let b32 = b as i32;
623 let u = ((-38i32 * r32 - 74i32 * g32 + 112i32 * b32 + 128i32) >> 8) + 128i32;
624 if u < 0 {
625 0u8
626 } else if u > 255 {
627 255u8
628 } else {
629 u as u8
630 }
631}
632
633fn rgb_to_v(r: u8, g: u8, b: u8) -> u8 {
637 let r32 = r as i32;
638 let g32 = g as i32;
639 let b32 = b as i32;
640 let v = ((112i32 * r32 - 94i32 * g32 - 18i32 * b32 + 128i32) >> 8) + 128i32;
641 if v < 0 {
642 0u8
643 } else if v > 255 {
644 255u8
645 } else {
646 v as u8
647 }
648}
649
650fn generate_color_bars_yuyv(output: &mut [u8], width: usize, height: usize, frame_num: u32) {
654 let bar_width = width / NUM_COLOR_BARS;
655
656 for y in 0..height {
657 let row_offset = y * width * 2;
658
659 let mut x = 0usize;
661 while x < width {
662 let bar0 = if bar_width > 0 { x / bar_width } else { 0 };
663 let bar1 = if bar_width > 0 {
664 (x + 1) / bar_width
665 } else {
666 0
667 };
668 let bar0 = if bar0 >= NUM_COLOR_BARS {
669 NUM_COLOR_BARS - 1
670 } else {
671 bar0
672 };
673 let bar1 = if bar1 >= NUM_COLOR_BARS {
674 NUM_COLOR_BARS - 1
675 } else {
676 bar1
677 };
678
679 let (r0, g0, b0) = COLOR_BAR_RGB[bar0];
680 let (r1, g1, b1) = COLOR_BAR_RGB[bar1];
681
682 let y0 = rgb_to_y(r0, g0, b0);
683 let y1 = rgb_to_y(r1, g1, b1);
684 let u = rgb_to_u(
686 ((r0 as u16 + r1 as u16) / 2) as u8,
687 ((g0 as u16 + g1 as u16) / 2) as u8,
688 ((b0 as u16 + b1 as u16) / 2) as u8,
689 );
690 let v = rgb_to_v(
691 ((r0 as u16 + r1 as u16) / 2) as u8,
692 ((g0 as u16 + g1 as u16) / 2) as u8,
693 ((b0 as u16 + b1 as u16) / 2) as u8,
694 );
695
696 let offset = row_offset + x * 2;
697 if offset + 3 < output.len() {
698 output[offset] = y0;
699 output[offset + 1] = u;
700 output[offset + 2] = y1;
701 output[offset + 3] = v;
702 }
703
704 x += 2;
705 }
706 }
707
708 let marker_val = if frame_num & 1 == 0 { 235u8 } else { 16u8 };
711 for py in 0..8usize {
712 if py >= height {
713 break;
714 }
715 for px in 0..8usize {
716 if px >= width {
717 break;
718 }
719 let offset = py * width * 2 + px * 2;
720 if offset < output.len() {
721 output[offset] = marker_val;
722 }
723 }
724 }
725}
726
727pub fn v4l2_ioctl(device: &mut V4l2Device, cmd: u32, arg: u64) -> Result<u64, KernelError> {
733 match cmd {
734 VIDIOC_QUERYCAP => {
735 let _cap = device.query_cap();
736 Ok(0)
737 }
738 VIDIOC_ENUM_FMT => {
739 let index = arg as u32;
740 match device.enum_fmt(index) {
741 Some(_desc) => Ok(0),
742 None => Err(KernelError::InvalidArgument {
743 name: "v4l2",
744 value: "invalid",
745 }),
746 }
747 }
748 VIDIOC_G_FMT => {
749 let _fmt = device.get_format();
750 Ok(0)
751 }
752 VIDIOC_S_FMT => {
753 let fmt = V4l2PixFormat {
755 width: (arg & 0xFFFF) as u32,
756 height: ((arg >> 16) & 0xFFFF) as u32,
757 pixelformat: V4L2_PIX_FMT_YUYV,
758 bytesperline: 0, sizeimage: 0, };
761 device.set_format(fmt)?;
762 Ok(0)
763 }
764 VIDIOC_REQBUFS => {
765 let count = arg as u32;
766 let actual = device.request_buffers(count)?;
767 Ok(actual as u64)
768 }
769 VIDIOC_QUERYBUF => {
770 let index = arg as u32;
771 match device.query_buffer(index) {
772 Some(_buf) => Ok(0),
773 None => Err(KernelError::InvalidArgument {
774 name: "v4l2",
775 value: "invalid",
776 }),
777 }
778 }
779 VIDIOC_QBUF => {
780 let index = arg as u32;
781 device.queue_buffer(index)?;
782 Ok(0)
783 }
784 VIDIOC_DQBUF => {
785 let buf = device.dequeue_buffer()?;
786 Ok(buf.index as u64)
787 }
788 VIDIOC_STREAMON => {
789 device.stream_on()?;
790 Ok(0)
791 }
792 VIDIOC_STREAMOFF => {
793 device.stream_off()?;
794 Ok(0)
795 }
796 _ => Err(KernelError::InvalidArgument {
797 name: "v4l2",
798 value: "invalid",
799 }),
800 }
801}
802
803static V4L2_DEVICE: Mutex<V4l2Device> = Mutex::new(V4l2Device::new());
808
809static V4L2_INITIALIZED: AtomicBool = AtomicBool::new(false);
810
811pub fn v4l2_init() {
813 let mut dev = V4L2_DEVICE.lock();
814 dev.init();
815 V4L2_INITIALIZED.store(true, Ordering::Release);
816 crate::println!("[V4L2] Virtual camera device initialized (640x480 YUYV)");
817}
818
819pub fn v4l2_global_ioctl(cmd: u32, arg: u64) -> Result<u64, KernelError> {
821 if !V4L2_INITIALIZED.load(Ordering::Acquire) {
822 return Err(KernelError::NotInitialized { subsystem: "v4l2" });
823 }
824 let mut dev = V4L2_DEVICE.lock();
825 v4l2_ioctl(&mut dev, cmd, arg)
826}
827
828fn copy_str_to_buf(buf: &mut [u8], src: &[u8]) {
834 let len = if src.len() < buf.len() - 1 {
835 src.len()
836 } else {
837 buf.len() - 1
838 };
839 buf[..len].copy_from_slice(&src[..len]);
840 buf[len] = 0;
841}
842
843fn clamp(val: u32, min: u32, max: u32) -> u32 {
845 if val < min {
846 min
847 } else if val > max {
848 max
849 } else {
850 val
851 }
852}
853
854#[cfg(test)]
859mod tests {
860 use super::*;
861
862 #[test]
863 fn test_fourcc() {
864 let yuyv = fourcc(b'Y', b'U', b'Y', b'V');
865 assert_ne!(yuyv, 0);
866 assert_eq!(yuyv & 0xFF, b'Y' as u32);
868 assert_eq!((yuyv >> 8) & 0xFF, b'U' as u32);
869 }
870
871 #[test]
872 fn test_rgb_to_y() {
873 let y_white = rgb_to_y(255, 255, 255);
875 assert!(y_white > 200);
876
877 let y_black = rgb_to_y(0, 0, 0);
879 assert!(y_black < 30);
880
881 assert!(y_white <= 255);
883 assert!(y_black >= 16);
884 }
885
886 #[test]
887 fn test_rgb_to_u() {
888 let u_gray = rgb_to_u(128, 128, 128);
890 assert!((u_gray as i32 - 128).unsigned_abs() < 5);
891 }
892
893 #[test]
894 fn test_rgb_to_v() {
895 let v_gray = rgb_to_v(128, 128, 128);
897 assert!((v_gray as i32 - 128).unsigned_abs() < 5);
898 }
899
900 #[test]
901 fn test_v4l2_capability_default() {
902 let cap = V4l2Capability::default();
903 assert!(cap.capabilities & V4L2_CAP_VIDEO_CAPTURE != 0);
904 assert!(cap.capabilities & V4L2_CAP_STREAMING != 0);
905 assert_eq!(cap.driver[0], b'v');
906 }
907
908 #[test]
909 fn test_v4l2_device_new() {
910 let dev = V4l2Device::new();
911 assert!(!dev.is_streaming());
912 assert_eq!(dev.frame_counter(), 0);
913 assert_eq!(dev.num_buffers(), 0);
914 }
915
916 #[test]
917 fn test_v4l2_device_init() {
918 let mut dev = V4l2Device::new();
919 dev.init();
920 let fmt = dev.get_format();
921 assert_eq!(fmt.width, DEFAULT_WIDTH);
922 assert_eq!(fmt.height, DEFAULT_HEIGHT);
923 assert_eq!(fmt.pixelformat, V4L2_PIX_FMT_YUYV);
924 }
925
926 #[test]
927 fn test_v4l2_enum_fmt() {
928 let mut dev = V4l2Device::new();
929 dev.init();
930
931 assert!(dev.enum_fmt(0).is_some());
932 assert!(dev.enum_fmt(1).is_some());
933 assert!(dev.enum_fmt(2).is_none());
934 }
935
936 #[test]
937 fn test_v4l2_set_format() {
938 let mut dev = V4l2Device::new();
939 dev.init();
940
941 let fmt = V4l2PixFormat {
942 width: 320,
943 height: 240,
944 pixelformat: V4L2_PIX_FMT_YUYV,
945 bytesperline: 0,
946 sizeimage: 0,
947 };
948 let result = dev.set_format(fmt);
949 assert!(result.is_ok());
950
951 let actual = result.unwrap();
952 assert_eq!(actual.width, 320);
953 assert_eq!(actual.height, 240);
954 assert_eq!(actual.bytesperline, 640); }
956
957 #[test]
958 fn test_v4l2_set_format_clamping() {
959 let mut dev = V4l2Device::new();
960 dev.init();
961
962 let fmt = V4l2PixFormat {
963 width: 10000,
964 height: 10000,
965 pixelformat: V4L2_PIX_FMT_YUYV,
966 bytesperline: 0,
967 sizeimage: 0,
968 };
969 let result = dev.set_format(fmt).unwrap();
970 assert_eq!(result.width, 1920);
971 assert_eq!(result.height, 1080);
972 }
973
974 #[test]
975 fn test_v4l2_request_buffers() {
976 let mut dev = V4l2Device::new();
977 dev.init();
978
979 let count = dev.request_buffers(4).unwrap();
980 assert_eq!(count, 4);
981 assert_eq!(dev.num_buffers(), 4);
982 }
983
984 #[test]
985 fn test_v4l2_request_buffers_capped() {
986 let mut dev = V4l2Device::new();
987 dev.init();
988
989 let count = dev.request_buffers(100).unwrap();
990 assert_eq!(count, MAX_BUFFERS as u32);
991 }
992
993 #[test]
994 fn test_v4l2_query_buffer() {
995 let mut dev = V4l2Device::new();
996 dev.init();
997 dev.request_buffers(4).unwrap();
998
999 let buf = dev.query_buffer(0);
1000 assert!(buf.is_some());
1001 let buf = buf.unwrap();
1002 assert_eq!(buf.index, 0);
1003 assert_eq!(buf.state, V4l2BufState::Idle);
1004
1005 assert!(dev.query_buffer(4).is_none());
1006 }
1007
1008 #[test]
1009 fn test_v4l2_queue_dequeue() {
1010 let mut dev = V4l2Device::new();
1011 dev.init();
1012 dev.request_buffers(2).unwrap();
1013
1014 assert!(dev.queue_buffer(0).is_ok());
1016
1017 assert!(dev.dequeue_buffer().is_err());
1019
1020 assert!(dev.stream_on().is_ok());
1022
1023 let mut frame = [0u8; (DEFAULT_WIDTH * DEFAULT_HEIGHT * 2) as usize];
1025 assert!(dev.generate_test_frame(&mut frame).is_ok());
1026
1027 let buf = dev.dequeue_buffer();
1029 assert!(buf.is_ok());
1030 assert_eq!(buf.unwrap().sequence, 0);
1031 }
1032
1033 #[test]
1034 fn test_v4l2_stream_on_off() {
1035 let mut dev = V4l2Device::new();
1036 dev.init();
1037 dev.request_buffers(2).unwrap();
1038
1039 assert!(dev.stream_on().is_ok());
1040 assert!(dev.is_streaming());
1041
1042 assert!(dev.stream_on().is_err());
1044
1045 assert!(dev.stream_off().is_ok());
1046 assert!(!dev.is_streaming());
1047 }
1048
1049 #[test]
1050 fn test_v4l2_no_buffers_stream_on() {
1051 let mut dev = V4l2Device::new();
1052 dev.init();
1053
1054 assert!(dev.stream_on().is_err());
1056 }
1057
1058 #[test]
1059 fn test_v4l2_ioctl_dispatch() {
1060 let mut dev = V4l2Device::new();
1061 dev.init();
1062
1063 assert!(v4l2_ioctl(&mut dev, VIDIOC_QUERYCAP, 0).is_ok());
1064 assert!(v4l2_ioctl(&mut dev, VIDIOC_ENUM_FMT, 0).is_ok());
1065 assert!(v4l2_ioctl(&mut dev, VIDIOC_ENUM_FMT, 99).is_err());
1066 assert!(v4l2_ioctl(&mut dev, VIDIOC_G_FMT, 0).is_ok());
1067
1068 let result = v4l2_ioctl(&mut dev, VIDIOC_REQBUFS, 4);
1070 assert!(result.is_ok());
1071 assert_eq!(result.unwrap(), 4);
1072
1073 assert!(v4l2_ioctl(&mut dev, 0xFFFF, 0).is_err());
1075 }
1076
1077 #[test]
1078 fn test_generate_color_bars() {
1079 let width = 64usize;
1080 let height = 4usize;
1081 let mut buf = [0u8; 64 * 4 * 2];
1082 generate_color_bars_yuyv(&mut buf, width, height, 0);
1083
1084 assert!(buf[0] > 200); }
1087
1088 #[test]
1089 fn test_copy_str_to_buf() {
1090 let mut buf = [0xFFu8; 16];
1091 copy_str_to_buf(&mut buf, b"hello");
1092 assert_eq!(&buf[..5], b"hello");
1093 assert_eq!(buf[5], 0);
1094 }
1095
1096 #[test]
1097 fn test_clamp() {
1098 assert_eq!(clamp(50, 0, 100), 50);
1099 assert_eq!(clamp(0, 10, 100), 10);
1100 assert_eq!(clamp(200, 10, 100), 100);
1101 }
1102
1103 #[test]
1104 fn test_ioctl_constants() {
1105 assert_eq!(VIDIOC_QUERYCAP, 0x5600);
1106 assert_eq!(VIDIOC_ENUM_FMT, 0x5602);
1107 assert_eq!(VIDIOC_STREAMON, 0x5612);
1108 assert_eq!(VIDIOC_STREAMOFF, 0x5613);
1109 }
1110}