1extern crate alloc;
17
18use alloc::{collections::BTreeMap, sync::Arc, vec, vec::Vec};
19use core::any::Any;
20use spin::RwLock;
21
22use crate::device::{
23 Device, DeviceType, char::CharDevice, graphics::manager::FramebufferResource,
24 manager::DeviceManager,
25};
26use crate::object::capability::selectable::Selectable;
27use crate::object::capability::{ControlOps, MemoryMappingOps};
28
29pub mod framebuffer_commands {
32 pub const FBIOGET_VSCREENINFO: u32 = 0x4600;
34 pub const FBIOPUT_VSCREENINFO: u32 = 0x4601;
36 pub const FBIOGET_FSCREENINFO: u32 = 0x4602;
38 pub const FBIO_FLUSH: u32 = 0x4620;
40}
41
42#[repr(C)]
44#[derive(Debug, Clone, Copy)]
45pub struct FbVarScreenInfo {
46 pub xres: u32,
48 pub yres: u32,
50 pub xres_virtual: u32,
52 pub yres_virtual: u32,
54 pub xoffset: u32,
56 pub yoffset: u32,
58 pub bits_per_pixel: u32,
60 pub grayscale: u32,
62 pub red: FbBitfield,
64 pub green: FbBitfield,
66 pub blue: FbBitfield,
68 pub transp: FbBitfield,
70 pub nonstd: u32,
72 pub activate: u32,
74 pub height: u32,
76 pub width: u32,
78 pub accel_flags: u32,
80 pub pixclock: u32,
82 pub left_margin: u32,
84 pub right_margin: u32,
86 pub upper_margin: u32,
88 pub lower_margin: u32,
90 pub hsync_len: u32,
92 pub vsync_len: u32,
94 pub sync: u32,
96 pub vmode: u32,
98 pub rotate: u32,
100 pub colorspace: u32,
102 pub reserved: [u32; 4],
104}
105
106impl Default for FbVarScreenInfo {
107 fn default() -> Self {
108 Self {
109 xres: 0,
110 yres: 0,
111 xres_virtual: 0,
112 yres_virtual: 0,
113 xoffset: 0,
114 yoffset: 0,
115 bits_per_pixel: 0,
116 grayscale: 0,
117 red: FbBitfield::default(),
118 green: FbBitfield::default(),
119 blue: FbBitfield::default(),
120 transp: FbBitfield::default(),
121 nonstd: 0,
122 activate: 0,
123 height: 0,
124 width: 0,
125 accel_flags: 0,
126 pixclock: 0,
127 left_margin: 0,
128 right_margin: 0,
129 upper_margin: 0,
130 lower_margin: 0,
131 hsync_len: 0,
132 vsync_len: 0,
133 sync: 0,
134 vmode: 0,
135 rotate: 0,
136 colorspace: 0,
137 reserved: [0; 4],
138 }
139 }
140}
141
142#[repr(C)]
144#[derive(Debug, Clone)]
145pub struct FbFixScreenInfo {
146 pub id: [u8; 16],
148 pub smem_start: usize,
150 pub smem_len: u32,
152 pub type_: u32,
154 pub type_aux: u32,
156 pub visual: u32,
158 pub xpanstep: u16,
160 pub ypanstep: u16,
162 pub ywrapstep: u16,
164 pub line_length: u32,
166 pub mmio_start: usize,
168 pub mmio_len: u32,
170 pub accel: u32,
172 pub capabilities: u16,
174 pub reserved: [u16; 2],
176}
177
178impl Default for FbFixScreenInfo {
179 fn default() -> Self {
180 Self {
181 id: [0; 16],
182 smem_start: 0,
183 smem_len: 0,
184 type_: 0,
185 type_aux: 0,
186 visual: 0,
187 xpanstep: 0,
188 ypanstep: 0,
189 ywrapstep: 0,
190 line_length: 0,
191 mmio_start: 0,
192 mmio_len: 0,
193 accel: 0,
194 capabilities: 0,
195 reserved: [0; 2],
196 }
197 }
198}
199
200#[repr(C)]
202#[derive(Debug, Clone, Copy)]
203pub struct FbBitfield {
204 pub offset: u32,
206 pub length: u32,
208 pub msb_right: u32,
210}
211
212impl Default for FbBitfield {
213 fn default() -> Self {
214 Self {
215 offset: 0,
216 length: 0,
217 msb_right: 0,
218 }
219 }
220}
221
222#[allow(dead_code)]
224#[derive(Debug, Clone)]
225struct MockMapping {
226 vaddr: usize,
227 length: usize,
228}
229
230pub struct FramebufferCharDevice {
239 fb_resource: Arc<FramebufferResource>,
241 mappings: RwLock<BTreeMap<usize, MockMapping>>, }
244
245impl FramebufferCharDevice {
246 pub fn new(fb_resource: Arc<FramebufferResource>) -> Self {
256 Self {
257 fb_resource,
258 mappings: RwLock::new(BTreeMap::new()),
259 }
260 }
261
262 pub fn get_framebuffer_name(&self) -> &str {
264 &self.fb_resource.logical_name
265 }
266}
267
268impl Device for FramebufferCharDevice {
269 fn device_type(&self) -> DeviceType {
270 DeviceType::Char
271 }
272
273 fn name(&self) -> &'static str {
274 "framebuffer"
275 }
276
277 fn as_any(&self) -> &dyn Any {
278 self
279 }
280
281 fn as_any_mut(&mut self) -> &mut dyn Any {
282 self
283 }
284
285 fn as_char_device(&self) -> Option<&dyn CharDevice> {
286 Some(self)
287 }
288}
289
290impl CharDevice for FramebufferCharDevice {
291 fn read_byte(&self) -> Option<u8> {
300 None
303 }
304
305 fn write_byte(&self, _byte: u8) -> Result<(), &'static str> {
318 Err("write_byte is not supported - use write_at through DevFileObject instead")
321 }
322
323 fn can_read(&self) -> bool {
329 let fb_resource = &self.fb_resource;
330 fb_resource.physical_addr != 0 && fb_resource.size > 0
331 }
332
333 fn can_write(&self) -> bool {
339 let fb_resource = &self.fb_resource;
340 fb_resource.physical_addr != 0 && fb_resource.size > 0
341 }
342
343 fn read_at(&self, position: u64, buffer: &mut [u8]) -> Result<usize, &'static str> {
354 let fb_resource = &self.fb_resource;
355
356 if fb_resource.physical_addr == 0 {
358 return Err("Invalid framebuffer address");
359 }
360
361 let logical_size = fb_resource.config.size();
363 let start_pos = position as usize;
364 if start_pos >= logical_size {
365 return Ok(0); }
367
368 let available = logical_size - start_pos;
369 let to_read = buffer.len().min(available);
370
371 unsafe {
378 let fb_ptr = fb_resource.physical_addr as *const u8;
379 let src_ptr = fb_ptr.add(start_pos);
380
381 for i in 0..to_read {
382 buffer[i] = core::ptr::read_volatile(src_ptr.add(i));
383 }
384 }
385
386 Ok(to_read)
387 }
388
389 fn write_at(&self, position: u64, buffer: &[u8]) -> Result<usize, &'static str> {
400 let fb_resource = &self.fb_resource;
401
402 if fb_resource.physical_addr == 0 {
404 return Err("Invalid framebuffer address");
405 }
406
407 let logical_size = fb_resource.config.size();
409 let start_pos = position as usize;
410 if start_pos >= logical_size {
411 return Err("Position beyond framebuffer size");
412 }
413
414 let available = logical_size - start_pos;
415 let to_write = buffer.len().min(available);
416
417 unsafe {
420 let fb_ptr = fb_resource.physical_addr as *mut u8;
421 let dst_ptr = fb_ptr.add(start_pos);
422
423 for i in 0..to_write {
424 core::ptr::write_volatile(dst_ptr.add(i), buffer[i]);
425 }
426 }
427
428 Ok(to_write)
429 }
430}
431
432impl ControlOps for FramebufferCharDevice {
433 fn control(&self, command: u32, arg: usize) -> Result<i32, &'static str> {
434 use framebuffer_commands::*;
435
436 match command {
437 FBIOGET_VSCREENINFO => self.handle_get_vscreeninfo(arg),
438 FBIOGET_FSCREENINFO => self.handle_get_fscreeninfo(arg),
439 FBIO_FLUSH => self.handle_flush(arg),
440 FBIOPUT_VSCREENINFO => self.handle_put_vscreeninfo(arg),
441 _ => Err("Unsupported framebuffer control command"),
442 }
443 }
444
445 fn supported_control_commands(&self) -> Vec<(u32, &'static str)> {
446 use framebuffer_commands::*;
447 vec![
448 (FBIOGET_VSCREENINFO, "Get variable screen information"),
449 (FBIOGET_FSCREENINFO, "Get fixed screen information"),
450 (FBIO_FLUSH, "Flush framebuffer to display"),
451 (FBIOPUT_VSCREENINFO, "Set variable screen information"),
452 ]
453 }
454}
455
456impl MemoryMappingOps for FramebufferCharDevice {
457 fn get_mapping_info(
458 &self,
459 offset: usize,
460 length: usize,
461 ) -> Result<(usize, usize, bool), &'static str> {
462 let fb_resource = &self.fb_resource;
463
464 if offset % crate::environment::PAGE_SIZE != 0 {
466 return Err("Framebuffer mmap offset must be page-aligned");
467 }
468 if length % crate::environment::PAGE_SIZE != 0 {
469 return Err("Framebuffer mmap length must be page-aligned");
470 }
471
472 if fb_resource.physical_addr == 0 || fb_resource.size == 0 {
474 return Err("Invalid framebuffer configuration");
475 }
476
477 if offset >= fb_resource.size {
480 return Err("Offset exceeds framebuffer size");
481 }
482
483 let available_size = fb_resource.size - offset;
484 if length > available_size {
485 return Err("Requested length exceeds available framebuffer size");
486 }
487
488 let kva = fb_resource.physical_addr + offset;
491 let paddr = crate::vm::get_kernel_vm_manager()
492 .translate_vaddr(kva)
493 .ok_or("Failed to translate framebuffer address")?;
494 let permissions = 0x3; let is_shared = true; Ok((paddr, permissions, is_shared))
498 }
499
500 fn on_mapped(&self, vaddr: usize, _paddr: usize, length: usize, _offset: usize) {
501 let mapping = MockMapping { vaddr, length };
503 self.mappings.write().insert(vaddr, mapping);
504 }
505
506 fn on_unmapped(&self, vaddr: usize, _length: usize) {
507 self.mappings.write().remove(&vaddr);
509 }
510
511 fn supports_mmap(&self) -> bool {
512 self.fb_resource.physical_addr != 0 && self.fb_resource.size > 0
513 }
514}
515
516impl Selectable for FramebufferCharDevice {
517 fn wait_until_ready(
518 &self,
519 _interest: crate::object::capability::selectable::ReadyInterest,
520 _trapframe: &mut crate::arch::Trapframe,
521 _timeout_ticks: Option<u64>,
522 ) -> crate::object::capability::selectable::SelectWaitOutcome {
523 crate::object::capability::selectable::SelectWaitOutcome::Ready
524 }
525}
526
527impl FramebufferCharDevice {
528 fn current_var_info(&self) -> FbVarScreenInfo {
530 let fb_resource = &self.fb_resource;
531 let config = &fb_resource.config;
532
533 let mut var_info = FbVarScreenInfo::default();
534 var_info.xres = config.width;
535 var_info.yres = config.height;
536 var_info.xres_virtual = config.width;
537 var_info.yres_virtual = config.height;
538 var_info.bits_per_pixel = (config.format.bytes_per_pixel() * 8) as u32;
539
540 match config.format {
541 super::PixelFormat::RGBA8888 => {
542 var_info.red = FbBitfield {
543 offset: 0,
544 length: 8,
545 msb_right: 0,
546 };
547 var_info.green = FbBitfield {
548 offset: 8,
549 length: 8,
550 msb_right: 0,
551 };
552 var_info.blue = FbBitfield {
553 offset: 16,
554 length: 8,
555 msb_right: 0,
556 };
557 var_info.transp = FbBitfield {
558 offset: 24,
559 length: 8,
560 msb_right: 0,
561 };
562 }
563 super::PixelFormat::BGRA8888 => {
564 var_info.blue = FbBitfield {
565 offset: 0,
566 length: 8,
567 msb_right: 0,
568 };
569 var_info.green = FbBitfield {
570 offset: 8,
571 length: 8,
572 msb_right: 0,
573 };
574 var_info.red = FbBitfield {
575 offset: 16,
576 length: 8,
577 msb_right: 0,
578 };
579 var_info.transp = FbBitfield {
580 offset: 24,
581 length: 8,
582 msb_right: 0,
583 };
584 }
585 super::PixelFormat::RGB888 => {
586 var_info.red = FbBitfield {
587 offset: 0,
588 length: 8,
589 msb_right: 0,
590 };
591 var_info.green = FbBitfield {
592 offset: 8,
593 length: 8,
594 msb_right: 0,
595 };
596 var_info.blue = FbBitfield {
597 offset: 16,
598 length: 8,
599 msb_right: 0,
600 };
601 var_info.transp = FbBitfield {
602 offset: 0,
603 length: 0,
604 msb_right: 0,
605 };
606 }
607 super::PixelFormat::RGB565 => {
608 var_info.red = FbBitfield {
609 offset: 11,
610 length: 5,
611 msb_right: 0,
612 };
613 var_info.green = FbBitfield {
614 offset: 5,
615 length: 6,
616 msb_right: 0,
617 };
618 var_info.blue = FbBitfield {
619 offset: 0,
620 length: 5,
621 msb_right: 0,
622 };
623 var_info.transp = FbBitfield {
624 offset: 0,
625 length: 0,
626 msb_right: 0,
627 };
628 }
629 }
630
631 var_info
632 }
633
634 fn handle_get_vscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
636 if arg == 0 {
637 return Err("Invalid argument pointer");
638 }
639
640 let target_ptr = if let Some(current_task) = crate::task::mytask() {
643 current_task
645 .vm_manager
646 .translate_vaddr(arg)
647 .ok_or("Invalid user pointer - not mapped")?
648 } else {
649 arg
651 };
652
653 let var_info = self.current_var_info();
654
655 unsafe {
657 let user_ptr = target_ptr as *mut FbVarScreenInfo;
658 core::ptr::write(user_ptr, var_info);
659 }
660
661 Ok(0) }
663
664 fn handle_get_fscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
666 if arg == 0 {
667 return Err("Invalid argument pointer");
668 }
669
670 let target_ptr = if let Some(current_task) = crate::task::mytask() {
673 current_task
675 .vm_manager
676 .translate_vaddr(arg)
677 .ok_or("Invalid user pointer - not mapped")?
678 } else {
679 arg
681 };
682
683 let fb_resource = &self.fb_resource;
684 let config = &fb_resource.config;
685
686 let mut fix_info = FbFixScreenInfo::default();
688
689 let fb_name = fb_resource.logical_name.as_bytes();
691 let copy_len = fb_name.len().min(fix_info.id.len() - 1);
692 fix_info.id[..copy_len].copy_from_slice(&fb_name[..copy_len]);
693
694 fix_info.smem_start = fb_resource.physical_addr;
695 fix_info.smem_len = fb_resource.size as u32;
696 fix_info.line_length = config.stride;
697 fix_info.type_ = 0; fix_info.visual = 2; unsafe {
702 let user_ptr = target_ptr as *mut FbFixScreenInfo;
703 core::ptr::write(user_ptr, fix_info);
704 }
705
706 Ok(0) }
708
709 fn handle_flush(&self, _arg: usize) -> Result<i32, &'static str> {
715 let fb_resource = &self.fb_resource;
716
717 if fb_resource.physical_addr == 0 {
719 return Err("Invalid framebuffer address");
720 }
721
722 self.trigger_display_update()?;
730
731 Ok(0) }
733
734 fn trigger_display_update(&self) -> Result<(), &'static str> {
739 let device_manager = DeviceManager::get_manager();
741 if let Some(device) = device_manager.get_device(self.fb_resource.source_device_id) {
742 if let Some(graphics_device) = device.as_graphics_device() {
744 let config = &self.fb_resource.config;
746 graphics_device.flush_framebuffer(0, 0, config.width, config.height)?;
747
748 match graphics_device.get_framebuffer_address() {
750 Ok(addr) => {
751 if addr == 0 {
752 return Err("Graphics device framebuffer address is null");
753 }
754 if addr != self.fb_resource.physical_addr {
755 return Err("Graphics device framebuffer address mismatch");
756 }
757 }
758 Err(e) => return Err(e),
759 }
760 }
761 }
762
763 Ok(())
767 }
768
769 fn handle_put_vscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
771 if arg == 0 {
773 return Ok(-22); }
775
776 let target_ptr = if let Some(current_task) = crate::task::mytask() {
778 match current_task.vm_manager.translate_vaddr(arg) {
779 Some(p) => p,
780 None => return Ok(-14), }
782 } else {
783 arg
784 } as *mut FbVarScreenInfo;
785
786 let _req = unsafe { core::ptr::read(target_ptr as *const FbVarScreenInfo) };
788
789 let accepted = self.current_var_info();
792 unsafe {
793 core::ptr::write(target_ptr, accepted);
794 }
795
796 Ok(0)
797 }
798}
799
800#[cfg(test)]
801mod tests {
802 use super::*;
803 use crate::device::{
804 Device,
805 graphics::{
806 FramebufferConfig, GenericGraphicsDevice, PixelFormat, manager::GraphicsManager,
807 },
808 };
809 use alloc::{string::ToString, sync::Arc};
810 use spin::RwLock;
811
812 fn setup_clean_graphics_manager() -> &'static GraphicsManager {
814 let manager = GraphicsManager::get_manager();
815 manager.clear_for_test();
817 manager
818 }
819
820 #[test_case]
821 fn test_framebuffer_char_device_creation() {
822 let config = FramebufferConfig::new(1024, 768, PixelFormat::RGBA8888);
824 let fb_resource = Arc::new(FramebufferResource::new(
825 0,
826 "fb0".to_string(),
827 config,
828 0x80000000,
829 1024 * 768 * 4,
830 ));
831
832 let device = FramebufferCharDevice::new(fb_resource);
833 assert_eq!(device.get_framebuffer_name(), "fb0");
834 assert_eq!(device.device_type(), DeviceType::Char);
835 assert_eq!(device.name(), "framebuffer");
836 }
837
838 #[test_case]
839 fn test_framebuffer_char_device_read_write_at() {
840 let graphics_manager = setup_clean_graphics_manager();
842 let mut test_device = GenericGraphicsDevice::new("test-gpu-read-write");
843 let config = FramebufferConfig::new(100, 100, PixelFormat::RGBA8888);
844 test_device.set_framebuffer_config(config.clone());
845
846 let fb_size = config.size();
848 let fb_pages = (fb_size + 4095) / 4096;
849 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
850 test_device.set_framebuffer_address(fb_addr);
851
852 let shared_device: Arc<dyn Device> = Arc::new(test_device);
853
854 let device_manager = DeviceManager::get_manager();
856 let device_id = device_manager
857 .register_device_with_name("test-gpu-read-write".to_string(), shared_device.clone());
858
859 graphics_manager
861 .register_framebuffer_from_device(device_id, shared_device)
862 .unwrap();
863
864 let fb_resource = {
866 let fb_names = graphics_manager.get_framebuffer_names();
867 let fb_name = fb_names
868 .iter()
869 .find(|name| {
870 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
871 fb_resource.source_device_id == device_id
872 } else {
873 false
874 }
875 })
876 .expect("Should have framebuffer for this device");
877 graphics_manager
878 .get_framebuffer(fb_name)
879 .expect("Framebuffer should exist")
880 };
881 let char_device = FramebufferCharDevice::new(fb_resource);
882
883 let test_data = [0x12, 0x34, 0x56, 0x78];
885 let written = char_device.write_at(0, &test_data).unwrap();
886 assert_eq!(written, 4);
887
888 let mut read_buffer = [0u8; 4];
890 let read_count = char_device.read_at(0, &mut read_buffer).unwrap();
891 assert_eq!(read_count, 4);
892 assert_eq!(read_buffer, test_data);
893 }
894
895 #[test_case]
896 fn test_framebuffer_char_device_boundaries() {
897 let graphics_manager = setup_clean_graphics_manager();
899 let mut test_device = GenericGraphicsDevice::new("test-gpu-boundaries");
900 let config = FramebufferConfig::new(10, 10, PixelFormat::RGB888); test_device.set_framebuffer_config(config.clone());
902
903 let fb_size = config.size(); let fb_pages = (fb_size + 4095) / 4096;
905 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
906 test_device.set_framebuffer_address(fb_addr);
907
908 let shared_device: Arc<dyn Device> = Arc::new(test_device);
909 let device_manager = DeviceManager::get_manager();
910 let device_id = device_manager
911 .register_device_with_name("test-gpu-boundaries".to_string(), shared_device.clone());
912 graphics_manager
913 .register_framebuffer_from_device(device_id, shared_device)
914 .unwrap();
915
916 let fb_resource = {
917 let fb_names = graphics_manager.get_framebuffer_names();
918 let fb_name = fb_names
919 .iter()
920 .find(|name| {
921 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
922 fb_resource.source_device_id == device_id
923 } else {
924 false
925 }
926 })
927 .expect("Should have framebuffer for this device");
928 graphics_manager
929 .get_framebuffer(fb_name)
930 .expect("Framebuffer should exist")
931 };
932 let char_device = FramebufferCharDevice::new(fb_resource);
933
934 let zero_buffer = vec![0u8; fb_size];
936 assert_eq!(char_device.write_at(0, &zero_buffer).unwrap(), fb_size);
937
938 let start_data = [0xFF, 0x00, 0xFF];
940 assert_eq!(char_device.write_at(0, &start_data).unwrap(), 3);
941
942 let end_data = [0x00, 0xFF, 0x00];
944 assert_eq!(
945 char_device
946 .write_at((fb_size - 6) as u64, &end_data)
947 .unwrap(),
948 3
949 );
950
951 let beyond_data = [0xAA, 0xBB, 0xCC, 0xDD];
953 let result = char_device.write_at(fb_size as u64, &beyond_data);
954 assert!(result.is_err() || result.unwrap() == 0);
955
956 let partial_data = [0x11, 0x22, 0x33, 0x44, 0x55];
958 let written = char_device
959 .write_at((fb_size - 2) as u64, &partial_data)
960 .unwrap();
961 assert_eq!(written, 2); let mut read_start = [0u8; 3];
965 assert_eq!(char_device.read_at(0, &mut read_start).unwrap(), 3);
966 assert_eq!(read_start, start_data);
967
968 let mut read_end = [0u8; 3];
969 assert_eq!(
970 char_device
971 .read_at((fb_size - 6) as u64, &mut read_end)
972 .unwrap(),
973 3
974 );
975 assert_eq!(read_end, end_data);
976
977 let mut read_partial = [0u8; 2];
979 assert_eq!(
980 char_device
981 .read_at((fb_size - 2) as u64, &mut read_partial)
982 .unwrap(),
983 2
984 );
985 assert_eq!(read_partial, [0x11, 0x22]);
986 }
987
988 #[test_case]
989 fn test_framebuffer_char_device_pixel_formats() {
990 for (pixel_format, expected_bpp) in [
991 (PixelFormat::RGB565, 2),
992 (PixelFormat::RGB888, 3),
993 (PixelFormat::RGBA8888, 4),
994 (PixelFormat::BGRA8888, 4),
995 ] {
996 let graphics_manager = setup_clean_graphics_manager();
997 let mut test_device = GenericGraphicsDevice::new("test-gpu-pixel-format");
998 let config = FramebufferConfig::new(4, 4, pixel_format); test_device.set_framebuffer_config(config.clone());
1000
1001 let fb_size = config.size();
1002 let expected_size = 4 * 4 * expected_bpp;
1003 assert_eq!(fb_size, expected_size);
1004
1005 let fb_pages = (fb_size + 4095) / 4096;
1006 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1007 test_device.set_framebuffer_address(fb_addr);
1008
1009 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1010 let device_manager = DeviceManager::get_manager();
1011 let device_id = device_manager.register_device_with_name(
1012 alloc::format!("test-gpu-{:?}", pixel_format),
1013 shared_device.clone(),
1014 );
1015 graphics_manager
1016 .register_framebuffer_from_device(device_id, shared_device)
1017 .unwrap();
1018
1019 let fb_resource = {
1020 let fb_names = graphics_manager.get_framebuffer_names();
1021 let fb_name = fb_names
1022 .iter()
1023 .find(|name| {
1024 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1025 fb_resource.source_device_id == device_id
1026 } else {
1027 false
1028 }
1029 })
1030 .expect("Should have framebuffer for this device");
1031 graphics_manager
1032 .get_framebuffer(fb_name)
1033 .expect("Framebuffer should exist")
1034 };
1035 let char_device = FramebufferCharDevice::new(fb_resource);
1036
1037 let pixel_data = match expected_bpp {
1039 2 => vec![0xFF, 0x00], 3 => vec![0xFF, 0x00, 0xFF], 4 => vec![0xFF, 0x00, 0xFF, 0x80], _ => unreachable!(),
1043 };
1044
1045 assert_eq!(char_device.write_at(0, &pixel_data).unwrap(), expected_bpp);
1046
1047 let mut read_pixel = vec![0u8; expected_bpp];
1049 assert_eq!(
1050 char_device.read_at(0, &mut read_pixel).unwrap(),
1051 expected_bpp
1052 );
1053 assert_eq!(read_pixel, pixel_data);
1054 }
1055 }
1056
1057 #[test_case]
1058 fn test_framebuffer_char_device_capabilities() {
1059 let graphics_manager = setup_clean_graphics_manager();
1061 let mut test_device = GenericGraphicsDevice::new("test-gpu-caps");
1062 let config = FramebufferConfig::new(100, 100, PixelFormat::RGBA8888);
1063 test_device.set_framebuffer_config(config.clone());
1064
1065 let fb_size = config.size();
1066 let fb_pages = (fb_size + 4095) / 4096;
1067 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1068 test_device.set_framebuffer_address(fb_addr);
1069
1070 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1071 let device_manager = DeviceManager::get_manager();
1072 let device_id = device_manager
1073 .register_device_with_name("test-gpu-caps".to_string(), shared_device.clone());
1074 graphics_manager
1075 .register_framebuffer_from_device(device_id, shared_device)
1076 .unwrap();
1077
1078 let fb_resource = {
1079 let fb_names = graphics_manager.get_framebuffer_names();
1080 let fb_name = fb_names
1081 .iter()
1082 .find(|name| {
1083 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1084 fb_resource.source_device_id == device_id
1085 } else {
1086 false
1087 }
1088 })
1089 .expect("Should have framebuffer for this device");
1090 graphics_manager
1091 .get_framebuffer(fb_name)
1092 .expect("Framebuffer should exist")
1093 };
1094 let char_device = FramebufferCharDevice::new(fb_resource);
1095
1096 assert!(char_device.can_read());
1098 assert!(char_device.can_write());
1099 assert_eq!(char_device.device_type(), DeviceType::Char);
1100 assert_eq!(char_device.name(), "framebuffer");
1101
1102 let invalid_config = FramebufferConfig::new(10, 10, PixelFormat::RGB888);
1104 let invalid_resource = Arc::new(FramebufferResource {
1105 source_device_id: 999,
1106 logical_name: "invalid".to_string(),
1107 config: invalid_config,
1108 physical_addr: 0, size: 300,
1110 created_char_device_id: RwLock::new(None),
1111 });
1112 let invalid_device = FramebufferCharDevice::new(invalid_resource);
1113
1114 assert!(!invalid_device.can_read());
1115 assert!(!invalid_device.can_write());
1116 }
1117
1118 #[test_case]
1119 fn test_framebuffer_char_device_unsupported_methods() {
1120 let graphics_manager = setup_clean_graphics_manager();
1121 let mut test_device = GenericGraphicsDevice::new("test-gpu-unsupported");
1122 let config = FramebufferConfig::new(10, 10, PixelFormat::RGB888);
1123 test_device.set_framebuffer_config(config.clone());
1124
1125 let fb_size = config.size();
1126 let fb_pages = (fb_size + 4095) / 4096;
1127 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1128 test_device.set_framebuffer_address(fb_addr);
1129
1130 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1131 let device_manager = DeviceManager::get_manager();
1132 let device_id = device_manager
1133 .register_device_with_name("test-gpu-unsupported".to_string(), shared_device.clone());
1134 graphics_manager
1135 .register_framebuffer_from_device(device_id, shared_device)
1136 .unwrap();
1137
1138 let fb_resource = {
1139 let fb_names = graphics_manager.get_framebuffer_names();
1140 let fb_name = fb_names
1141 .iter()
1142 .find(|name| {
1143 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1144 fb_resource.source_device_id == device_id
1145 } else {
1146 false
1147 }
1148 })
1149 .expect("Should have framebuffer for this device");
1150 graphics_manager
1151 .get_framebuffer(fb_name)
1152 .expect("Framebuffer should exist")
1153 };
1154 let char_device = FramebufferCharDevice::new(fb_resource);
1155
1156 assert_eq!(char_device.read_byte(), None);
1158
1159 let result = char_device.write_byte(0xFF);
1161 assert!(result.is_err());
1162 assert!(result.unwrap_err().contains("not supported"));
1163 }
1164
1165 #[test_case]
1166 fn test_framebuffer_char_device_large_operations() {
1167 let graphics_manager = setup_clean_graphics_manager();
1168 let mut test_device = GenericGraphicsDevice::new("test-gpu-large");
1169 let config = FramebufferConfig::new(256, 256, PixelFormat::RGBA8888);
1170 test_device.set_framebuffer_config(config.clone());
1171
1172 let fb_size = config.size(); let fb_pages = (fb_size + 4095) / 4096;
1174 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1175 test_device.set_framebuffer_address(fb_addr);
1176
1177 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1178 let device_manager = DeviceManager::get_manager();
1179 let device_id = device_manager
1180 .register_device_with_name("test-gpu-large".to_string(), shared_device.clone());
1181 graphics_manager
1182 .register_framebuffer_from_device(device_id, shared_device)
1183 .unwrap();
1184
1185 let fb_resource = {
1186 let fb_names = graphics_manager.get_framebuffer_names();
1187 let fb_name = fb_names
1188 .iter()
1189 .find(|name| {
1190 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1191 fb_resource.source_device_id == device_id
1192 } else {
1193 false
1194 }
1195 })
1196 .expect("Should have framebuffer for this device");
1197 graphics_manager
1198 .get_framebuffer(fb_name)
1199 .expect("Framebuffer should exist")
1200 };
1201 let char_device = FramebufferCharDevice::new(fb_resource);
1202
1203 let large_data = vec![0x55u8; 4096]; let written = char_device.write_at(0, &large_data).unwrap();
1206 assert_eq!(written, 4096);
1207
1208 let mut read_buffer = vec![0u8; 4096];
1210 let read_count = char_device.read_at(0, &mut read_buffer).unwrap();
1211 assert_eq!(read_count, 4096);
1212 assert_eq!(read_buffer, large_data);
1213
1214 let cross_page_data = vec![0xAAu8; 8192]; let written = char_device.write_at(2048, &cross_page_data).unwrap();
1217 assert_eq!(written, 8192);
1218
1219 let mut cross_read_buffer = vec![0u8; 8192];
1221 let read_count = char_device.read_at(2048, &mut cross_read_buffer).unwrap();
1222 assert_eq!(read_count, 8192);
1223 assert_eq!(cross_read_buffer, cross_page_data);
1224 }
1225
1226 #[test_case]
1227 fn test_framebuffer_char_device_pattern_operations() {
1228 let graphics_manager = setup_clean_graphics_manager();
1229 let mut test_device = GenericGraphicsDevice::new("test-gpu-pattern");
1230 let config = FramebufferConfig::new(16, 16, PixelFormat::RGB888);
1231 test_device.set_framebuffer_config(config.clone());
1232
1233 let fb_size = config.size(); let fb_pages = (fb_size + 4095) / 4096;
1235 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1236 test_device.set_framebuffer_address(fb_addr);
1237
1238 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1239 let device_manager = DeviceManager::get_manager();
1240 let device_id = device_manager
1241 .register_device_with_name("test-gpu-pattern".to_string(), shared_device.clone());
1242 graphics_manager
1243 .register_framebuffer_from_device(device_id, shared_device)
1244 .unwrap();
1245
1246 let fb_resource = {
1247 let fb_names = graphics_manager.get_framebuffer_names();
1248 let fb_name = fb_names
1249 .iter()
1250 .find(|name| {
1251 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1252 fb_resource.source_device_id == device_id
1253 } else {
1254 false
1255 }
1256 })
1257 .expect("Should have framebuffer for this device");
1258 graphics_manager
1259 .get_framebuffer(fb_name)
1260 .expect("Framebuffer should exist")
1261 };
1262 let char_device = FramebufferCharDevice::new(fb_resource);
1263
1264 for y in 0..16 {
1266 for x in 0..16 {
1267 let pixel_offset = (y * 16 + x) * 3;
1268 let color = if (x + y) % 2 == 0 {
1269 [0xFF, 0x00, 0x00] } else {
1271 [0x00, 0xFF, 0x00] };
1273 assert_eq!(
1274 char_device.write_at(pixel_offset as u64, &color).unwrap(),
1275 3
1276 );
1277 }
1278 }
1279
1280 for y in 0..16 {
1282 for x in 0..16 {
1283 let pixel_offset = (y * 16 + x) * 3;
1284 let mut read_color = [0u8; 3];
1285 assert_eq!(
1286 char_device
1287 .read_at(pixel_offset as u64, &mut read_color)
1288 .unwrap(),
1289 3
1290 );
1291
1292 let expected_color = if (x + y) % 2 == 0 {
1293 [0xFF, 0x00, 0x00] } else {
1295 [0x00, 0xFF, 0x00] };
1297 assert_eq!(read_color, expected_color);
1298 }
1299 }
1300 }
1301
1302 #[test_case]
1303 fn test_framebuffer_memory_mapping_ops() {
1304 use crate::object::capability::MemoryMappingOps;
1305
1306 let graphics_manager = setup_clean_graphics_manager();
1307 let mut test_device = GenericGraphicsDevice::new("test-mmap-ops");
1308 let config = FramebufferConfig::new(4, 4, PixelFormat::RGBA8888);
1309 test_device.set_framebuffer_config(config.clone());
1310
1311 let fb_size = config.size();
1312 let fb_pages = (fb_size + 4095) / 4096;
1313 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1314 test_device.set_framebuffer_address(fb_addr);
1315
1316 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1317 let device_manager = DeviceManager::get_manager();
1318 let device_id = device_manager
1319 .register_device_with_name("test-mmap-ops-device".to_string(), shared_device.clone());
1320 graphics_manager
1321 .register_framebuffer_from_device(device_id, shared_device)
1322 .unwrap();
1323
1324 let fb_resource = {
1325 let fb_names = graphics_manager.get_framebuffer_names();
1326 let fb_name = fb_names
1327 .iter()
1328 .find(|name| {
1329 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1330 fb_resource.source_device_id == device_id
1331 } else {
1332 false
1333 }
1334 })
1335 .expect("Should have framebuffer for this device");
1336 graphics_manager
1337 .get_framebuffer(fb_name)
1338 .expect("Framebuffer should exist")
1339 };
1340
1341 let fb_device = FramebufferCharDevice::new(fb_resource.clone());
1342
1343 assert!(fb_device.supports_mmap());
1345
1346 let result = fb_device.get_mapping_info(0, fb_pages * 4096);
1348 assert!(result.is_ok());
1349 let (paddr, permissions, is_shared) = result.unwrap();
1350 assert_eq!(paddr, fb_addr);
1351 assert_eq!(permissions, 0x3); assert!(is_shared);
1353
1354 let result = fb_device.get_mapping_info(fb_size + 1, 32);
1356 assert!(result.is_err());
1357
1358 let result = fb_device.get_mapping_info(0, fb_size + 1);
1360 assert!(result.is_err());
1361
1362 fb_device.on_mapped(0x1000, paddr, fb_pages * 4096, 0);
1364 {
1365 let mappings = fb_device.mappings.read();
1366 assert_eq!(mappings.len(), 1);
1367 assert!(mappings.contains_key(&0x1000));
1368 }
1369
1370 fb_device.on_unmapped(0x1000, fb_pages * 4096);
1372 {
1373 let mappings = fb_device.mappings.read();
1374 assert_eq!(mappings.len(), 0);
1375 }
1376 }
1377
1378 #[test_case]
1379 fn test_framebuffer_mapping_basic_ops() {
1380 let graphics_manager = setup_clean_graphics_manager();
1381 let mut test_device = GenericGraphicsDevice::new("test-mapping-basic");
1382 let config = FramebufferConfig::new(4, 4, PixelFormat::RGBA8888);
1383 test_device.set_framebuffer_config(config.clone());
1384
1385 let fb_size = config.size();
1386 let fb_pages = (fb_size + 4095) / 4096;
1387 let fb_addr = crate::mem::page::allocate_raw_pages(fb_pages) as usize;
1388 test_device.set_framebuffer_address(fb_addr);
1389
1390 let shared_device: Arc<dyn Device> = Arc::new(test_device);
1391 let device_manager = DeviceManager::get_manager();
1392 let device_id = device_manager.register_device_with_name(
1393 "test-mapping-basic-device".to_string(),
1394 shared_device.clone(),
1395 );
1396 graphics_manager
1397 .register_framebuffer_from_device(device_id, shared_device)
1398 .unwrap();
1399
1400 let fb_resource = {
1401 let fb_names = graphics_manager.get_framebuffer_names();
1402 let fb_name = fb_names
1403 .iter()
1404 .find(|name| {
1405 if let Some(fb_resource) = graphics_manager.get_framebuffer(name) {
1406 fb_resource.source_device_id == device_id
1407 } else {
1408 false
1409 }
1410 })
1411 .expect("Should have framebuffer for this device");
1412 graphics_manager
1413 .get_framebuffer(fb_name)
1414 .expect("Framebuffer should exist")
1415 };
1416
1417 let char_device = FramebufferCharDevice::new(fb_resource);
1418
1419 {
1421 let mappings = char_device.mappings.read();
1422 assert_eq!(mappings.len(), 0);
1423 }
1424
1425 assert!(char_device.supports_mmap());
1427
1428 let result = char_device.get_mapping_info(0, fb_pages * 4096);
1430 assert!(result.is_ok());
1431 let (paddr, permissions, is_shared) = result.unwrap();
1432 assert_eq!(paddr, fb_addr);
1433 assert_eq!(permissions, 0x3);
1434 assert!(is_shared);
1435 }
1436}