1use core::cell::Cell;
19use core::mem;
20
21use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
22use kernel::hil;
23use kernel::hil::screen::ScreenRotation;
24use kernel::hil::touch::{GestureEvent, TouchClient, TouchEvent, TouchStatus};
25use kernel::processbuffer::WriteableProcessBuffer;
26use kernel::syscall::{CommandReturn, SyscallDriver};
27use kernel::{ErrorCode, ProcessId};
28
29use capsules_core::driver;
31pub const DRIVER_NUM: usize = driver::NUM::Touch as usize;
32
33mod rw_allow {
35 pub const EVENTS: usize = 2;
43 pub const COUNT: u8 = 3;
45}
46
47fn touch_status_to_number(status: &TouchStatus) -> usize {
48 match status {
49 TouchStatus::Released => 0,
50 TouchStatus::Pressed => 1,
51 TouchStatus::Moved => 2,
52 TouchStatus::Unstarted => 3,
53 }
54}
55
56pub struct App {
57 ack: bool,
58 dropped_events: usize,
59 x: u16,
60 y: u16,
61 status: usize,
62 touch_enable: bool,
63 multi_touch_enable: bool,
64}
65
66impl Default for App {
67 fn default() -> App {
68 App {
69 ack: true,
70 dropped_events: 0,
71 x: 0,
72 y: 0,
73 status: touch_status_to_number(&TouchStatus::Unstarted),
74 touch_enable: false,
75 multi_touch_enable: false,
76 }
77 }
78}
79
80pub struct Touch<'a> {
81 touch: Option<&'a dyn hil::touch::Touch<'a>>,
82 multi_touch: Option<&'a dyn hil::touch::MultiTouch<'a>>,
83 screen: Option<&'a dyn hil::screen::Screen<'a>>,
89 apps: Grant<App, UpcallCount<3>, AllowRoCount<0>, AllowRwCount<{ rw_allow::COUNT }>>,
90 screen_rotation_offset: Cell<ScreenRotation>,
91}
92
93impl<'a> Touch<'a> {
94 pub fn new(
95 touch: Option<&'a dyn hil::touch::Touch<'a>>,
96 multi_touch: Option<&'a dyn hil::touch::MultiTouch<'a>>,
97 screen: Option<&'a dyn hil::screen::Screen<'a>>,
98 grant: Grant<App, UpcallCount<3>, AllowRoCount<0>, AllowRwCount<{ rw_allow::COUNT }>>,
99 ) -> Touch<'a> {
100 Touch {
101 touch,
102 multi_touch,
103 screen,
104 screen_rotation_offset: Cell::new(ScreenRotation::Normal),
105 apps: grant,
106 }
107 }
108
109 pub fn set_screen_rotation_offset(&self, screen_rotation_offset: ScreenRotation) {
110 self.screen_rotation_offset.set(screen_rotation_offset);
111 }
112
113 fn touch_enable(&self) -> Result<(), ErrorCode> {
114 let mut enabled = false;
115 for app in self.apps.iter() {
116 if app.enter(|app, _| app.touch_enable) {
117 enabled = true;
118 break;
119 }
120 }
121 self.touch.map_or_else(
122 || {
123 self.multi_touch
127 .map_or(Err(ErrorCode::NODEVICE), |multi_touch| {
128 if enabled {
129 multi_touch.enable()
130 } else {
131 multi_touch.disable()
132 }
133 })
134 },
135 |touch| {
136 if enabled {
137 touch.enable()
138 } else {
139 touch.disable()
140 }
141 },
142 )
143 }
144
145 fn multi_touch_enable(&self) -> Result<(), ErrorCode> {
146 let mut enabled = false;
147 for app in self.apps.iter() {
148 if app.enter(|app, _| app.multi_touch_enable) {
149 enabled = true;
150 break;
151 }
152 }
153 self.multi_touch
154 .map_or(Err(ErrorCode::NODEVICE), |multi_touch| {
155 if enabled {
156 multi_touch.enable()
157 } else {
158 multi_touch.disable()
159 }
160 })
161 }
162
163 fn update_rotation(&self, touch_event: &mut TouchEvent) {
166 if let Some(screen) = self.screen {
167 let rotation = screen.get_rotation() + self.screen_rotation_offset.get();
168 let (mut width, mut height) = screen.get_resolution();
169
170 let (x, y) = match rotation {
171 ScreenRotation::Rotated90 => {
172 mem::swap(&mut width, &mut height);
173 (touch_event.y, height as u16 - touch_event.x)
174 }
175 ScreenRotation::Rotated180 => {
176 (width as u16 - touch_event.x, height as u16 - touch_event.y)
177 }
178 ScreenRotation::Rotated270 => {
179 mem::swap(&mut width, &mut height);
180 (width as u16 - touch_event.y, touch_event.x)
181 }
182 _ => (touch_event.x, touch_event.y),
183 };
184
185 touch_event.x = x;
186 touch_event.y = y;
187 }
188 }
189}
190
191impl hil::touch::TouchClient for Touch<'_> {
192 fn touch_event(&self, mut event: TouchEvent) {
193 self.update_rotation(&mut event);
195 for app in self.apps.iter() {
200 app.enter(|app, kernel_data| {
201 let event_status = touch_status_to_number(&event.status);
202 if app.x != event.x || app.y != event.y || app.status != event_status {
203 app.x = event.x;
204 app.y = event.y;
205 app.status = event_status;
206
207 let pressure_size = match event.pressure {
208 Some(pressure) => (pressure as usize) << 16,
209 None => 0,
210 } | match event.size {
211 Some(size) => size as usize,
212 None => 0,
213 };
214 let _ = kernel_data.schedule_upcall(
215 0,
216 (
217 event_status,
218 (event.x as usize) << 16 | event.y as usize,
219 pressure_size,
220 ),
221 );
222 }
223 });
224 }
225 }
226}
227
228impl hil::touch::MultiTouchClient for Touch<'_> {
229 fn touch_events(&self, touch_events: &[TouchEvent], num_events: usize) {
230 let len = if touch_events.len() < num_events {
231 touch_events.len()
232 } else {
233 num_events
234 };
235 if len > 0 {
236 self.touch_event(touch_events[0]);
239 for app in self.apps.iter() {
241 app.enter(|app, kernel_data| {
242 if app.ack {
243 app.dropped_events = 0;
244
245 let num = kernel_data
246 .get_readwrite_processbuffer(rw_allow::EVENTS)
247 .and_then(|events| {
248 events.mut_enter(|buffer| {
249 let num = if buffer.len() / 8 < len {
250 buffer.len() / 8
251 } else {
252 len
253 };
254
255 for event_index in 0..num {
256 let mut event = touch_events[event_index];
257 self.update_rotation(&mut event);
258 let event_status = touch_status_to_number(&event.status);
259 let offset = event_index * 8;
265 if buffer.len() > event_index + 8 {
266 buffer[offset].set(event.id as u8);
267 buffer[offset + 1].set(event_status as u8);
268 buffer[offset + 2].set((event.x & 0xFF) as u8);
269 buffer[offset + 3].set(((event.x & 0xFFFF) >> 8) as u8);
270 buffer[offset + 4].set((event.y & 0xFF) as u8);
271 buffer[offset + 5].set(((event.y & 0xFFFF) >> 8) as u8);
272 buffer[offset + 6].set(
273 if let Some(size) = event.size {
274 size as u8
275 } else {
276 0
277 },
278 );
279 buffer[offset + 7].set(
280 if let Some(pressure) = event.pressure {
281 pressure as u8
282 } else {
283 0
284 },
285 );
286 } else {
287 break;
288 }
289 }
290 num
291 })
292 })
293 .unwrap_or(0);
294 let dropped_events = app.dropped_events;
295 if num > 0 {
296 app.ack = false;
297 let _ = kernel_data
298 .schedule_upcall(2, (num, dropped_events, len.saturating_sub(num)));
299 }
300 } else {
301 app.dropped_events += 1;
302 }
303 });
304 }
305 }
306 }
307}
308
309impl hil::touch::GestureClient for Touch<'_> {
310 fn gesture_event(&self, event: GestureEvent) {
311 for app in self.apps.iter() {
312 app.enter(|_app, kernel_data| {
313 let gesture_id = match event {
314 GestureEvent::SwipeUp => 1,
315 GestureEvent::SwipeDown => 2,
316 GestureEvent::SwipeLeft => 3,
317 GestureEvent::SwipeRight => 4,
318 GestureEvent::ZoomIn => 5,
319 GestureEvent::ZoomOut => 6,
320 };
321 let _ = kernel_data.schedule_upcall(1, (gesture_id, 0, 0));
322 });
323 }
324 }
325}
326
327impl SyscallDriver for Touch<'_> {
328 fn command(
329 &self,
330 command_num: usize,
331 _data1: usize,
332 _data2: usize,
333 processid: ProcessId,
334 ) -> CommandReturn {
335 match command_num {
336 0 => CommandReturn::success(),
338
339 1 => {
341 self.apps
342 .enter(processid, |app, _| {
343 app.touch_enable = true;
344 })
345 .unwrap_or(());
346 let _ = self.touch_enable();
347 CommandReturn::success()
348 }
349
350 2 => {
352 self.apps
353 .enter(processid, |app, _| {
354 app.touch_enable = false;
355 })
356 .unwrap_or(());
357 let _ = self.touch_enable();
358 CommandReturn::success()
359 }
360
361 10 => {
363 self.apps
364 .enter(processid, |app, _| {
365 app.ack = true;
366 })
367 .unwrap_or(());
368 CommandReturn::success()
369 }
370
371 11 => {
373 self.apps
374 .enter(processid, |app, _| {
375 app.multi_touch_enable = true;
376 })
377 .unwrap_or(());
378 let _ = self.multi_touch_enable();
379 CommandReturn::success()
380 }
381
382 12 => {
384 self.apps
385 .enter(processid, |app, _| {
386 app.multi_touch_enable = false;
387 })
388 .unwrap_or(());
389 let _ = self.multi_touch_enable();
390 CommandReturn::success()
391 }
392
393 100 => {
395 let num_touches = if let Some(multi_touch) = self.multi_touch {
396 multi_touch.get_num_touches()
397 } else {
398 usize::from(self.touch.is_some())
399 };
400 CommandReturn::success_u32(num_touches as u32)
401 }
402
403 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
404 }
405 }
406
407 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
408 self.apps.enter(processid, |_, _| {})
409 }
410}