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