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 let _ = upcalls.schedule_upcall(
88 0,
89 (kernel::errorcode::into_statuscode(Ok(())), 0, 0),
90 );
91 app.awaiting = None;
92 }
93 }
94 }
95 });
96 if found {
97 break;
98 }
99 }
100
101 if !found {
102 // No userspace requests pending at this time
103 }
104 }
105}
106
107#[derive(Copy, Clone)]
108enum Request {
109 EnableAndAttach,
110}
111
112impl<'a, C> SyscallDriver for UsbSyscallDriver<'a, C>
113where
114 C: hil::usb::Client<'a>,
115{
116 fn command(
117 &self,
118 command_num: usize,
119 _arg: usize,
120 _: usize,
121 processid: ProcessId,
122 ) -> CommandReturn {
123 match command_num {
124 // This driver is present
125 0 => CommandReturn::success(),
126
127 // Enable USB controller, attach to bus, and service default control endpoint
128 1 => {
129 let result = self
130 .apps
131 .enter(processid, |app, _| {
132 if app.awaiting.is_some() {
133 // Each app may make only one request at a time
134 Err(ErrorCode::BUSY)
135 } else {
136 app.awaiting = Some(Request::EnableAndAttach);
137 Ok(())
138 }
139 })
140 .unwrap_or_else(|err| Err(err.into()));
141
142 match result {
143 Ok(()) => {
144 self.serve_waiting_apps();
145 CommandReturn::success()
146 }
147 Err(e) => CommandReturn::failure(e),
148 }
149 }
150
151 _ => CommandReturn::failure(ErrorCode::NOSUPPORT),
152 }
153 }
154
155 fn allocate_grant(&self, processid: ProcessId) -> Result<(), kernel::process::Error> {
156 self.apps.enter(processid, |_, _| {})
157 }
158}