1use crate::rendering::eink::{
10 FbFixScreeninfo, FbVarScreeninfo, MxcfbRect, MxcfbUpdateData, FBIOGET_FSCREENINFO,
11 FBIOGET_VSCREENINFO, MXCFB_SEND_UPDATE,
12};
13use log::{debug, info, warn};
14
15pub fn dump_ppm(path: &str, buf: &[u8], w: usize, h: usize) {
16 let mut out = Vec::with_capacity(15 + w * h * 3);
17 out.extend_from_slice(format!("P6\n{} {}\n255\n", w, h).as_bytes());
18 for i in 0..w * h {
19 let off = i * 2;
20 let v = (buf[off] as u16) | ((buf[off + 1] as u16) << 8);
21 let r = ((v >> 11) & 0x1f) as u8;
22 let g = ((v >> 5) & 0x3f) as u8;
23 let b = (v & 0x1f) as u8;
24 out.push((r << 3) | (r >> 2));
25 out.push((g << 2) | (g >> 4));
26 out.push((b << 3) | (b >> 2));
27 }
28 match std::fs::write(path, &out) {
29 Ok(_) => debug!("wrote {} ({} bytes)", path, out.len()),
30 Err(e) => warn!("PPM write err: {}", e),
31 }
32}
33
34pub struct Fb {
35 _file: std::fs::File,
36 pub fd: libc::c_int,
37 pub ptr: *mut u8,
38 pub map_len: usize,
39 pub stride: usize,
40 pub bpp: usize,
41 pub xres: usize,
42 pub yres: usize,
43 r_off: u32,
44 g_off: u32,
45 b_off: u32,
46}
47
48impl Fb {
49 pub fn open() -> Option<Fb> {
50 use std::os::unix::io::AsRawFd;
51 let file = std::fs::OpenOptions::new()
52 .read(true)
53 .write(true)
54 .open("/dev/fb0")
55 .ok()?;
56 let fd = file.as_raw_fd();
57 let mut var = FbVarScreeninfo::default();
58 let mut fix = FbFixScreeninfo::default();
59 unsafe {
64 if libc::ioctl(fd, FBIOGET_VSCREENINFO as _, &mut var) < 0 {
65 return None;
66 }
67 if libc::ioctl(fd, FBIOGET_FSCREENINFO as _, &mut fix) < 0 {
68 return None;
69 }
70 }
71 let map_len = fix.smem_len as usize;
72 let ptr = unsafe {
77 libc::mmap(
78 std::ptr::null_mut(),
79 map_len,
80 libc::PROT_READ | libc::PROT_WRITE,
81 libc::MAP_SHARED,
82 fd,
83 0,
84 )
85 };
86 if ptr == libc::MAP_FAILED {
87 return None;
88 }
89 info!(
90 "fb: {}x{} bpp={} line_length={} smem_len={}",
91 var.xres, var.yres, var.bits_per_pixel, fix.line_length, fix.smem_len
92 );
93 let (r_off, g_off, b_off) = (
94 var.rest.first().copied().unwrap_or(16),
95 var.rest.get(3).copied().unwrap_or(8),
96 var.rest.get(6).copied().unwrap_or(0),
97 );
98 info!("fb: rgb offsets r={} g={} b={}", r_off, g_off, b_off);
99 Some(Fb {
100 _file: file,
101 fd,
102 ptr: ptr as *mut u8,
103 map_len,
104 stride: fix.line_length as usize,
105 bpp: var.bits_per_pixel as usize,
106 xres: var.xres as usize,
107 yres: var.yres as usize,
108 r_off,
109 g_off,
110 b_off,
111 })
112 }
113
114 pub fn present(
117 &self,
118 buf: &[u8],
119 w: usize,
120 h: usize,
121 full: bool,
122 top: usize,
123 rh: usize,
124 waveform: u32,
125 ) {
126 let fb = unsafe { std::slice::from_raw_parts_mut(self.ptr, self.map_len) };
131 let bpp = self.bpp;
132 let stride = self.stride;
133 let (y0, y1) = if full {
134 (0, h.min(self.yres))
135 } else {
136 let end = (top + rh).min(h).min(self.yres);
137 (top.min(end), end)
138 };
139 let x1 = w.min(self.xres);
140 for y in y0..y1 {
141 let row = y * w;
142 let fb_row = y * stride;
143 for x in 0..x1 {
144 let buf_off = (row + x) * 2;
145 let px = (buf[buf_off] as u16) | ((buf[buf_off + 1] as u16) << 8);
146 let off = fb_row + x * (bpp / 8);
147 match bpp {
148 32 => {
149 let r = (((px >> 11) & 0x1f) << 3) as u8;
150 let g = (((px >> 5) & 0x3f) << 2) as u8;
151 let b = ((px & 0x1f) << 3) as u8;
152 fb[off + (self.r_off / 8) as usize] = r;
153 fb[off + (self.g_off / 8) as usize] = g;
154 fb[off + (self.b_off / 8) as usize] = b;
155 fb[off + 3] = 0xff;
156 }
157 16 => {
158 fb[off] = (px & 0xff) as u8;
159 fb[off + 1] = (px >> 8) as u8;
160 }
161 _ => {}
162 }
163 }
164 }
165 let sync_start = y0 * self.stride;
166 let sync_len = ((y1 - y0) * self.stride).max(1);
167 unsafe {
171 libc::msync(
172 self.ptr.add(sync_start) as *mut libc::c_void,
173 sync_len,
174 libc::MS_SYNC,
175 );
176 }
177 let upd = MxcfbUpdateData {
178 update_region: MxcfbRect {
179 top: y0 as u32,
180 left: 0,
181 width: x1 as u32,
182 height: (y1 - y0) as u32,
183 },
184 waveform_mode: waveform,
185 update_mode: if full { 1 } else { 0 },
186 update_marker: 1,
187 temp: 0x1000,
188 flags: 0,
189 };
190 let rc = unsafe { libc::ioctl(self.fd, MXCFB_SEND_UPDATE as _, &upd) };
194 debug!(
195 "eink refresh (GC16 {} rows=[{},{}] {}x{}) rc={}",
196 if full { "FULL" } else { "PARTIAL" },
197 y0,
198 y1,
199 x1,
200 y1 - y0,
201 rc
202 );
203 }
204}
205
206impl Drop for Fb {
207 fn drop(&mut self) {
208 unsafe {
212 libc::munmap(self.ptr as *mut libc::c_void, self.map_len);
213 }
214 }
215}