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