mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-07-23 15:08:19 +00:00
chore(core/rust): Add uPy-exported io_usb_start fn
This commit is contained in:
parent
47d56deed3
commit
a581ace9d5
@ -129,6 +129,7 @@ fn generate_micropython_bindings() {
|
|||||||
.allowlist_function("trezor_obj_get_ll_checked")
|
.allowlist_function("trezor_obj_get_ll_checked")
|
||||||
.allowlist_function("trezor_obj_get_ull_checked")
|
.allowlist_function("trezor_obj_get_ull_checked")
|
||||||
.allowlist_function("trezor_obj_str_from_rom_text")
|
.allowlist_function("trezor_obj_str_from_rom_text")
|
||||||
|
.allowlist_var("mp_const_empty_tuple_obj")
|
||||||
// buffer
|
// buffer
|
||||||
.allowlist_function("mp_get_buffer")
|
.allowlist_function("mp_get_buffer")
|
||||||
.allowlist_var("MP_BUFFER_READ")
|
.allowlist_var("MP_BUFFER_READ")
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
#include "librust_qstr.h"
|
#include "librust_qstr.h"
|
||||||
|
|
||||||
|
mp_obj_t io_usb_start(mp_obj_t serial_number);
|
||||||
|
|
||||||
mp_obj_t protobuf_type_for_name(mp_obj_t name);
|
mp_obj_t protobuf_type_for_name(mp_obj_t name);
|
||||||
mp_obj_t protobuf_type_for_wire(mp_obj_t wire_id);
|
mp_obj_t protobuf_type_for_wire(mp_obj_t wire_id);
|
||||||
mp_obj_t protobuf_decode(mp_obj_t buf, mp_obj_t def,
|
mp_obj_t protobuf_decode(mp_obj_t buf, mp_obj_t def,
|
||||||
@ -16,4 +18,4 @@ extern mp_obj_module_t mp_module_trezorui2;
|
|||||||
|
|
||||||
#ifdef TREZOR_EMULATOR
|
#ifdef TREZOR_EMULATOR
|
||||||
mp_obj_t ui_debug_layout_type();
|
mp_obj_t ui_debug_layout_type();
|
||||||
#endif
|
#endif
|
@ -1,7 +1,16 @@
|
|||||||
|
use core::ops::RangeFrom;
|
||||||
|
|
||||||
|
use cstr_core::{cstr, CStr};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
|
micropython::{buffer::Buffer, obj::Obj},
|
||||||
time::{Duration, Instant},
|
time::{Duration, Instant},
|
||||||
trezorhal::usb::{usb_is_ready_to_read, usb_is_ready_to_write, IfaceTicket},
|
trezorhal::usb::{
|
||||||
|
usb_is_ready_to_read, usb_is_ready_to_write, usb_open, IfaceTicket, UsbConfig, UsbError,
|
||||||
|
WebUsbConfig,
|
||||||
|
},
|
||||||
|
util,
|
||||||
util::wait_in_busy_loop,
|
util::wait_in_busy_loop,
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -87,3 +96,190 @@ pub fn poll_io(resources: &[Resource], timeout: Duration) -> Result<Event, Error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" fn io_usb_start(serial_number: Obj) -> Obj {
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
use crate::trezorhal::usb::VcpConfig;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "bitcoin_only"))]
|
||||||
|
use crate::trezorhal::usb::HidConfig;
|
||||||
|
|
||||||
|
// SAFETY:
|
||||||
|
// In this function we often take mut refs to static mut buffers. This requires
|
||||||
|
// an unsafe block. Safety rationale:
|
||||||
|
// - We are in a single-threaded context.
|
||||||
|
// - `io_usb_start` is required to be called with USB turned off.
|
||||||
|
// - Underlying USB stack is required to manage the buffers on its own,
|
||||||
|
// guarding i.e. against escaping of stale buffer content.
|
||||||
|
|
||||||
|
const UDP_PORT: u16 = 0;
|
||||||
|
const WIRE_PORT_OFFSET: u16 = 0;
|
||||||
|
const DEBUGLINK_PORT_OFFSET: u16 = 1;
|
||||||
|
const WEBAUTHN_PORT_OFFSET: u16 = 2;
|
||||||
|
const VCP_PORT_OFFSET: u16 = 3;
|
||||||
|
|
||||||
|
fn usb_config(serial_number: &CStr) -> UsbConfig {
|
||||||
|
UsbConfig {
|
||||||
|
vendor_id: 0x1209,
|
||||||
|
product_id: 0x53C1,
|
||||||
|
release_num: 0x0200,
|
||||||
|
manufacturer: cstr!("SatoshiLabs"),
|
||||||
|
product: cstr!("TREZOR"),
|
||||||
|
serial_number: set_global_serial_number(serial_number),
|
||||||
|
interface: cstr!("TREZOR Interface"),
|
||||||
|
usb21_landing: false,
|
||||||
|
..UsbConfig::default()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_wire_iface(ids: &mut RangeFrom<u8>) -> WebUsbConfig {
|
||||||
|
static mut WIRE_RX_BUFFER: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let id = ids.next().unwrap();
|
||||||
|
WebUsbConfig {
|
||||||
|
rx_buffer: unsafe { &mut WIRE_RX_BUFFER },
|
||||||
|
iface_num: id,
|
||||||
|
ep_in: 0x81 + id,
|
||||||
|
ep_out: 0x01 + id,
|
||||||
|
emu_port: UDP_PORT + WIRE_PORT_OFFSET,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
fn create_debug_iface(ids: &mut RangeFrom<u8>) -> WebUsbConfig {
|
||||||
|
static mut DEBUG_RX_BUFFER: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let id = ids.next().unwrap();
|
||||||
|
WebUsbConfig {
|
||||||
|
rx_buffer: unsafe { &mut DEBUG_RX_BUFFER },
|
||||||
|
iface_num: id,
|
||||||
|
ep_in: 0x81 + id,
|
||||||
|
ep_out: 0x01 + id,
|
||||||
|
emu_port: UDP_PORT + DEBUGLINK_PORT_OFFSET,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "bitcoin_only"))]
|
||||||
|
fn create_webauthn_iface(ids: &mut RangeFrom<u8>) -> HidConfig {
|
||||||
|
static mut WEBAUTHN_RX_BUFFER: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let id = ids.next().unwrap();
|
||||||
|
HidConfig {
|
||||||
|
report_desc: &[
|
||||||
|
0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance)
|
||||||
|
0x09, 0x01, // USAGE (U2F HID Authenticator Device)
|
||||||
|
0xa1, 0x01, // COLLECTION (Application)
|
||||||
|
0x09, 0x20, // USAGE (Input Report Data)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x95, 0x40, // REPORT_COUNT (64)
|
||||||
|
0x81, 0x02, // INPUT (Data,Var,Abs)
|
||||||
|
0x09, 0x21, // USAGE (Output Report Data)
|
||||||
|
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
||||||
|
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
||||||
|
0x75, 0x08, // REPORT_SIZE (8)
|
||||||
|
0x95, 0x40, // REPORT_COUNT (64)
|
||||||
|
0x91, 0x02, // OUTPUT (Data,Var,Abs)
|
||||||
|
0xc0, // END_COLLECTION
|
||||||
|
],
|
||||||
|
rx_buffer: unsafe { &mut WEBAUTHN_RX_BUFFER },
|
||||||
|
iface_num: id,
|
||||||
|
ep_in: 0x81 + id,
|
||||||
|
ep_out: 0x01 + id,
|
||||||
|
emu_port: UDP_PORT + WEBAUTHN_PORT_OFFSET,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
fn create_vcp_iface(ids: &mut RangeFrom<u8>) -> VcpConfig {
|
||||||
|
static mut VCP_RX_BUFFER: [u8; 1024] = [0; 1024];
|
||||||
|
static mut VCP_TX_BUFFER: [u8; 1024] = [0; 1024];
|
||||||
|
static mut VCP_RX_PACKET: [u8; 64] = [0; 64];
|
||||||
|
static mut VCP_TX_PACKET: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
let id = ids.next().unwrap();
|
||||||
|
let id_data = ids.next().unwrap();
|
||||||
|
VcpConfig {
|
||||||
|
rx_buffer: unsafe { &mut VCP_RX_BUFFER },
|
||||||
|
tx_buffer: unsafe { &mut VCP_TX_BUFFER },
|
||||||
|
rx_packet: unsafe { &mut VCP_RX_PACKET },
|
||||||
|
tx_packet: unsafe { &mut VCP_TX_PACKET },
|
||||||
|
rx_intr_fn: None, // Use pendsv_kbd_intr here.
|
||||||
|
rx_intr_byte: 3, // Ctrl-C
|
||||||
|
iface_num: id,
|
||||||
|
data_iface_num: id_data,
|
||||||
|
ep_in: 0x81 + id,
|
||||||
|
ep_out: 0x01 + id,
|
||||||
|
ep_cmd: 0x81 + id_data,
|
||||||
|
emu_port: UDP_PORT + VCP_PORT_OFFSET,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn set_global_serial_number(sn: &CStr) -> &'static CStr {
|
||||||
|
static mut GLOBAL_BUFFER: [u8; 64] = [0; 64];
|
||||||
|
|
||||||
|
// SAFETY: We are in a single threaded context, so the only possible race on
|
||||||
|
// `GLOBAL_BUFFER` is with the USB stack. We should take care to only call
|
||||||
|
// `set_global_serial_number` with the USB stopped.
|
||||||
|
unsafe {
|
||||||
|
let sn_nul = sn.to_bytes_with_nul();
|
||||||
|
|
||||||
|
// Panics if `sn_nul` is bigger then `GLOBAL_BUFFER`.
|
||||||
|
GLOBAL_BUFFER[..sn_nul.len()].copy_from_slice(sn_nul);
|
||||||
|
|
||||||
|
CStr::from_bytes_with_nul_unchecked(&GLOBAL_BUFFER[..sn_nul.len()])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let block = || {
|
||||||
|
let serial_number: Buffer = serial_number.try_into()?;
|
||||||
|
let serial_number =
|
||||||
|
CStr::from_bytes_with_nul(&serial_number).map_err(|_| Error::TypeError)?;
|
||||||
|
|
||||||
|
let mut ids = 0u8..; // Iterator of interface IDS.
|
||||||
|
let mut usb = usb_config(serial_number);
|
||||||
|
|
||||||
|
// Create the interfaces we use and add them to the config. Note that the order
|
||||||
|
// matters.
|
||||||
|
|
||||||
|
let wire_id = create_wire_iface(&mut ids).add(&mut usb)?.iface_num();
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
let debug_id = Some(create_debug_iface(&mut ids).add(&mut usb)?.iface_num());
|
||||||
|
#[cfg(not(feature = "ui_debug"))]
|
||||||
|
let debug_id: Option<u8> = None;
|
||||||
|
|
||||||
|
#[cfg(feature = "bitcoin_only")]
|
||||||
|
let webauthn_id: Option<u8> = None;
|
||||||
|
#[cfg(not(feature = "bitcoin_only"))]
|
||||||
|
let webauthn_id = Some(create_webauthn_iface(&mut ids).add(&mut usb)?.iface_num());
|
||||||
|
|
||||||
|
#[cfg(feature = "ui_debug")]
|
||||||
|
{
|
||||||
|
create_vcp_iface(&mut ids).add(&mut usb)?;
|
||||||
|
// TODO: Enable MicroPython VCP support.
|
||||||
|
}
|
||||||
|
|
||||||
|
// Convert used interface IDs to MicroPython objects.
|
||||||
|
let wire_id_obj = Obj::try_from(wire_id as u32)?;
|
||||||
|
let debug_id_obj = Obj::from(debug_id.map(Obj::try_from).transpose()?);
|
||||||
|
let webauthn_id_obj = Obj::from(webauthn_id.map(Obj::try_from).transpose()?);
|
||||||
|
let tuple = Obj::try_from((wire_id_obj, debug_id_obj, webauthn_id_obj))?;
|
||||||
|
|
||||||
|
// Initialize the USB and start the configured interfaces.
|
||||||
|
usb_open(usb)?;
|
||||||
|
|
||||||
|
Ok(tuple)
|
||||||
|
};
|
||||||
|
unsafe { util::try_or_raise(block) }
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<UsbError> for Error {
|
||||||
|
fn from(usb: UsbError) -> Self {
|
||||||
|
match usb {
|
||||||
|
UsbError::FailedToAddInterface => Error::ValueError(cstr!("Failed to add interface")),
|
||||||
|
UsbError::InterfaceNotFound => Error::ValueError(cstr!("Interface not found")),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -22,7 +22,7 @@ mod util;
|
|||||||
#[cfg(not(feature = "test"))]
|
#[cfg(not(feature = "test"))]
|
||||||
#[panic_handler]
|
#[panic_handler]
|
||||||
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
fn panic(_info: &core::panic::PanicInfo) -> ! {
|
||||||
use cstr_core::CStr;
|
use cstr_core::cstr;
|
||||||
|
|
||||||
// Although it would be ideal to use the original error message, ignoring it
|
// Although it would be ideal to use the original error message, ignoring it
|
||||||
// lets us avoid the `fmt` machinery and its code size and is also important for
|
// lets us avoid the `fmt` machinery and its code size and is also important for
|
||||||
@ -30,9 +30,8 @@ fn panic(_info: &core::panic::PanicInfo) -> ! {
|
|||||||
// should also avoid printing "panic" or "rust" on the user screen to avoid any
|
// should also avoid printing "panic" or "rust" on the user screen to avoid any
|
||||||
// confusion.
|
// confusion.
|
||||||
|
|
||||||
// SAFETY: Safe because we are passing in \0-terminated literals.
|
let empty = cstr!("");
|
||||||
let empty = unsafe { CStr::from_bytes_with_nul_unchecked(b"\0") };
|
let msg = cstr!("rs");
|
||||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"rs\0") };
|
|
||||||
|
|
||||||
// TODO: Ideally we would take the file and line info out of
|
// TODO: Ideally we would take the file and line info out of
|
||||||
// `PanicInfo::location()`.
|
// `PanicInfo::location()`.
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
|
#![allow(dead_code)]
|
||||||
#![allow(non_camel_case_types)]
|
#![allow(non_camel_case_types)]
|
||||||
#![allow(non_upper_case_globals)]
|
#![allow(non_upper_case_globals)]
|
||||||
#![allow(dead_code)]
|
#![allow(unsafe_op_in_unsafe_fn)]
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/micropython.rs"));
|
include!(concat!(env!("OUT_DIR"), "/micropython.rs"));
|
||||||
|
@ -305,6 +305,48 @@ impl TryFrom<(Obj, Obj)> for Obj {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl From<()> for Obj {
|
||||||
|
fn from(_value: ()) -> Self {
|
||||||
|
// micropython/py/obj.h
|
||||||
|
// #define mp_const_empty_tuple (MP_OBJ_FROM_PTR(&mp_const_empty_tuple_obj))
|
||||||
|
unsafe {
|
||||||
|
let empty_tuple_ptr = &ffi::mp_const_empty_tuple_obj as *const _ as *mut _;
|
||||||
|
Obj::from_ptr(empty_tuple_ptr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: WARNING: while rebasing I chose randomly to ignore this implementation
|
||||||
|
// and use the abovementioned
|
||||||
|
|
||||||
|
// impl TryFrom<(Obj, Obj)> for Obj {
|
||||||
|
// type Error = Error;
|
||||||
|
|
||||||
|
// fn try_from(value: (Obj, Obj)) -> Result<Self, Self::Error> {
|
||||||
|
// // SAFETY: `mp_obj_new_tuple` does not retain pointer to `items`,
|
||||||
|
// expects to // find `items.len()` elements and does not modify them.
|
||||||
|
// // EXCEPTION: Will raise if allocation fails.
|
||||||
|
// catch_exception(|| unsafe {
|
||||||
|
// let items = [value.0, value.1];
|
||||||
|
// ffi::mp_obj_new_tuple(items.len(), items.as_ptr())
|
||||||
|
// })
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
impl TryFrom<(Obj, Obj, Obj)> for Obj {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(value: (Obj, Obj, Obj)) -> Result<Self, Self::Error> {
|
||||||
|
// SAFETY: `mp_obj_new_tuple` does not retain pointer to `items`, expects to
|
||||||
|
// find `items.len()` elements and does not modify them.
|
||||||
|
// EXCEPTION: Will raise if allocation fails.
|
||||||
|
catch_exception(|| unsafe {
|
||||||
|
let items = [value.0, value.1, value.2];
|
||||||
|
ffi::mp_obj_new_tuple(items.len(), items.as_ptr())
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
// # Additional conversions based on the methods above.
|
// # Additional conversions based on the methods above.
|
||||||
//
|
//
|
||||||
|
@ -1,31 +1,30 @@
|
|||||||
use cstr_core::CStr;
|
use cstr_core::cstr;
|
||||||
|
|
||||||
use crate::{error::Error, micropython::qstr::Qstr};
|
use crate::{error::Error, micropython::qstr::Qstr};
|
||||||
|
|
||||||
// XXX const version of `from_bytes_with_nul_unchecked` is nightly-only.
|
// XXX const version of `from_bytes_with_nul_unchecked` is nightly-only.
|
||||||
|
|
||||||
pub fn experimental_not_enabled() -> Error {
|
pub fn experimental_not_enabled() -> Error {
|
||||||
let msg =
|
let msg = cstr!("Experimental features are disabled");
|
||||||
unsafe { CStr::from_bytes_with_nul_unchecked(b"Experimental features are disabled.\0") };
|
|
||||||
Error::ValueError(msg)
|
Error::ValueError(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn unknown_field_type() -> Error {
|
pub fn unknown_field_type() -> Error {
|
||||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"Unknown field type.\0") };
|
let msg = cstr!("Unknown field type");
|
||||||
Error::ValueError(msg)
|
Error::ValueError(msg)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn missing_required_field(field: Qstr) -> Error {
|
pub fn missing_required_field(field: Qstr) -> Error {
|
||||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"Missing required field\0") };
|
let msg = cstr!("Missing required field");
|
||||||
Error::ValueErrorParam(msg, field.into())
|
Error::ValueErrorParam(msg, field.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalid_value(field: Qstr) -> Error {
|
pub fn invalid_value(field: Qstr) -> Error {
|
||||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"Invalid value for field\0") };
|
let msg = cstr!("Invalid value for field");
|
||||||
Error::ValueErrorParam(msg, field.into())
|
Error::ValueErrorParam(msg, field.into())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn end_of_buffer() -> Error {
|
pub fn end_of_buffer() -> Error {
|
||||||
let msg = unsafe { CStr::from_bytes_with_nul_unchecked(b"End of buffer.\0") };
|
let msg = cstr!("End of buffer");
|
||||||
Error::ValueError(msg)
|
Error::ValueError(msg)
|
||||||
}
|
}
|
||||||
|
@ -107,19 +107,19 @@ pub fn usb_close() {
|
|||||||
const MAX_INTERFACE_COUNT: usize = 4;
|
const MAX_INTERFACE_COUNT: usize = 4;
|
||||||
|
|
||||||
#[derive(Default)]
|
#[derive(Default)]
|
||||||
pub struct UsbConfig<'a> {
|
pub struct UsbConfig {
|
||||||
pub vendor_id: u16,
|
pub vendor_id: u16,
|
||||||
pub product_id: u16,
|
pub product_id: u16,
|
||||||
pub release_num: u16,
|
pub release_num: u16,
|
||||||
pub manufacturer: &'static CStr,
|
pub manufacturer: &'static CStr,
|
||||||
pub product: &'static CStr,
|
pub product: &'static CStr,
|
||||||
pub interface: &'static CStr,
|
pub interface: &'static CStr,
|
||||||
pub serial_number: &'a CStr,
|
pub serial_number: &'static CStr,
|
||||||
pub usb21_landing: bool,
|
pub usb21_landing: bool,
|
||||||
pub interfaces: Vec<IfaceConfig, MAX_INTERFACE_COUNT>,
|
pub interfaces: Vec<IfaceConfig, MAX_INTERFACE_COUNT>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UsbConfig<'_> {
|
impl UsbConfig {
|
||||||
fn as_dev_info(&self) -> ffi::usb_dev_info_t {
|
fn as_dev_info(&self) -> ffi::usb_dev_info_t {
|
||||||
ffi::usb_dev_info_t {
|
ffi::usb_dev_info_t {
|
||||||
device_class: 0,
|
device_class: 0,
|
||||||
@ -131,8 +131,7 @@ impl UsbConfig<'_> {
|
|||||||
manufacturer: self.manufacturer.as_ptr(),
|
manufacturer: self.manufacturer.as_ptr(),
|
||||||
product: self.product.as_ptr(),
|
product: self.product.as_ptr(),
|
||||||
interface: self.interface.as_ptr(),
|
interface: self.interface.as_ptr(),
|
||||||
// Because we set the SN dynamically, we need to copy it to the static storage first.
|
serial_number: self.serial_number.as_ptr(),
|
||||||
serial_number: set_global_serial_number(self.serial_number).as_ptr(),
|
|
||||||
usb21_enabled: secbool::TRUE,
|
usb21_enabled: secbool::TRUE,
|
||||||
usb21_landing: if self.usb21_landing {
|
usb21_landing: if self.usb21_landing {
|
||||||
secbool::TRUE
|
secbool::TRUE
|
||||||
@ -151,6 +150,15 @@ pub enum IfaceTicket {
|
|||||||
WebUsb(u8),
|
WebUsb(u8),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IfaceTicket {
|
||||||
|
pub fn iface_num(&self) -> u8 {
|
||||||
|
match self {
|
||||||
|
IfaceTicket::Hid(iface_num) => *iface_num,
|
||||||
|
IfaceTicket::WebUsb(iface_num) => *iface_num,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub enum IfaceConfig {
|
pub enum IfaceConfig {
|
||||||
WebUsb(WebUsbConfig),
|
WebUsb(WebUsbConfig),
|
||||||
Hid(HidConfig),
|
Hid(HidConfig),
|
||||||
@ -383,130 +391,3 @@ impl VcpConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod tests {
|
|
||||||
use cstr_core::cstr;
|
|
||||||
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn test_usb() {
|
|
||||||
// Example port of usb.py.
|
|
||||||
|
|
||||||
const UDP_PORT: u16 = 0;
|
|
||||||
const WIRE_PORT_OFFSET: u16 = 0;
|
|
||||||
const DEBUGLINK_PORT_OFFSET: u16 = 1;
|
|
||||||
const WEBAUTHN_PORT_OFFSET: u16 = 2;
|
|
||||||
const VCP_PORT_OFFSET: u16 = 3;
|
|
||||||
|
|
||||||
let mut iface_iter = 0u8..;
|
|
||||||
|
|
||||||
const ENABLE_IFACE_DEBUG: bool = true;
|
|
||||||
const ENABLE_IFACE_WEBAUTHN: bool = true;
|
|
||||||
const ENABLE_IFACE_VCP: bool = true;
|
|
||||||
|
|
||||||
let mut config = UsbConfig {
|
|
||||||
vendor_id: 0x1209,
|
|
||||||
product_id: 0x53C1,
|
|
||||||
release_num: 0x0200,
|
|
||||||
manufacturer: cstr!("SatoshiLabs"),
|
|
||||||
product: cstr!("TREZOR"),
|
|
||||||
serial_number: cstr!(""),
|
|
||||||
interface: cstr!("TREZOR Interface"),
|
|
||||||
usb21_landing: false,
|
|
||||||
..UsbConfig::default()
|
|
||||||
};
|
|
||||||
|
|
||||||
let id_wire = iface_iter.next().unwrap();
|
|
||||||
let iface_wire = WebUsbConfig {
|
|
||||||
rx_buffer: &mut [0; 64],
|
|
||||||
iface_num: id_wire,
|
|
||||||
ep_in: 0x81 + id_wire,
|
|
||||||
ep_out: 0x01 + id_wire,
|
|
||||||
emu_port: UDP_PORT + WIRE_PORT_OFFSET,
|
|
||||||
};
|
|
||||||
let ticket_wire = iface_wire.add(&mut config).unwrap();
|
|
||||||
|
|
||||||
if ENABLE_IFACE_DEBUG {
|
|
||||||
let id_debug = iface_iter.next().unwrap();
|
|
||||||
let iface_debug = WebUsbConfig {
|
|
||||||
rx_buffer: &mut [0; 64],
|
|
||||||
iface_num: id_debug,
|
|
||||||
ep_in: 0x81 + id_debug,
|
|
||||||
ep_out: 0x01 + id_debug,
|
|
||||||
emu_port: UDP_PORT + DEBUGLINK_PORT_OFFSET,
|
|
||||||
};
|
|
||||||
let ticket_debug = iface_debug.add(&mut config).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ENABLE_IFACE_WEBAUTHN {
|
|
||||||
let id_webauthn = iface_iter.next().unwrap();
|
|
||||||
let iface_webauthn = HidConfig {
|
|
||||||
report_desc: &[
|
|
||||||
0x06, 0xd0, 0xf1, // USAGE_PAGE (FIDO Alliance)
|
|
||||||
0x09, 0x01, // USAGE (U2F HID Authenticator Device)
|
|
||||||
0xa1, 0x01, // COLLECTION (Application)
|
|
||||||
0x09, 0x20, // USAGE (Input Report Data)
|
|
||||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
|
||||||
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
|
||||||
0x75, 0x08, // REPORT_SIZE (8)
|
|
||||||
0x95, 0x40, // REPORT_COUNT (64)
|
|
||||||
0x81, 0x02, // INPUT (Data,Var,Abs)
|
|
||||||
0x09, 0x21, // USAGE (Output Report Data)
|
|
||||||
0x15, 0x00, // LOGICAL_MINIMUM (0)
|
|
||||||
0x26, 0xff, 0x00, // LOGICAL_MAXIMUM (255)
|
|
||||||
0x75, 0x08, // REPORT_SIZE (8)
|
|
||||||
0x95, 0x40, // REPORT_COUNT (64)
|
|
||||||
0x91, 0x02, // OUTPUT (Data,Var,Abs)
|
|
||||||
0xc0, // END_COLLECTION
|
|
||||||
],
|
|
||||||
rx_buffer: &mut [0; 64],
|
|
||||||
iface_num: id_webauthn,
|
|
||||||
ep_in: 0x81 + id_webauthn,
|
|
||||||
ep_out: 0x01 + id_webauthn,
|
|
||||||
emu_port: UDP_PORT + WEBAUTHN_PORT_OFFSET,
|
|
||||||
};
|
|
||||||
let ticket_webauthn = iface_webauthn.add(&mut config).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ENABLE_IFACE_VCP {
|
|
||||||
let id_vcp = iface_iter.next().unwrap();
|
|
||||||
let id_vcp_data = iface_iter.next().unwrap();
|
|
||||||
let iface_vcp = VcpConfig {
|
|
||||||
rx_buffer: &mut [0; 1024],
|
|
||||||
tx_buffer: &mut [0; 1024],
|
|
||||||
rx_packet: &mut [0; 64],
|
|
||||||
tx_packet: &mut [0; 64],
|
|
||||||
rx_intr_fn: None, // Use pendsv_kbd_intr here.
|
|
||||||
rx_intr_byte: 3, // Ctrl-C
|
|
||||||
iface_num: id_vcp,
|
|
||||||
data_iface_num: id_vcp_data,
|
|
||||||
ep_in: 0x81 + id_vcp,
|
|
||||||
ep_out: 0x01 + id_vcp,
|
|
||||||
ep_cmd: 0x81 + id_vcp_data,
|
|
||||||
emu_port: UDP_PORT + VCP_PORT_OFFSET,
|
|
||||||
};
|
|
||||||
iface_vcp.add(&mut config).unwrap();
|
|
||||||
}
|
|
||||||
|
|
||||||
usb_open(config).unwrap();
|
|
||||||
usb_close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn set_global_serial_number(sn: &CStr) -> &'static CStr {
|
|
||||||
static mut GLOBAL_BUFFER: [u8; 64] = [0; 64];
|
|
||||||
|
|
||||||
// SAFETY: We are in a single threaded context, so the only possible race on
|
|
||||||
// `GLOBAL_BUFFER` is with the USB stack. We should take care to only call
|
|
||||||
// `set_global_serial_number` with the USB stopped.
|
|
||||||
unsafe {
|
|
||||||
let sn_nul = sn.to_bytes_with_nul();
|
|
||||||
|
|
||||||
// Panics if `sn_nul` is bigger then `GLOBAL_BUFFER`.
|
|
||||||
GLOBAL_BUFFER[..sn_nul.len()].copy_from_slice(sn_nul);
|
|
||||||
|
|
||||||
CStr::from_bytes_with_nul_unchecked(&GLOBAL_BUFFER[..sn_nul.len()])
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -1,14 +1,14 @@
|
|||||||
use core::slice;
|
use core::slice;
|
||||||
|
|
||||||
use crate::micropython::time;
|
|
||||||
use crate::time::Duration;
|
|
||||||
use crate::{
|
use crate::{
|
||||||
error::Error,
|
error::Error,
|
||||||
micropython::{
|
micropython::{
|
||||||
map::{Map, MapElem},
|
map::{Map, MapElem},
|
||||||
obj::Obj,
|
obj::Obj,
|
||||||
runtime::raise_exception,
|
runtime::raise_exception,
|
||||||
|
time,
|
||||||
},
|
},
|
||||||
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Wait an unspecified short amount of time. To be used in busy loops.
|
/// Wait an unspecified short amount of time. To be used in busy loops.
|
||||||
|
Loading…
Reference in New Issue
Block a user