capsules_extra/usb/usb_user.rs
1// Licensed under the Apache License, Version 2.0 or the MIT License.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3// Copyright Tock Contributors 2022.
4
5//! USB system call interface
6//!
7//! This capsule provides a system call interface to the USB controller.
8//!
9//! ## Instantiation
10//!
11//! The `UsbSyscallDriver` must be created by passing a reference to something
12//! that implements `hil::usb::Client` (that is, something that is connected to
13//! the USBC), as well as a `Grant` for managing application requests. For
14//! example:
15//!
16//! ```rust,ignore
17//! # use kernel::static_init;
18//!
19//! // Configure the USB controller
20//! let usb_client = static_init!(
21//! capsules::usb::usbc_client::Client<'static, sam4l::usbc::Usbc<'static>>,
22//! capsules::usb::usbc_client::Client::new(&sam4l::usbc::USBC));
23//! sam4l::usbc::USBC.set_client(usb_client);
24//!
25//! // Configure the USB userspace driver
26//! let usb_driver = static_init!(
27//! capsules::usb::usb_user::UsbSyscallDriver<'static,
28//! capsules::usb::usbc_client::Client<'static, sam4l::usbc::Usbc<'static>>>,
29//! capsules::usb::usb_user::UsbSyscallDriver::new(
30//! usb_client, board_kernel.create_grant(&grant_cap)));
31//! ```
32
33use kernel::grant::{AllowRoCount, AllowRwCount, Grant, UpcallCount};
34use kernel::hil;
35use kernel::syscall::{CommandReturn, SyscallDriver};
36use kernel::utilities::cells::OptionalCell;
37use kernel::{ErrorCode, ProcessId};
38
39use capsules_core::driver;
40pub const DRIVER_NUM: usize = driver::NUM::UsbUser as usize;
41
42#[derive(Default)]
43pub struct App {
44 awaiting: Option<Request>,
45}
46
47pub struct UsbSyscallDriver<'a, C: hil::usb::Client<'a>> {
48 usbc_client: &'a C,
49 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
50 serving_app: OptionalCell<ProcessId>,
51}
52
53impl<'a, C> UsbSyscallDriver<'a, C>
54where
55 C: hil::usb::Client<'a>,
56{
57 pub fn new(
58 usbc_client: &'a C,
59 apps: Grant<App, UpcallCount<1>, AllowRoCount<0>, AllowRwCount<0>>,
60 ) -> Self {
61 UsbSyscallDriver {
62 usbc_client,
63 apps,
64 serving_app: OptionalCell::empty(),
65 }
66 }
67
68 fn serve_waiting_apps(&self) {
69 if self.serving_app.is_some() {
70 // An operation on the USBC client is in progress
71 return;
72 }
73
74 // Find a waiting app and start its requested computation
75 let mut found = false;
76 for app in self.apps.iter() {
77 app.enter(|app, upcalls| {
78 if let Some(request) = app.awaiting {
79 found = true;
80 match request {
81 Request::EnableAndAttach => {
82 // Enable and attach (synchronously)
83 self.usbc_client.enable();
84 self.usbc_client.attach();
85
86 // Schedule a callback immediately
87 upcalls
88 .schedule_upcall(
89 0,
90 (kernel::errorcode::into_statuscode(Ok(())), 0, 0),
91 )
92 .ok();
93 app.awaiting = None;
94 }
95 }
96 }
97 });
98 if found {
99 break;
100 }
101 }
102
103 if !found {
104 // No userspace requests pending at this time
105 }
106 }
107}
108
109#[derive(Copy, Clone)]
110enum Request {
111 EnableAndAttach,
112}
113
114impl<'a, C> SyscallDriver for UsbSyscallDriver<'a, C>
115where
116 C: hil::usb::Client<'a>,
117{
118 fn command(
119 &self,
120 command_num: usize,
121 _arg: usize,
122 _: usize,
123 processid: ProcessId,
124 ) -> CommandReturn {
125 match command_num {
126 // This driver is present
127 0 => CommandReturn::success(),
128
129 // Enable USB controller, attach to bus, and service default control endpoint
130 1 => {
131 let result = self
132 .apps
133 .enter(processid, |app, _| {
134 if app.awaiting.is_some() {
135 // Each app may make only one request at a time
136 Err(ErrorCode::BUSY)
137 } else {
138 app.awaiting = Some(Request::EnableAndAttach);
139 Ok(())
140 }
141 })
142 .unwrap_or_else(|err| Err(err.into()));
143
144 match result {
145 Ok(()) => {
146 self.serve_waiting_apps();
147 CommandReturn::success()
148 }
149 Err(e) => CommandReturn::failure(e),
150 }
151 }
152
153 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
154 }
155 }
156
157 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
158 self.apps.enter(processid, |_, _| {})
159 }
160}