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 kernel_data
215 .schedule_upcall(
216 0,
217 (
218 event_status,
219 (event.x as usize) << 16 | event.y as usize,
220 pressure_size,
221 ),
222 )
223 .ok();
224 }
225 });
226 }
227 }
228}
229
230impl hil::touch::MultiTouchClient for Touch<'_> {
231 fn touch_events(&self, touch_events: &[TouchEvent], num_events: usize) {
232 let len = if touch_events.len() < num_events {
233 touch_events.len()
234 } else {
235 num_events
236 };
237 if len > 0 {
238 self.touch_event(touch_events[0]);
241 for app in self.apps.iter() {
243 app.enter(|app, kernel_data| {
244 if app.ack {
245 app.dropped_events = 0;
246
247 let num = kernel_data
248 .get_readwrite_processbuffer(rw_allow::EVENTS)
249 .and_then(|events| {
250 events.mut_enter(|buffer| {
251 let num = if buffer.len() / 8 < len {
252 buffer.len() / 8
253 } else {
254 len
255 };
256
257 for event_index in 0..num {
258 let mut event = touch_events[event_index];
259 self.update_rotation(&mut event);
260 let event_status = touch_status_to_number(&event.status);
261 let offset = event_index * 8;
267 if buffer.len() > event_index + 8 {
268 buffer[offset].set(event.id as u8);
269 buffer[offset + 1].set(event_status as u8);
270 buffer[offset + 2].set((event.x & 0xFF) as u8);
271 buffer[offset + 3].set(((event.x & 0xFFFF) >> 8) as u8);
272 buffer[offset + 4].set((event.y & 0xFF) as u8);
273 buffer[offset + 5].set(((event.y & 0xFFFF) >> 8) as u8);
274 buffer[offset + 6].set(
275 if let Some(size) = event.size {
276 size as u8
277 } else {
278 0
279 },
280 );
281 buffer[offset + 7].set(
282 if let Some(pressure) = event.pressure {
283 pressure as u8
284 } else {
285 0
286 },
287 );
288 } else {
289 break;
290 }
291 }
292 num
293 })
294 })
295 .unwrap_or(0);
296 let dropped_events = app.dropped_events;
297 if num > 0 {
298 app.ack = false;
299 kernel_data
300 .schedule_upcall(2, (num, dropped_events, len.saturating_sub(num)))
301 .ok();
302 }
303 } else {
304 app.dropped_events += 1;
305 }
306 });
307 }
308 }
309 }
310}
311
312impl hil::touch::GestureClient for Touch<'_> {
313 fn gesture_event(&self, event: GestureEvent) {
314 for app in self.apps.iter() {
315 app.enter(|_app, kernel_data| {
316 let gesture_id = match event {
317 GestureEvent::SwipeUp => 1,
318 GestureEvent::SwipeDown => 2,
319 GestureEvent::SwipeLeft => 3,
320 GestureEvent::SwipeRight => 4,
321 GestureEvent::ZoomIn => 5,
322 GestureEvent::ZoomOut => 6,
323 };
324 kernel_data.schedule_upcall(1, (gesture_id, 0, 0)).ok();
325 });
326 }
327 }
328}
329
330impl SyscallDriver for Touch<'_> {
331 fn command(
332 &self,
333 command_num: usize,
334 _data1: usize,
335 _data2: usize,
336 processid: ProcessId,
337 ) -> CommandReturn {
338 match command_num {
339 0 => CommandReturn::success(),
341
342 1 => {
344 self.apps
345 .enter(processid, |app, _| {
346 app.touch_enable = true;
347 })
348 .unwrap_or(());
349 let _ = self.touch_enable();
350 CommandReturn::success()
351 }
352
353 2 => {
355 self.apps
356 .enter(processid, |app, _| {
357 app.touch_enable = false;
358 })
359 .unwrap_or(());
360 let _ = self.touch_enable();
361 CommandReturn::success()
362 }
363
364 10 => {
366 self.apps
367 .enter(processid, |app, _| {
368 app.ack = true;
369 })
370 .unwrap_or(());
371 CommandReturn::success()
372 }
373
374 11 => {
376 self.apps
377 .enter(processid, |app, _| {
378 app.multi_touch_enable = true;
379 })
380 .unwrap_or(());
381 let _ = self.multi_touch_enable();
382 CommandReturn::success()
383 }
384
385 12 => {
387 self.apps
388 .enter(processid, |app, _| {
389 app.multi_touch_enable = false;
390 })
391 .unwrap_or(());
392 let _ = self.multi_touch_enable();
393 CommandReturn::success()
394 }
395
396 100 => {
398 let num_touches = if let Some(multi_touch) = self.multi_touch {
399 multi_touch.get_num_touches()
400 } else {
401 usize::from(self.touch.is_some())
402 };
403 CommandReturn::success_u32(num_touches as u32)
404 }
405
406 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
407 }
408 }
409
410 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
411 self.apps.enter(processid, |_, _| {})
412 }
413}