starnix_modules_framebuffer/
lib.rs1#![recursion_limit = "512"]
6
7mod server;
8
9use crate::server::{FramebufferServer, init_viewport_scene, start_presentation_loop};
10use fidl_fuchsia_io as fio;
11use fidl_fuchsia_math as fmath;
12use fidl_fuchsia_ui_composition as fuicomposition;
13use fidl_fuchsia_ui_display_singleton as fuidisplay;
14use fidl_fuchsia_ui_views as fuiviews;
15use fuchsia_component::client::connect_to_protocol_sync;
16use starnix_core::device::kobject::DeviceMetadata;
17use starnix_core::device::{DeviceMode, DeviceOps};
18use starnix_core::mm::MemoryAccessorExt;
19use starnix_core::mm::memory::MemoryObject;
20use starnix_core::task::{CurrentTask, Kernel};
21use starnix_core::vfs::{
22 CloseFreeSafe, FileObject, FileOps, NamespaceNode, fileops_impl_memory, fileops_impl_noop_sync,
23};
24use starnix_logging::{log_info, log_warn, track_stub};
25use starnix_sync::{FileOpsCore, LockEqualOrBefore, Locked, Mutex, RwLock, Unlocked};
26use starnix_syscalls::{SUCCESS, SyscallArg, SyscallResult};
27use starnix_uapi::device_id::DeviceId;
28use starnix_uapi::errors::Errno;
29use starnix_uapi::open_flags::OpenFlags;
30use starnix_uapi::user_address::{MultiArchUserRef, UserAddress};
31use starnix_uapi::{
32 FB_BLANK_POWERDOWN, FB_BLANK_UNBLANK, FB_TYPE_PACKED_PIXELS, FB_VISUAL_TRUECOLOR, FBIOBLANK,
33 FBIOGET_FSCREENINFO, FBIOGET_VSCREENINFO, FBIOPUT_VSCREENINFO, errno, error, fb_bitfield,
34 fb_fix_screeninfo, fb_var_screeninfo, uapi,
35};
36use std::sync::Arc;
37use zerocopy::IntoBytes;
38
39fn get_display_size() -> Result<fmath::SizeU, Errno> {
40 let singleton_display_info =
41 connect_to_protocol_sync::<fuidisplay::InfoMarker>().map_err(|_| errno!(ENOENT))?;
42 let metrics = singleton_display_info
43 .get_metrics(zx::MonotonicInstant::INFINITE)
44 .map_err(|_| errno!(EINVAL))?;
45 let extent_in_px =
46 metrics.extent_in_px.ok_or("Failed to get extent_in_px").map_err(|_| errno!(EINVAL))?;
47 Ok(extent_in_px)
48}
49
50#[derive(Clone, Copy, Debug, Default)]
51pub struct AspectRatio {
52 pub width: u32,
53 pub height: u32,
54}
55
56pub struct Framebuffer {
57 server: Option<Arc<FramebufferServer>>,
58 memory: Mutex<Option<Arc<MemoryObject>>>,
59 pub info: RwLock<fb_var_screeninfo>,
60 pub view_identity: Mutex<Option<fuiviews::ViewIdentityOnCreation>>,
61 pub view_bound_protocols: Mutex<Option<fuicomposition::ViewBoundProtocols>>,
62 pub initial_view_id_annotation: String,
63}
64
65impl Framebuffer {
66 pub fn get(kernel: &Kernel) -> Result<Arc<Self>, Errno> {
68 kernel.expando.get_or_try_init(|| error!(EINVAL))
69 }
70
71 pub fn device_init<L>(
73 locked: &mut Locked<L>,
74 system_task: &CurrentTask,
75 aspect_ratio: Option<AspectRatio>,
76 enable_visual_debugging: bool,
77 initial_view_id_annotation: String,
78 ) -> Result<Arc<Framebuffer>, Errno>
79 where
80 L: LockEqualOrBefore<FileOpsCore>,
81 {
82 let kernel = system_task.kernel();
83 let registry = &kernel.device_registry;
84
85 let framebuffer = kernel.expando.get_or_try_init(|| {
86 Framebuffer::new(aspect_ratio, enable_visual_debugging, initial_view_id_annotation)
87 })?;
88
89 let graphics_class = registry.objects.graphics_class();
90 registry.register_device(
91 locked,
92 system_task,
93 "fb0".into(),
94 DeviceMetadata::new("fb0".into(), DeviceId::FB0, DeviceMode::Char),
95 graphics_class,
96 FramebufferDevice { framebuffer: framebuffer.clone() },
97 )?;
98
99 Ok(framebuffer)
100 }
101
102 fn new(
106 aspect_ratio: Option<AspectRatio>,
107 enable_visual_debugging: bool,
108 initial_view_id_annotation: String,
109 ) -> Result<Self, Errno> {
110 let mut info = fb_var_screeninfo::default();
111
112 let display_size = get_display_size().unwrap_or(fmath::SizeU { width: 700, height: 1200 });
113
114 let (feature_width, feature_height) = aspect_ratio
117 .map(|ar| (ar.width, ar.height))
118 .unwrap_or((display_size.width, display_size.height));
119
120 let ratio =
122 std::cmp::min(display_size.width / feature_width, display_size.height / feature_height);
123 let (width, height) = (feature_width * ratio, feature_height * ratio);
124
125 info.xres = width;
126 info.yres = height;
127 info.xres_virtual = info.xres;
128 info.yres_virtual = info.yres;
129 info.bits_per_pixel = 32;
130 info.red = fb_bitfield { offset: 0, length: 8, msb_right: 0 };
131 info.green = fb_bitfield { offset: 8, length: 8, msb_right: 0 };
132 info.blue = fb_bitfield { offset: 16, length: 8, msb_right: 0 };
133 info.transp = fb_bitfield { offset: 24, length: 8, msb_right: 0 };
134
135 if let Ok((server, memory)) = FramebufferServer::new(width, height) {
136 let server = Arc::new(server);
137 let memory_len = memory.info()?.size_bytes as u32;
138
139 let background = if enable_visual_debugging {
142 [0xff, 0x00, 0xff, 0xff].repeat((memory_len / 4) as usize)
143 } else {
144 vec![0x00; memory_len as usize]
145 };
146
147 if let Err(err) = memory.write(&background, 0) {
148 log_warn!("could not write initial framebuffer: {:?}", err);
149 }
150
151 Ok(Self {
152 server: Some(server),
153 memory: Mutex::new(Some(memory)),
154 info: RwLock::new(info),
155 view_identity: Default::default(),
156 view_bound_protocols: Default::default(),
157 initial_view_id_annotation,
158 })
159 } else {
160 Ok(Self {
161 server: None,
162 memory: Default::default(),
163 info: RwLock::new(info),
164 view_identity: Default::default(),
165 view_bound_protocols: Default::default(),
166 initial_view_id_annotation,
167 })
168 }
169 }
170
171 pub fn start_server(&self, kernel: &Kernel, incoming_dir: Option<fio::DirectoryProxy>) {
177 if let Some(server) = &self.server {
178 let view_bound_protocols = self.view_bound_protocols.lock().take().unwrap();
179 let view_identity = self.view_identity.lock().take().unwrap();
180 log_info!("Presenting view using GraphicalPresenter");
181 start_presentation_loop(
182 kernel,
183 server.clone(),
184 view_bound_protocols,
185 view_identity,
186 incoming_dir,
187 self.initial_view_id_annotation.clone(),
188 );
189 }
190 }
191
192 pub fn present_view(&self, viewport_token: fuiviews::ViewportCreationToken) {
197 if let Some(server) = &self.server {
198 init_viewport_scene(server.clone(), viewport_token);
199
200 let mut memory = self.memory.lock();
202 if let Some(memory_ref) = memory.as_ref() {
203 let bytes = memory_ref.get_size();
204 let refs = Arc::strong_count(memory_ref);
205 *memory = None;
206 log_info!("Released framebuffer memory ({} bytes, {} refs)", bytes, refs);
207 }
208 }
209 }
210
211 fn get_memory(&self) -> Result<Arc<MemoryObject>, Errno> {
213 self.memory.lock().clone().ok_or_else(|| errno!(EIO))
214 }
215
216 fn memory_len(&self) -> usize {
218 self.memory
219 .lock()
220 .as_ref()
221 .map_or(0, |memory| memory.info().map_or(0, |info| info.size_bytes)) as usize
222 }
223
224 fn memory_size(&self) -> usize {
226 self.memory.lock().as_ref().map_or(0, |memory| memory.get_size()) as usize
227 }
228}
229
230#[derive(Clone)]
231struct FramebufferDevice {
232 framebuffer: Arc<Framebuffer>,
233}
234
235type FbFixScreeninfoPtr =
236 MultiArchUserRef<uapi::fb_fix_screeninfo, uapi::arch32::fb_fix_screeninfo>;
237type FbVarScreeninfoPtr =
238 MultiArchUserRef<uapi::fb_var_screeninfo, uapi::arch32::fb_var_screeninfo>;
239
240fn set_display_power(mode: fuidisplay::PowerMode) -> Result<(), Errno> {
241 let singleton_display_power =
242 connect_to_protocol_sync::<fuidisplay::DisplayPowerMarker>().map_err(|_| errno!(ENOENT))?;
243 singleton_display_power
244 .set_power_mode(mode, zx::MonotonicInstant::INFINITE)
245 .map_err(|_| errno!(EIO))?
246 .map_err(|_| errno!(EINVAL))?;
247 Ok(())
248}
249
250impl DeviceOps for FramebufferDevice {
251 fn open(
252 &self,
253 _locked: &mut Locked<FileOpsCore>,
254 _current_task: &CurrentTask,
255 dev: DeviceId,
256 node: &NamespaceNode,
257 _flags: OpenFlags,
258 ) -> Result<Box<dyn FileOps>, Errno> {
259 if dev.minor() != 0 {
260 return error!(ENODEV);
261 }
262 node.entry.node.update_info(|info| {
263 info.size = self.framebuffer.memory_len();
264 info.blocks = self.framebuffer.memory_size() / info.blksize;
265 Ok(())
266 })?;
267 Ok(Box::new(Arc::clone(&self.framebuffer)))
268 }
269}
270impl CloseFreeSafe for Framebuffer {}
272impl FileOps for Framebuffer {
273 fileops_impl_memory!(self, &self.get_memory()?);
274 fileops_impl_noop_sync!();
275
276 fn ioctl(
277 &self,
278 _locked: &mut Locked<Unlocked>,
279 _file: &FileObject,
280 current_task: &CurrentTask,
281 request: u32,
282 arg: SyscallArg,
283 ) -> Result<SyscallResult, Errno> {
284 let user_addr = UserAddress::from(arg);
285 match request {
286 FBIOGET_FSCREENINFO => {
287 let info = self.info.read();
288 let finfo = fb_fix_screeninfo {
289 id: zerocopy::FromBytes::read_from_bytes(&b"Starnix\0\0\0\0\0\0\0\0\0"[..])
290 .unwrap(),
291 smem_start: 0,
292 smem_len: self.memory_len() as u32,
293 type_: FB_TYPE_PACKED_PIXELS,
294 visual: FB_VISUAL_TRUECOLOR,
295 line_length: info.bits_per_pixel / 8 * info.xres,
296 ..fb_fix_screeninfo::default()
297 };
298 let user_ref = FbFixScreeninfoPtr::new(current_task, user_addr);
299 current_task.write_multi_arch_object(user_ref, finfo)?;
300 Ok(SUCCESS)
301 }
302
303 FBIOGET_VSCREENINFO => {
304 let info = self.info.read();
305 let user_ref = FbVarScreeninfoPtr::new(current_task, user_addr);
306 current_task.write_multi_arch_object(user_ref, *info)?;
307 Ok(SUCCESS)
308 }
309
310 FBIOPUT_VSCREENINFO => {
311 let user_ref = FbVarScreeninfoPtr::new(current_task, user_addr);
312 let new_info: fb_var_screeninfo = current_task.read_multi_arch_object(user_ref)?;
313 let old_info = self.info.read();
314 if new_info.as_bytes() != old_info.as_bytes() {
316 return error!(EINVAL);
317 }
318 Ok(SUCCESS)
319 }
320
321 FBIOBLANK => {
322 let arg = u32::from(arg);
323 match arg {
324 FB_BLANK_POWERDOWN => {
325 set_display_power(fuidisplay::PowerMode::Off)?;
326 Ok(SUCCESS)
327 }
328 FB_BLANK_UNBLANK => {
329 set_display_power(fuidisplay::PowerMode::On)?;
330 Ok(SUCCESS)
331 }
332 _ => {
333 track_stub!(TODO("https://fxbug.dev/475633434"), "FBIOBLANK", arg);
334 error!(EINVAL)
335 }
336 }
337 }
338
339 _ => {
340 track_stub!(TODO("https://fxbug.dev/475633434"), "fb ioctl", request);
341 error!(EINVAL)
342 }
343 }
344 }
345}