kernel/device/graphics/
framebuffer_device.rs

1//! # Framebuffer Character Device Module
2//!
3//! This module provides character device interface for framebuffer access.
4//! It integrates with the GraphicsManager to provide user-space access to
5//! framebuffer resources through the standard character device interface.
6//!
7//! ## Overview
8//!
9//! The FramebufferCharDevice provides:
10//! - Basic read/write operations to framebuffer memory
11//! - Control operations (ioctl-equivalent) for device configuration
12//! - Integration with GraphicsManager for resource management
13//! - Standard character device interface for user programs
14//! - Support for Linux-compatible framebuffer ioctls
15
16extern 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
29/// Linux framebuffer ioctl command constants
30/// These provide compatibility with Linux framebuffer applications
31pub mod framebuffer_commands {
32    /// Get variable screen information
33    pub const FBIOGET_VSCREENINFO: u32 = 0x4600;
34    /// Set variable screen information  
35    pub const FBIOPUT_VSCREENINFO: u32 = 0x4601;
36    /// Get fixed screen information
37    pub const FBIOGET_FSCREENINFO: u32 = 0x4602;
38    /// Flush framebuffer to display
39    pub const FBIO_FLUSH: u32 = 0x4620;
40}
41
42/// Variable screen information structure (Linux fb_var_screeninfo compatible)
43#[repr(C)]
44#[derive(Debug, Clone, Copy)]
45pub struct FbVarScreenInfo {
46    /// Visible resolution width
47    pub xres: u32,
48    /// Visible resolution height  
49    pub yres: u32,
50    /// Virtual resolution width
51    pub xres_virtual: u32,
52    /// Virtual resolution height
53    pub yres_virtual: u32,
54    /// Offset from virtual to visible resolution
55    pub xoffset: u32,
56    /// Offset from virtual to visible resolution
57    pub yoffset: u32,
58    /// Bits per pixel
59    pub bits_per_pixel: u32,
60    /// Grayscale != 0 means graylevels instead of colors
61    pub grayscale: u32,
62    /// Red bitfield
63    pub red: FbBitfield,
64    /// Green bitfield
65    pub green: FbBitfield,
66    /// Blue bitfield
67    pub blue: FbBitfield,
68    /// Transparency bitfield
69    pub transp: FbBitfield,
70    /// Non-zero if not grayscale
71    pub nonstd: u32,
72    /// Activate settings
73    pub activate: u32,
74    /// Screen height in mm
75    pub height: u32,
76    /// Screen width in mm
77    pub width: u32,
78    /// Acceleration flags
79    pub accel_flags: u32,
80    /// Pixel clock in picoseconds
81    pub pixclock: u32,
82    /// Time from sync to picture
83    pub left_margin: u32,
84    /// Time from picture to sync
85    pub right_margin: u32,
86    /// Time from sync to picture
87    pub upper_margin: u32,
88    /// Time from picture to sync
89    pub lower_margin: u32,
90    /// Length of horizontal sync
91    pub hsync_len: u32,
92    /// Length of vertical sync
93    pub vsync_len: u32,
94    /// Sync flags
95    pub sync: u32,
96    /// Video mode flags
97    pub vmode: u32,
98    /// Rotation angle (0=normal, 1=90°, 2=180°, 3=270°)
99    pub rotate: u32,
100    /// Color space for frame buffer
101    pub colorspace: u32,
102    /// Reserved for future use
103    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/// Fixed screen information structure (Linux fb_fix_screeninfo compatible)
143#[repr(C)]
144#[derive(Debug, Clone)]
145pub struct FbFixScreenInfo {
146    /// Identification string
147    pub id: [u8; 16],
148    /// Start of frame buffer memory (physical address)
149    pub smem_start: usize,
150    /// Length of frame buffer memory
151    pub smem_len: u32,
152    /// Framebuffer type
153    pub type_: u32,
154    /// Type of auxiliary display
155    pub type_aux: u32,
156    /// Visual type
157    pub visual: u32,
158    /// Zero if no hardware panning
159    pub xpanstep: u16,
160    /// Zero if no hardware panning
161    pub ypanstep: u16,
162    /// Zero if no hardware ywrap
163    pub ywrapstep: u16,
164    /// Length of a line in bytes
165    pub line_length: u32,
166    /// Start of memory mapped I/O
167    pub mmio_start: usize,
168    /// Length of memory mapped I/O
169    pub mmio_len: u32,
170    /// Acceleration type
171    pub accel: u32,
172    /// Capabilities
173    pub capabilities: u16,
174    /// Reserved for future compatibility
175    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/// Bitfield information for color components
201#[repr(C)]
202#[derive(Debug, Clone, Copy)]
203pub struct FbBitfield {
204    /// Beginning of bitfield (LSB is 0)
205    pub offset: u32,
206    /// Length of bitfield
207    pub length: u32,
208    /// MSB position (0 = MSB is rightmost)
209    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/// Mock mapping for testing purposes
223#[allow(dead_code)]
224#[derive(Debug, Clone)]
225struct MockMapping {
226    vaddr: usize,
227    length: usize,
228}
229
230/// Framebuffer character device implementation
231///
232/// This device provides character-based access to framebuffer memory.
233/// It acts as a bridge between user-space programs and the graphics
234/// hardware through the GraphicsManager.
235///
236/// Note: This device is stateless and does not maintain position information.
237/// Position management is handled by the FileObject layer for proper POSIX semantics.
238pub struct FramebufferCharDevice {
239    /// The framebuffer resource this device represents
240    fb_resource: Arc<FramebufferResource>,
241    /// Track mappings for testing purposes  
242    mappings: RwLock<BTreeMap<usize, MockMapping>>, // virtual_start -> MockMapping
243}
244
245impl FramebufferCharDevice {
246    /// Create a new framebuffer character device
247    ///
248    /// # Arguments
249    ///
250    /// * `fb_resource` - The framebuffer resource to access
251    ///
252    /// # Returns
253    ///
254    /// A new FramebufferCharDevice instance
255    pub fn new(fb_resource: Arc<FramebufferResource>) -> Self {
256        Self {
257            fb_resource,
258            mappings: RwLock::new(BTreeMap::new()),
259        }
260    }
261
262    /// Get the framebuffer name this device represents
263    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    /// Read a single byte from the framebuffer
292    ///
293    /// This method is not supported in the new position-per-file-handle architecture.
294    /// Use read_at() through a DevFileObject instead for proper position management.
295    ///
296    /// # Returns
297    ///
298    /// Always returns None to indicate unsupported operation
299    fn read_byte(&self) -> Option<u8> {
300        // This method is intentionally unsupported in the new architecture
301        // Position management should be done by DevFileObject, not the device
302        None
303    }
304
305    /// Write a single byte to the framebuffer
306    ///
307    /// This method is not supported in the new position-per-file-handle architecture.
308    /// Use write_at() through a DevFileObject instead for proper position management.
309    ///
310    /// # Arguments
311    ///
312    /// * `_byte` - The byte to write (ignored)
313    ///
314    /// # Returns
315    ///
316    /// Always returns an error to indicate unsupported operation
317    fn write_byte(&self, _byte: u8) -> Result<(), &'static str> {
318        // This method is intentionally unsupported in the new architecture
319        // Position management should be done by DevFileObject, not the device
320        Err("write_byte is not supported - use write_at through DevFileObject instead")
321    }
322
323    /// Check if the device is ready for reading
324    ///
325    /// # Returns
326    ///
327    /// True if framebuffer is valid
328    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    /// Check if the device is ready for writing
334    ///
335    /// # Returns
336    ///
337    /// True if framebuffer is valid
338    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    /// Read data from a specific position in the framebuffer
344    ///
345    /// # Arguments
346    ///
347    /// * `position` - Byte offset to read from
348    /// * `buffer` - Buffer to read data into
349    ///
350    /// # Returns
351    ///
352    /// Result containing the number of bytes read or an error
353    fn read_at(&self, position: u64, buffer: &mut [u8]) -> Result<usize, &'static str> {
354        let fb_resource = &self.fb_resource;
355
356        // Check if framebuffer address is valid
357        if fb_resource.physical_addr == 0 {
358            return Err("Invalid framebuffer address");
359        }
360
361        // Use logical framebuffer size for boundary checks
362        let logical_size = fb_resource.config.size();
363        let start_pos = position as usize;
364        if start_pos >= logical_size {
365            return Ok(0); // EOF
366        }
367
368        let available = logical_size - start_pos;
369        let to_read = buffer.len().min(available);
370
371        // Read data from framebuffer memory.
372        //
373        // NOTE: On QEMU+HVF, some non-volatile memcpy-style accesses to MMIO-like
374        // framebuffer regions can VM-exit as EC_DATAABORT without ISV set, which
375        // causes QEMU's HVF backend to abort (assert(isv)). Use byte-wise volatile
376        // accesses to keep the trapped instruction decodable.
377        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    /// Write data to a specific position in the framebuffer
390    ///
391    /// # Arguments
392    ///
393    /// * `position` - Byte offset to write to
394    /// * `buffer` - Buffer containing data to write
395    ///
396    /// # Returns
397    ///
398    /// Result containing the number of bytes written or an error
399    fn write_at(&self, position: u64, buffer: &[u8]) -> Result<usize, &'static str> {
400        let fb_resource = &self.fb_resource;
401
402        // Check if framebuffer address is valid
403        if fb_resource.physical_addr == 0 {
404            return Err("Invalid framebuffer address");
405        }
406
407        // Use logical framebuffer size for boundary checks
408        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        // Write data to framebuffer memory.
418        // See note in read_at() about QEMU+HVF and ISV.
419        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        // VMM requires page-aligned physical mappings.
465        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        // Check if framebuffer supports memory mapping
473        if fb_resource.physical_addr == 0 || fb_resource.size == 0 {
474            return Err("Invalid framebuffer configuration");
475        }
476
477        // Basic validation
478        // fb_resource.size is the page-aligned physical size, safe for mmap
479        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        // FramebufferResource stores a kernel virtual address for CPU access.
489        // Convert it to a physical address for user mmap.
490        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; // Read and Write
495        let is_shared = true; // Framebuffer mappings are shared
496
497        Ok((paddr, permissions, is_shared))
498    }
499
500    fn on_mapped(&self, vaddr: usize, _paddr: usize, length: usize, _offset: usize) {
501        // Record this mapping in our tracking structure
502        let mapping = MockMapping { vaddr, length };
503        self.mappings.write().insert(vaddr, mapping);
504    }
505
506    fn on_unmapped(&self, vaddr: usize, _length: usize) {
507        // Remove the mapping from our tracking
508        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    /// Build a FbVarScreenInfo reflecting the current framebuffer configuration
529    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    /// Handle FBIOGET_VSCREENINFO control command
635    fn handle_get_vscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
636        if arg == 0 {
637            return Err("Invalid argument pointer");
638        }
639
640        // Try to get current task for user pointer translation
641        // If no task (kernel context), use pointer directly
642        let target_ptr = if let Some(current_task) = crate::task::mytask() {
643            // User space: translate virtual address to physical
644            current_task
645                .vm_manager
646                .translate_vaddr(arg)
647                .ok_or("Invalid user pointer - not mapped")?
648        } else {
649            // Kernel space: use pointer directly
650            arg
651        };
652
653        let var_info = self.current_var_info();
654
655        // Safely copy to user space using translated physical address
656        unsafe {
657            let user_ptr = target_ptr as *mut FbVarScreenInfo;
658            core::ptr::write(user_ptr, var_info);
659        }
660
661        Ok(0) // Success
662    }
663
664    /// Handle FBIOGET_FSCREENINFO control command
665    fn handle_get_fscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
666        if arg == 0 {
667            return Err("Invalid argument pointer");
668        }
669
670        // Try to get current task for user pointer translation
671        // If no task (kernel context), use pointer directly
672        let target_ptr = if let Some(current_task) = crate::task::mytask() {
673            // User space: translate virtual address to physical
674            current_task
675                .vm_manager
676                .translate_vaddr(arg)
677                .ok_or("Invalid user pointer - not mapped")?
678        } else {
679            // Kernel space: use pointer directly
680            arg
681        };
682
683        let fb_resource = &self.fb_resource;
684        let config = &fb_resource.config;
685
686        // Create fixed screen info structure
687        let mut fix_info = FbFixScreenInfo::default();
688
689        // Set identification string
690        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; // FB_TYPE_PACKED_PIXELS
698        fix_info.visual = 2; // FB_VISUAL_TRUECOLOR
699
700        // Safely copy to user space using translated physical address
701        unsafe {
702            let user_ptr = target_ptr as *mut FbFixScreenInfo;
703            core::ptr::write(user_ptr, fix_info);
704        }
705
706        Ok(0) // Success
707    }
708
709    /// Handle FBIO_FLUSH control command
710    ///
711    /// This command forces any pending framebuffer changes to be displayed.
712    /// For memory-mapped framebuffers, this typically involves ensuring
713    /// CPU caches are flushed and any display controller updates are triggered.
714    fn handle_flush(&self, _arg: usize) -> Result<i32, &'static str> {
715        let fb_resource = &self.fb_resource;
716
717        // Check if framebuffer address is valid
718        if fb_resource.physical_addr == 0 {
719            return Err("Invalid framebuffer address");
720        }
721
722        // Flush the CPU cache for the framebuffer memory
723        // In a real implementation, this would ensure that any writes to the framebuffer
724        // are visible to the display controller.
725        // TODO: Implement actual cache flushing logic
726
727        // Trigger display controller update if needed
728        // For some hardware, writing to framebuffer memory doesn't immediately update the display
729        self.trigger_display_update()?;
730
731        Ok(0) // Success
732    }
733
734    /// Trigger display controller update
735    ///
736    /// Some display controllers require explicit commands to update the display
737    /// from framebuffer contents. This method handles such updates.
738    fn trigger_display_update(&self) -> Result<(), &'static str> {
739        // Try to get the source graphics device to trigger a display update
740        let device_manager = DeviceManager::get_manager();
741        if let Some(device) = device_manager.get_device(self.fb_resource.source_device_id) {
742            // Check if the device supports graphics operations
743            if let Some(graphics_device) = device.as_graphics_device() {
744                // Trigger a full framebuffer flush to ensure display is updated
745                let config = &self.fb_resource.config;
746                graphics_device.flush_framebuffer(0, 0, config.width, config.height)?;
747
748                // Verify that the framebuffer address is still valid
749                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        // For virtualized environments (like QEMU), framebuffer writes are often
764        // automatically reflected on the display, so no additional action is needed
765
766        Ok(())
767    }
768
769    /// Handle FBIOPUT_VSCREENINFO control command  
770    fn handle_put_vscreeninfo(&self, arg: usize) -> Result<i32, &'static str> {
771        // Basic validation of user pointer
772        if arg == 0 {
773            return Ok(-22); // EINVAL
774        }
775
776        // Translate user-space pointer if available
777        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), // EFAULT
781            }
782        } else {
783            arg
784        } as *mut FbVarScreenInfo;
785
786        // Read the requested settings (not strictly required, but useful for future validation)
787        let _req = unsafe { core::ptr::read(target_ptr as *const FbVarScreenInfo) };
788
789        // We currently do not support mode switching. Acknowledge the request
790        // and write back the actually supported mode so userland can proceed.
791        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    /// Test utility to setup a clean global GraphicsManager for each test
813    fn setup_clean_graphics_manager() -> &'static GraphicsManager {
814        let manager = GraphicsManager::get_manager();
815        // Clear any existing state from previous tests
816        manager.clear_for_test();
817        manager
818    }
819
820    #[test_case]
821    fn test_framebuffer_char_device_creation() {
822        // Create test framebuffer resource
823        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        // Setup clean graphics manager for this test
841        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        // Allocate memory for test framebuffer
847        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        // Register device with DeviceManager first
855        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        // Then register with GraphicsManager
860        graphics_manager
861            .register_framebuffer_from_device(device_id, shared_device)
862            .unwrap();
863
864        // Get the framebuffer resource that was assigned to this specific device
865        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        // Test write_at operation
884        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        // Test read_at operation
889        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        // Setup clean graphics manager for this test
898        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); // Small 10x10 framebuffer
901        test_device.set_framebuffer_config(config.clone());
902
903        let fb_size = config.size(); // 10 * 10 * 3 = 300 bytes
904        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        // First, clear the framebuffer by writing zeros
935        let zero_buffer = vec![0u8; fb_size];
936        assert_eq!(char_device.write_at(0, &zero_buffer).unwrap(), fb_size);
937
938        // Test writing at the start
939        let start_data = [0xFF, 0x00, 0xFF];
940        assert_eq!(char_device.write_at(0, &start_data).unwrap(), 3);
941
942        // Test writing at the end (non-overlapping with partial write test)
943        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        // Test writing beyond boundaries (should fail or write partial)
952        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        // Test partial write at boundary (this will overwrite the last 2 bytes)
957        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); // Should only write 2 bytes that fit
962
963        // Verify reads
964        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        // Verify the partial write at the very end
978        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); // 4x4 pixels
999            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            // Test writing a single pixel
1038            let pixel_data = match expected_bpp {
1039                2 => vec![0xFF, 0x00],             // RGB565
1040                3 => vec![0xFF, 0x00, 0xFF],       // RGB888
1041                4 => vec![0xFF, 0x00, 0xFF, 0x80], // RGBA8888/BGRA8888
1042                _ => unreachable!(),
1043            };
1044
1045            assert_eq!(char_device.write_at(0, &pixel_data).unwrap(), expected_bpp);
1046
1047            // Test reading the pixel back
1048            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        // Test with valid framebuffer
1060        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        // Test capabilities
1097        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        // Test with invalid framebuffer (zero address)
1103        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, // Invalid address
1109            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        // Test that read_byte returns None (unsupported)
1157        assert_eq!(char_device.read_byte(), None);
1158
1159        // Test that write_byte returns error (unsupported)
1160        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(); // 256 * 256 * 4 = 262,144 bytes
1173        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        // Test large write operation
1204        let large_data = vec![0x55u8; 4096]; // 4KB
1205        let written = char_device.write_at(0, &large_data).unwrap();
1206        assert_eq!(written, 4096);
1207
1208        // Test large read operation
1209        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        // Test writing across page boundaries
1215        let cross_page_data = vec![0xAAu8; 8192]; // 8KB
1216        let written = char_device.write_at(2048, &cross_page_data).unwrap();
1217        assert_eq!(written, 8192);
1218
1219        // Test reading across page boundaries
1220        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(); // 16 * 16 * 3 = 768 bytes
1234        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        // Test checkerboard pattern
1265        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] // Red
1270                } else {
1271                    [0x00, 0xFF, 0x00] // Green
1272                };
1273                assert_eq!(
1274                    char_device.write_at(pixel_offset as u64, &color).unwrap(),
1275                    3
1276                );
1277            }
1278        }
1279
1280        // Verify checkerboard pattern
1281        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] // Red
1294                } else {
1295                    [0x00, 0xFF, 0x00] // Green
1296                };
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        // Test supports_mmap
1344        assert!(fb_device.supports_mmap());
1345
1346        // Test get_mapping_info
1347        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); // Read and Write
1352        assert!(is_shared);
1353
1354        // Test invalid offset
1355        let result = fb_device.get_mapping_info(fb_size + 1, 32);
1356        assert!(result.is_err());
1357
1358        // Test invalid length
1359        let result = fb_device.get_mapping_info(0, fb_size + 1);
1360        assert!(result.is_err());
1361
1362        // Test on_mapped callback
1363        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        // Test on_unmapped callback
1371        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        // Test that mappings are initially empty
1420        {
1421            let mappings = char_device.mappings.read();
1422            assert_eq!(mappings.len(), 0);
1423        }
1424
1425        // Test supports_mmap
1426        assert!(char_device.supports_mmap());
1427
1428        // Test get_mapping_info with valid parameters
1429        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}