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}