Adapts libtock drivers for vendor HID (#500)
* adapts libtock drivers for vendor HID * status with timeout
This commit is contained in:
158
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
158
third_party/libtock-drivers/src/usb_ctap_hid.rs
vendored
@@ -14,10 +14,11 @@
|
||||
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use crate::console::Console;
|
||||
use crate::result::TockError;
|
||||
use crate::result::{OutOfRangeError, TockError, TockResult};
|
||||
use crate::timer::Duration;
|
||||
use crate::{timer, util};
|
||||
use core::cell::Cell;
|
||||
use core::convert::TryFrom;
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
use core::fmt::Write;
|
||||
use libtock_core::result::{CommandError, EALREADY, EBUSY, SUCCESS};
|
||||
@@ -65,17 +66,30 @@ pub fn setup() -> bool {
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum UsbInterface {
|
||||
MainHid = 0,
|
||||
pub enum UsbEndpoint {
|
||||
MainHid = 1,
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
VendorHid = 1,
|
||||
VendorHid = 2,
|
||||
}
|
||||
|
||||
impl TryFrom<usize> for UsbEndpoint {
|
||||
type Error = TockError;
|
||||
|
||||
fn try_from(endpoint_num: usize) -> Result<Self, TockError> {
|
||||
match endpoint_num {
|
||||
1 => Ok(UsbEndpoint::MainHid),
|
||||
#[cfg(feature = "vendor_hid")]
|
||||
2 => Ok(UsbEndpoint::VendorHid),
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SendOrRecvStatus {
|
||||
Error,
|
||||
Timeout,
|
||||
Sent,
|
||||
Received(UsbInterface),
|
||||
Received(UsbEndpoint),
|
||||
}
|
||||
|
||||
/// Waits to receive a packet.
|
||||
@@ -85,7 +99,7 @@ pub enum SendOrRecvStatus {
|
||||
pub fn recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> Option<SendOrRecvStatus> {
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
@@ -97,12 +111,12 @@ pub fn recv_with_timeout(
|
||||
let result = recv_with_timeout_detail(buf, timeout_delay);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Some(SendOrRecvStatus::Received(interface)) = result {
|
||||
if let Ok(SendOrRecvStatus::Received(endpoint)) = result {
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on interface {}",
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
interface as u8,
|
||||
endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -126,8 +140,8 @@ pub fn recv_with_timeout(
|
||||
pub fn send_or_recv_with_timeout(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
interface: UsbInterface,
|
||||
) -> Option<SendOrRecvStatus> {
|
||||
endpoint: UsbEndpoint,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
@@ -137,15 +151,15 @@ pub fn send_or_recv_with_timeout(
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, interface);
|
||||
let result = send_or_recv_with_timeout_detail(buf, timeout_delay, endpoint);
|
||||
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
if let Some(SendOrRecvStatus::Received(received_interface)) = result {
|
||||
if let Ok(SendOrRecvStatus::Received(received_endpoint)) = result {
|
||||
writeln!(
|
||||
Console::new(),
|
||||
"Received packet = {:02x?} on interface {}",
|
||||
"Received packet = {:02x?} on endpoint {}",
|
||||
buf as &[u8],
|
||||
received_interface as u8,
|
||||
received_endpoint as u8,
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
@@ -156,54 +170,38 @@ pub fn send_or_recv_with_timeout(
|
||||
fn recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
) -> Option<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf);
|
||||
if result.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction| {
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::RECEIVED => {
|
||||
// TODO: set the correct interface
|
||||
SendOrRecvStatus::Received(UsbInterface::MainHid)
|
||||
UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i))
|
||||
}
|
||||
// Unknown direction or "transmitted" sent by the kernel.
|
||||
_ => SendOrRecvStatus::Error,
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::RECEIVE,
|
||||
&mut alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let timeout_expired = Cell::new(false);
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
timeout_expired.set(true);
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = match timeout_callback.init() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Some(SendOrRecvStatus::Error),
|
||||
};
|
||||
let timeout_alarm = match timeout.set_alarm(timeout_delay) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Some(SendOrRecvStatus::Error),
|
||||
};
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB reception.
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0);
|
||||
if result_code.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::RECEIVE, 0, 0)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some() || timeout_expired.get());
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
@@ -212,7 +210,7 @@ fn recv_with_timeout_detail(
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if !timeout_expired.get() {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
@@ -230,7 +228,7 @@ fn recv_with_timeout_detail(
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if status.get().is_none() {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB receive due to timeout").unwrap();
|
||||
let result_code =
|
||||
@@ -253,63 +251,54 @@ fn recv_with_timeout_detail(
|
||||
}
|
||||
}
|
||||
|
||||
status.get()
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
}
|
||||
|
||||
fn send_or_recv_with_timeout_detail(
|
||||
buf: &mut [u8; 64],
|
||||
timeout_delay: Duration<isize>,
|
||||
// TODO: To be used as part of the syscall.
|
||||
_interface: UsbInterface,
|
||||
) -> Option<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf);
|
||||
if result.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
endpoint: UsbEndpoint,
|
||||
) -> TockResult<SendOrRecvStatus> {
|
||||
let result = syscalls::allow(DRIVER_NUMBER, allow_nr::TRANSMIT_OR_RECEIVE, buf)?;
|
||||
|
||||
let status = Cell::new(None);
|
||||
let mut alarm = |direction| {
|
||||
let mut alarm = |direction, endpoint| {
|
||||
status.set(Some(match direction {
|
||||
subscribe_nr::callback_status::TRANSMITTED => SendOrRecvStatus::Sent,
|
||||
subscribe_nr::callback_status::TRANSMITTED => Ok(SendOrRecvStatus::Sent),
|
||||
subscribe_nr::callback_status::RECEIVED => {
|
||||
// TODO: set the correct interface
|
||||
SendOrRecvStatus::Received(UsbInterface::MainHid)
|
||||
UsbEndpoint::try_from(endpoint).map(|i| SendOrRecvStatus::Received(i))
|
||||
}
|
||||
// Unknown direction sent by the kernel.
|
||||
_ => SendOrRecvStatus::Error,
|
||||
_ => Err(OutOfRangeError.into()),
|
||||
}));
|
||||
};
|
||||
|
||||
let subscription = syscalls::subscribe::<callback::Identity1Consumer, _>(
|
||||
let subscription = syscalls::subscribe::<callback::Identity2Consumer, _>(
|
||||
DRIVER_NUMBER,
|
||||
subscribe_nr::TRANSMIT_OR_RECEIVE,
|
||||
&mut alarm,
|
||||
);
|
||||
if subscription.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
)?;
|
||||
|
||||
// Setup a time-out callback.
|
||||
let timeout_expired = Cell::new(false);
|
||||
let mut timeout_callback = timer::with_callback(|_, _| {
|
||||
timeout_expired.set(true);
|
||||
status.set(Some(Ok(SendOrRecvStatus::Timeout)));
|
||||
});
|
||||
let mut timeout = match timeout_callback.init() {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Some(SendOrRecvStatus::Error),
|
||||
};
|
||||
let timeout_alarm = match timeout.set_alarm(timeout_delay) {
|
||||
Ok(x) => x,
|
||||
Err(_) => return Some(SendOrRecvStatus::Error),
|
||||
};
|
||||
let mut timeout = timeout_callback.init()?;
|
||||
let timeout_alarm = timeout.set_alarm(timeout_delay)?;
|
||||
|
||||
// Trigger USB transmission.
|
||||
let result_code = syscalls::command(DRIVER_NUMBER, command_nr::TRANSMIT_OR_RECEIVE, 0, 0);
|
||||
if result_code.is_err() {
|
||||
return Some(SendOrRecvStatus::Error);
|
||||
}
|
||||
let result_code = syscalls::command(
|
||||
DRIVER_NUMBER,
|
||||
command_nr::TRANSMIT_OR_RECEIVE,
|
||||
endpoint as usize,
|
||||
0,
|
||||
)?;
|
||||
|
||||
util::yieldk_for(|| status.get().is_some() || timeout_expired.get());
|
||||
util::yieldk_for(|| status.get().is_some());
|
||||
let status = status.get().unwrap();
|
||||
|
||||
// Cleanup alarm callback.
|
||||
match timeout.stop_alarm(timeout_alarm) {
|
||||
@@ -318,7 +307,7 @@ fn send_or_recv_with_timeout_detail(
|
||||
return_code: EALREADY,
|
||||
..
|
||||
})) => {
|
||||
if !timeout_expired.get() {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "debug_ctap")]
|
||||
writeln!(
|
||||
Console::new(),
|
||||
@@ -336,7 +325,7 @@ fn send_or_recv_with_timeout_detail(
|
||||
}
|
||||
|
||||
// Cancel USB transaction if necessary.
|
||||
if status.get().is_none() {
|
||||
if matches!(status, Ok(SendOrRecvStatus::Timeout)) {
|
||||
#[cfg(feature = "verbose_usb")]
|
||||
writeln!(Console::new(), "Cancelling USB transaction due to timeout").unwrap();
|
||||
let result_code =
|
||||
@@ -361,5 +350,8 @@ fn send_or_recv_with_timeout_detail(
|
||||
writeln!(Console::new(), "Cancelled USB transaction!").unwrap();
|
||||
}
|
||||
|
||||
status.get()
|
||||
core::mem::drop(result);
|
||||
core::mem::drop(subscription);
|
||||
core::mem::drop(result_code);
|
||||
status
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user