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}