mirror of
https://github.com/trezor/trezor-firmware.git
synced 2025-02-19 11:02:02 +00:00
trezorhal: refactor trezor.io USB stuff into more files, add WebUSB
This commit is contained in:
parent
ba1a313016
commit
8bf19b4c92
108
embed/extmod/modtrezorio/modtrezorio-hid.h
Normal file
108
embed/extmod/modtrezorio/modtrezorio-hid.h
Normal file
@ -0,0 +1,108 @@
|
|||||||
|
/// class HID:
|
||||||
|
/// '''
|
||||||
|
/// USB HID interface configuration.
|
||||||
|
/// '''
|
||||||
|
typedef struct _mp_obj_HID_t {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
usb_hid_info_t info;
|
||||||
|
} mp_obj_HID_t;
|
||||||
|
|
||||||
|
/// def __init__(self,
|
||||||
|
/// iface_num: int,
|
||||||
|
/// ep_in: int,
|
||||||
|
/// ep_out: int,
|
||||||
|
/// report_desc: bytes,
|
||||||
|
/// subclass: int = 0,
|
||||||
|
/// protocol: int = 0,
|
||||||
|
/// polling_interval: int = 1,
|
||||||
|
/// max_packet_len: int = 64) -> None:
|
||||||
|
/// '''
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_HID_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||||
|
|
||||||
|
STATIC const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_in, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_out, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_subclass, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_protocol, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_polling_interval, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
||||||
|
{ MP_QSTR_max_packet_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
|
||||||
|
{ MP_QSTR_report_desc, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
||||||
|
};
|
||||||
|
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
||||||
|
|
||||||
|
const mp_int_t iface_num = vals[0].u_int;
|
||||||
|
const mp_int_t ep_in = vals[1].u_int;
|
||||||
|
const mp_int_t ep_out = vals[2].u_int;
|
||||||
|
const mp_int_t subclass = vals[3].u_int;
|
||||||
|
const mp_int_t protocol = vals[4].u_int;
|
||||||
|
const mp_int_t polling_interval = vals[5].u_int;
|
||||||
|
const mp_int_t max_packet_len = vals[6].u_int;
|
||||||
|
mp_buffer_info_t report_desc;
|
||||||
|
mp_get_buffer_raise(vals[7].u_obj, &report_desc, MP_BUFFER_READ);
|
||||||
|
|
||||||
|
if (report_desc.buf == NULL || report_desc.len == 0 || report_desc.len > 255) {
|
||||||
|
mp_raise_ValueError("report_desc is invalid");
|
||||||
|
}
|
||||||
|
CHECK_PARAM_RANGE(iface_num, 0, 32)
|
||||||
|
CHECK_PARAM_RANGE(ep_in, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(ep_out, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(subclass, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(protocol, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(polling_interval, 1, 255)
|
||||||
|
CHECK_PARAM_RANGE(max_packet_len, 64, 64)
|
||||||
|
|
||||||
|
mp_obj_HID_t *o = m_new_obj(mp_obj_HID_t);
|
||||||
|
o->base.type = type;
|
||||||
|
|
||||||
|
o->info.rx_buffer = m_new(uint8_t, max_packet_len);
|
||||||
|
o->info.report_desc = report_desc.buf;
|
||||||
|
o->info.iface_num = (uint8_t)(iface_num);
|
||||||
|
o->info.ep_in = (uint8_t)(ep_in);
|
||||||
|
o->info.ep_out = (uint8_t)(ep_out);
|
||||||
|
o->info.subclass = (uint8_t)(subclass);
|
||||||
|
o->info.protocol = (uint8_t)(protocol);
|
||||||
|
o->info.polling_interval = (uint8_t)(polling_interval);
|
||||||
|
o->info.max_packet_len = (uint8_t)(max_packet_len);
|
||||||
|
o->info.report_desc_len = (uint8_t)(report_desc.len);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def iface_num(self) -> int:
|
||||||
|
/// '''
|
||||||
|
/// Returns the configured number of this interface.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_HID_iface_num(mp_obj_t self) {
|
||||||
|
mp_obj_HID_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
return MP_OBJ_NEW_SMALL_INT(o->info.iface_num);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_HID_iface_num_obj, mod_trezorio_HID_iface_num);
|
||||||
|
|
||||||
|
/// def write(self, msg: bytes) -> int:
|
||||||
|
/// '''
|
||||||
|
/// Sends message using USB HID (device) or UDP (emulator).
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_HID_write(mp_obj_t self, mp_obj_t msg) {
|
||||||
|
mp_obj_HID_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
mp_buffer_info_t buf;
|
||||||
|
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
|
||||||
|
ssize_t r = usb_hid_write(o->info.iface_num, buf.buf, buf.len);
|
||||||
|
return MP_OBJ_NEW_SMALL_INT(r);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_HID_write_obj, mod_trezorio_HID_write);
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t mod_trezorio_HID_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_iface_num), MP_ROM_PTR(&mod_trezorio_HID_iface_num_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_trezorio_HID_write_obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_HID_locals_dict, mod_trezorio_HID_locals_dict_table);
|
||||||
|
|
||||||
|
STATIC const mp_obj_type_t mod_trezorio_HID_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_HID,
|
||||||
|
.make_new = mod_trezorio_HID_make_new,
|
||||||
|
.locals_dict = (void*)&mod_trezorio_HID_locals_dict,
|
||||||
|
};
|
@ -1,519 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) Pavol Rusnak, SatoshiLabs
|
|
||||||
*
|
|
||||||
* Licensed under TREZOR License
|
|
||||||
* see LICENSE file for details
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string.h>
|
|
||||||
#include <unistd.h>
|
|
||||||
|
|
||||||
#include "usb.h"
|
|
||||||
#include "touch.h"
|
|
||||||
|
|
||||||
void pendsv_kbd_intr(void);
|
|
||||||
void mp_hal_set_vcp_iface(int iface_num);
|
|
||||||
|
|
||||||
#define TOUCH_IFACE (255)
|
|
||||||
#define POLL_READ (0x0000)
|
|
||||||
#define POLL_WRITE (0x0100)
|
|
||||||
|
|
||||||
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
|
|
||||||
if (value < minimum || value > maximum) { \
|
|
||||||
mp_raise_ValueError(#value " is out of range"); \
|
|
||||||
}
|
|
||||||
|
|
||||||
/// def poll(ifaces: Iterable[int], list_ref: List, timeout_us: int) -> bool:
|
|
||||||
/// '''
|
|
||||||
/// Wait until one of `ifaces` is ready to read or write (using masks
|
|
||||||
// `io.POLL_READ` and `io.POLL_WRITE`) and assign the result into
|
|
||||||
/// `list_ref`:
|
|
||||||
///
|
|
||||||
/// `list_ref[0]` - the interface number, including the mask
|
|
||||||
/// `list_ref[1]` - for touch event, tuple of (event_type, x_position, y_position)
|
|
||||||
/// - for HID read event, received bytes
|
|
||||||
///
|
|
||||||
/// If timeout occurs, False is returned, True otherwise.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, mp_obj_t timeout_us) {
|
|
||||||
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
|
|
||||||
if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 2) {
|
|
||||||
mp_raise_TypeError("invalid list_ref");
|
|
||||||
}
|
|
||||||
|
|
||||||
const mp_uint_t timeout = mp_obj_get_int(timeout_us);
|
|
||||||
const mp_uint_t deadline = mp_hal_ticks_us() + timeout;
|
|
||||||
mp_obj_iter_buf_t iterbuf;
|
|
||||||
|
|
||||||
for (;;) {
|
|
||||||
mp_obj_t iter = mp_getiter(ifaces, &iterbuf);
|
|
||||||
mp_obj_t item;
|
|
||||||
while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
|
|
||||||
const mp_uint_t i = mp_obj_int_get_truncated(item);
|
|
||||||
const mp_uint_t iface = i & 0x00FF;
|
|
||||||
const mp_uint_t mode = i & 0xFF00;
|
|
||||||
|
|
||||||
if (iface == TOUCH_IFACE) {
|
|
||||||
const uint32_t evt = touch_read();
|
|
||||||
if (evt) {
|
|
||||||
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
|
|
||||||
tuple->items[0] = MP_OBJ_NEW_SMALL_INT((evt >> 24) & 0xFFU); // event type
|
|
||||||
tuple->items[1] = MP_OBJ_NEW_SMALL_INT((evt >> 12) & 0xFFFU); // x position
|
|
||||||
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(evt & 0xFFFU); // y position
|
|
||||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
|
||||||
ret->items[1] = MP_OBJ_FROM_PTR(tuple);
|
|
||||||
return mp_const_true;
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
if (mode == POLL_READ) {
|
|
||||||
if (sectrue == usb_hid_can_read(iface)) {
|
|
||||||
uint8_t buf[64];
|
|
||||||
int len = usb_hid_read(iface, buf, sizeof(buf));
|
|
||||||
if (len > 0) {
|
|
||||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
|
||||||
ret->items[1] = mp_obj_new_bytes(buf, len);
|
|
||||||
return mp_const_true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
if (mode == POLL_WRITE) {
|
|
||||||
if (sectrue == usb_hid_can_write(iface)) {
|
|
||||||
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
|
||||||
ret->items[1] = mp_const_none;
|
|
||||||
return mp_const_true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mp_hal_ticks_us() >= deadline) {
|
|
||||||
break;
|
|
||||||
} else {
|
|
||||||
MICROPY_EVENT_POLL_HOOK
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return mp_const_false;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_poll_obj, mod_trezorio_poll);
|
|
||||||
|
|
||||||
/// class HID:
|
|
||||||
/// '''
|
|
||||||
/// USB HID interface configuration.
|
|
||||||
/// '''
|
|
||||||
typedef struct _mp_obj_HID_t {
|
|
||||||
mp_obj_base_t base;
|
|
||||||
usb_hid_info_t info;
|
|
||||||
} mp_obj_HID_t;
|
|
||||||
|
|
||||||
/// def __init__(self,
|
|
||||||
/// iface_num: int,
|
|
||||||
/// ep_in: int,
|
|
||||||
/// ep_out: int,
|
|
||||||
/// report_desc: bytes,
|
|
||||||
/// subclass: int = 0,
|
|
||||||
/// protocol: int = 0,
|
|
||||||
/// polling_interval: int = 1,
|
|
||||||
/// max_packet_len: int = 64) -> None:
|
|
||||||
/// '''
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_HID_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
||||||
|
|
||||||
STATIC const mp_arg_t allowed_args[] = {
|
|
||||||
{ MP_QSTR_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_ep_in, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_ep_out, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_subclass, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_protocol, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_polling_interval, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
|
||||||
{ MP_QSTR_max_packet_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
|
|
||||||
{ MP_QSTR_report_desc, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = MP_OBJ_NULL} },
|
|
||||||
};
|
|
||||||
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
|
||||||
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
|
||||||
|
|
||||||
const mp_int_t iface_num = vals[0].u_int;
|
|
||||||
const mp_int_t ep_in = vals[1].u_int;
|
|
||||||
const mp_int_t ep_out = vals[2].u_int;
|
|
||||||
const mp_int_t subclass = vals[3].u_int;
|
|
||||||
const mp_int_t protocol = vals[4].u_int;
|
|
||||||
const mp_int_t polling_interval = vals[5].u_int;
|
|
||||||
const mp_int_t max_packet_len = vals[6].u_int;
|
|
||||||
mp_buffer_info_t report_desc;
|
|
||||||
mp_get_buffer_raise(vals[7].u_obj, &report_desc, MP_BUFFER_READ);
|
|
||||||
|
|
||||||
if (report_desc.buf == NULL || report_desc.len == 0 || report_desc.len > 255) {
|
|
||||||
mp_raise_ValueError("report_desc is invalid");
|
|
||||||
}
|
|
||||||
CHECK_PARAM_RANGE(iface_num, 0, 32)
|
|
||||||
CHECK_PARAM_RANGE(ep_in, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(ep_out, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(subclass, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(protocol, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(polling_interval, 1, 255)
|
|
||||||
CHECK_PARAM_RANGE(max_packet_len, 64, 64)
|
|
||||||
|
|
||||||
mp_obj_HID_t *o = m_new_obj(mp_obj_HID_t);
|
|
||||||
o->base.type = type;
|
|
||||||
|
|
||||||
o->info.rx_buffer = m_new(uint8_t, max_packet_len);
|
|
||||||
o->info.report_desc = report_desc.buf;
|
|
||||||
o->info.iface_num = (uint8_t)(iface_num);
|
|
||||||
o->info.ep_in = (uint8_t)(ep_in);
|
|
||||||
o->info.ep_out = (uint8_t)(ep_out);
|
|
||||||
o->info.subclass = (uint8_t)(subclass);
|
|
||||||
o->info.protocol = (uint8_t)(protocol);
|
|
||||||
o->info.polling_interval = (uint8_t)(polling_interval);
|
|
||||||
o->info.max_packet_len = (uint8_t)(max_packet_len);
|
|
||||||
o->info.report_desc_len = (uint8_t)(report_desc.len);
|
|
||||||
|
|
||||||
return MP_OBJ_FROM_PTR(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// def iface_num(self) -> int:
|
|
||||||
/// '''
|
|
||||||
/// Returns the configured number of this interface.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_HID_iface_num(mp_obj_t self) {
|
|
||||||
mp_obj_HID_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
return MP_OBJ_NEW_SMALL_INT(o->info.iface_num);
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_HID_iface_num_obj, mod_trezorio_HID_iface_num);
|
|
||||||
|
|
||||||
/// def write(self, msg: bytes) -> int:
|
|
||||||
/// '''
|
|
||||||
/// Sends message using USB HID (device) or UDP (emulator).
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_HID_write(mp_obj_t self, mp_obj_t msg) {
|
|
||||||
mp_obj_HID_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
mp_buffer_info_t buf;
|
|
||||||
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
|
|
||||||
ssize_t r = usb_hid_write(o->info.iface_num, buf.buf, buf.len);
|
|
||||||
return MP_OBJ_NEW_SMALL_INT(r);
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_HID_write_obj, mod_trezorio_HID_write);
|
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mod_trezorio_HID_locals_dict_table[] = {
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_iface_num), MP_ROM_PTR(&mod_trezorio_HID_iface_num_obj) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_trezorio_HID_write_obj) },
|
|
||||||
};
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_HID_locals_dict, mod_trezorio_HID_locals_dict_table);
|
|
||||||
|
|
||||||
STATIC const mp_obj_type_t mod_trezorio_HID_type = {
|
|
||||||
{ &mp_type_type },
|
|
||||||
.name = MP_QSTR_HID,
|
|
||||||
.make_new = mod_trezorio_HID_make_new,
|
|
||||||
.locals_dict = (void*)&mod_trezorio_HID_locals_dict,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// class VCP:
|
|
||||||
/// '''
|
|
||||||
/// USB VCP interface configuration.
|
|
||||||
/// '''
|
|
||||||
typedef struct _mp_obj_VCP_t {
|
|
||||||
mp_obj_base_t base;
|
|
||||||
usb_vcp_info_t info;
|
|
||||||
} mp_obj_VCP_t;
|
|
||||||
|
|
||||||
/// def __init__(self,
|
|
||||||
/// iface_num: int,
|
|
||||||
/// data_iface_num: int,
|
|
||||||
/// ep_in: int,
|
|
||||||
/// ep_out: int,
|
|
||||||
/// ep_cmd: int) -> None:
|
|
||||||
/// '''
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_VCP_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
||||||
|
|
||||||
STATIC const mp_arg_t allowed_args[] = {
|
|
||||||
{ MP_QSTR_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_data_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_ep_in, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_ep_out, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_ep_cmd, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
};
|
|
||||||
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
|
||||||
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
|
||||||
|
|
||||||
const mp_int_t iface_num = vals[0].u_int;
|
|
||||||
const mp_int_t data_iface_num = vals[1].u_int;
|
|
||||||
const mp_int_t ep_in = vals[2].u_int;
|
|
||||||
const mp_int_t ep_out = vals[3].u_int;
|
|
||||||
const mp_int_t ep_cmd = vals[4].u_int;
|
|
||||||
|
|
||||||
CHECK_PARAM_RANGE(iface_num, 0, 32)
|
|
||||||
CHECK_PARAM_RANGE(data_iface_num, 0, 32)
|
|
||||||
CHECK_PARAM_RANGE(ep_in, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(ep_out, 0, 255)
|
|
||||||
CHECK_PARAM_RANGE(ep_cmd, 0, 255)
|
|
||||||
|
|
||||||
const size_t vcp_buffer_len = 1024;
|
|
||||||
const size_t vcp_packet_len = 64;
|
|
||||||
|
|
||||||
mp_obj_VCP_t *o = m_new_obj(mp_obj_VCP_t);
|
|
||||||
o->base.type = type;
|
|
||||||
|
|
||||||
o->info.tx_packet = m_new(uint8_t, vcp_packet_len);
|
|
||||||
o->info.tx_buffer = m_new(uint8_t, vcp_buffer_len);
|
|
||||||
o->info.rx_packet = m_new(uint8_t, vcp_packet_len);
|
|
||||||
o->info.rx_buffer = m_new(uint8_t, vcp_buffer_len);
|
|
||||||
o->info.tx_buffer_len = vcp_buffer_len;
|
|
||||||
o->info.rx_buffer_len = vcp_buffer_len;
|
|
||||||
o->info.rx_intr_fn = pendsv_kbd_intr;
|
|
||||||
o->info.rx_intr_byte = 3; // Ctrl-C
|
|
||||||
o->info.iface_num = (uint8_t)(iface_num);
|
|
||||||
o->info.data_iface_num = (uint8_t)(data_iface_num);
|
|
||||||
o->info.ep_cmd = (uint8_t)(ep_cmd);
|
|
||||||
o->info.ep_in = (uint8_t)(ep_in);
|
|
||||||
o->info.ep_out = (uint8_t)(ep_out);
|
|
||||||
o->info.polling_interval = 10;
|
|
||||||
o->info.max_packet_len = (uint8_t)(vcp_packet_len);
|
|
||||||
|
|
||||||
return MP_OBJ_FROM_PTR(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// def iface_num(self) -> int:
|
|
||||||
/// '''
|
|
||||||
/// Returns the configured number of this interface.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_VCP_iface_num(mp_obj_t self) {
|
|
||||||
mp_obj_VCP_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
return MP_OBJ_NEW_SMALL_INT(o->info.iface_num);
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_VCP_iface_num_obj, mod_trezorio_VCP_iface_num);
|
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mod_trezorio_VCP_locals_dict_table[] = {
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_iface_num), MP_ROM_PTR(&mod_trezorio_VCP_iface_num_obj) },
|
|
||||||
};
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_VCP_locals_dict, mod_trezorio_VCP_locals_dict_table);
|
|
||||||
|
|
||||||
STATIC const mp_obj_type_t mod_trezorio_VCP_type = {
|
|
||||||
{ &mp_type_type },
|
|
||||||
.name = MP_QSTR_VCP,
|
|
||||||
.make_new = mod_trezorio_VCP_make_new,
|
|
||||||
.locals_dict = (void*)&mod_trezorio_VCP_locals_dict,
|
|
||||||
};
|
|
||||||
|
|
||||||
enum {
|
|
||||||
USB_CLOSED = 0,
|
|
||||||
USB_OPENED = 1,
|
|
||||||
};
|
|
||||||
|
|
||||||
/// class USB:
|
|
||||||
/// '''
|
|
||||||
/// USB device configuration.
|
|
||||||
/// '''
|
|
||||||
typedef struct _mp_obj_USB_t {
|
|
||||||
mp_obj_base_t base;
|
|
||||||
mp_obj_list_t ifaces;
|
|
||||||
usb_dev_info_t info;
|
|
||||||
mp_int_t state;
|
|
||||||
} mp_obj_USB_t;
|
|
||||||
|
|
||||||
static const char *get_0str(mp_obj_t o, size_t min_len, size_t max_len) {
|
|
||||||
size_t len;
|
|
||||||
const char *s = mp_obj_str_get_data(o, &len);
|
|
||||||
if ((len >= min_len) && (len <= max_len)) {
|
|
||||||
if (len == 0 && s == NULL) {
|
|
||||||
return "";
|
|
||||||
} else {
|
|
||||||
return s;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// def __init__(self,
|
|
||||||
/// vendor_id: int,
|
|
||||||
/// product_id: int,
|
|
||||||
/// release_num: int,
|
|
||||||
/// manufacturer: str='',
|
|
||||||
/// product: str='',
|
|
||||||
/// serial_number: str='',
|
|
||||||
/// configuration: str='',
|
|
||||||
/// interface: str='') -> None:
|
|
||||||
/// '''
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_USB_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
|
||||||
|
|
||||||
STATIC const mp_arg_t allowed_args[] = {
|
|
||||||
{ MP_QSTR_vendor_id, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_product_id, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_release_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
|
||||||
{ MP_QSTR_manufacturer, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
|
||||||
{ MP_QSTR_product, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
|
||||||
{ MP_QSTR_serial_number, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
|
||||||
{ MP_QSTR_configuration, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
|
||||||
{ MP_QSTR_interface, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
|
||||||
};
|
|
||||||
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
|
||||||
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
|
||||||
|
|
||||||
const mp_int_t vendor_id = vals[0].u_int;
|
|
||||||
const mp_int_t product_id = vals[1].u_int;
|
|
||||||
const mp_int_t release_num = vals[2].u_int;
|
|
||||||
const char *manufacturer = get_0str(vals[3].u_obj, 0, 32);
|
|
||||||
const char *product = get_0str(vals[4].u_obj, 0, 32);
|
|
||||||
const char *serial_number = get_0str(vals[5].u_obj, 0, 32);
|
|
||||||
const char *configuration = get_0str(vals[6].u_obj, 0, 32);
|
|
||||||
const char *interface = get_0str(vals[7].u_obj, 0, 32);
|
|
||||||
|
|
||||||
CHECK_PARAM_RANGE(vendor_id, 0, 65535)
|
|
||||||
CHECK_PARAM_RANGE(product_id, 0, 65535)
|
|
||||||
CHECK_PARAM_RANGE(release_num, 0, 65535)
|
|
||||||
if (manufacturer == NULL) {
|
|
||||||
mp_raise_ValueError("manufacturer is invalid");
|
|
||||||
}
|
|
||||||
if (product == NULL) {
|
|
||||||
mp_raise_ValueError("product is invalid");
|
|
||||||
}
|
|
||||||
if (serial_number == NULL) {
|
|
||||||
mp_raise_ValueError("serial_number is invalid");
|
|
||||||
}
|
|
||||||
if (configuration == NULL) {
|
|
||||||
mp_raise_ValueError("configuration is invalid");
|
|
||||||
}
|
|
||||||
if (interface == NULL) {
|
|
||||||
mp_raise_ValueError("interface is invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
mp_obj_USB_t *o = m_new_obj(mp_obj_USB_t);
|
|
||||||
o->base.type = type;
|
|
||||||
|
|
||||||
o->state = USB_CLOSED;
|
|
||||||
|
|
||||||
o->info.vendor_id = (uint16_t)(vendor_id);
|
|
||||||
o->info.product_id = (uint16_t)(product_id);
|
|
||||||
o->info.release_num = (uint16_t)(release_num);
|
|
||||||
o->info.manufacturer = (const uint8_t *)(manufacturer);
|
|
||||||
o->info.product = (const uint8_t *)(product);
|
|
||||||
o->info.serial_number = (const uint8_t *)(serial_number);
|
|
||||||
o->info.configuration = (const uint8_t *)(configuration);
|
|
||||||
o->info.interface = (const uint8_t *)(interface);
|
|
||||||
mp_obj_list_init(&o->ifaces, 0);
|
|
||||||
|
|
||||||
return MP_OBJ_FROM_PTR(o);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// def add(self, iface: Union[HID, VCP]) -> None:
|
|
||||||
/// '''
|
|
||||||
/// Registers passed interface into the USB stack.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_USB_add(mp_obj_t self, mp_obj_t iface) {
|
|
||||||
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
|
|
||||||
if (o->state != USB_CLOSED) {
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, "already initialized");
|
|
||||||
}
|
|
||||||
mp_obj_list_append(MP_OBJ_FROM_PTR(&o->ifaces), iface);
|
|
||||||
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_USB_add_obj, mod_trezorio_USB_add);
|
|
||||||
|
|
||||||
/// def open(self) -> None:
|
|
||||||
/// '''
|
|
||||||
/// Initializes the USB stack.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_USB_open(mp_obj_t self) {
|
|
||||||
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
|
|
||||||
if (o->state != USB_CLOSED) {
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, "already initialized");
|
|
||||||
}
|
|
||||||
|
|
||||||
size_t iface_cnt;
|
|
||||||
mp_obj_t *iface_objs;
|
|
||||||
mp_obj_get_array(MP_OBJ_FROM_PTR(&o->ifaces), &iface_cnt, &iface_objs);
|
|
||||||
|
|
||||||
// Initialize the USB stack
|
|
||||||
usb_init(&o->info);
|
|
||||||
|
|
||||||
int vcp_iface_num = -1;
|
|
||||||
|
|
||||||
// Add all interfaces
|
|
||||||
for (size_t i = 0; i < iface_cnt; i++) {
|
|
||||||
mp_obj_t iface = iface_objs[i];
|
|
||||||
|
|
||||||
if (MP_OBJ_IS_TYPE(iface, &mod_trezorio_HID_type)) {
|
|
||||||
mp_obj_HID_t *hid = MP_OBJ_TO_PTR(iface);
|
|
||||||
if (sectrue != usb_hid_add(&hid->info)) {
|
|
||||||
usb_deinit();
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, "failed to add HID interface");
|
|
||||||
}
|
|
||||||
} else if (MP_OBJ_IS_TYPE(iface, &mod_trezorio_VCP_type)) {
|
|
||||||
mp_obj_VCP_t *vcp = MP_OBJ_TO_PTR(iface);
|
|
||||||
if (sectrue != usb_vcp_add(&vcp->info)) {
|
|
||||||
usb_deinit();
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, "failed to add VCP interface");
|
|
||||||
}
|
|
||||||
vcp_iface_num = vcp->info.iface_num;
|
|
||||||
} else {
|
|
||||||
usb_deinit();
|
|
||||||
mp_raise_TypeError("expected HID or VCP type");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the USB stack
|
|
||||||
usb_start();
|
|
||||||
o->state = USB_OPENED;
|
|
||||||
|
|
||||||
// If we found any VCP interfaces, use the last one for stdio,
|
|
||||||
// otherwise disable the stdio support
|
|
||||||
mp_hal_set_vcp_iface(vcp_iface_num);
|
|
||||||
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB_open_obj, mod_trezorio_USB_open);
|
|
||||||
|
|
||||||
/// def close(self) -> None:
|
|
||||||
/// '''
|
|
||||||
/// Cleans up the USB stack.
|
|
||||||
/// '''
|
|
||||||
STATIC mp_obj_t mod_trezorio_USB_close(mp_obj_t self) {
|
|
||||||
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
|
|
||||||
if (o->state != USB_OPENED) {
|
|
||||||
mp_raise_msg(&mp_type_RuntimeError, "not initialized");
|
|
||||||
}
|
|
||||||
usb_stop();
|
|
||||||
usb_deinit();
|
|
||||||
mp_obj_list_set_len(MP_OBJ_FROM_PTR(&o->ifaces), 0);
|
|
||||||
mp_seq_clear(o->ifaces.items, 0, o->ifaces.alloc, sizeof(*o->ifaces.items));
|
|
||||||
o->info.vendor_id = 0;
|
|
||||||
o->info.product_id = 0;
|
|
||||||
o->info.release_num = 0;
|
|
||||||
o->info.manufacturer = NULL;
|
|
||||||
o->info.product = NULL;
|
|
||||||
o->info.serial_number = NULL;
|
|
||||||
o->state = USB_CLOSED;
|
|
||||||
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB_close_obj, mod_trezorio_USB_close);
|
|
||||||
|
|
||||||
STATIC mp_obj_t mod_trezorio_USB___del__(mp_obj_t self) {
|
|
||||||
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
|
||||||
if (o->state != USB_CLOSED) {
|
|
||||||
usb_stop();
|
|
||||||
usb_deinit();
|
|
||||||
o->state = USB_CLOSED;
|
|
||||||
}
|
|
||||||
return mp_const_none;
|
|
||||||
}
|
|
||||||
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB___del___obj, mod_trezorio_USB___del__);
|
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mod_trezorio_USB_locals_dict_table[] = {
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&mod_trezorio_USB_add_obj) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_trezorio_USB_open_obj) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mod_trezorio_USB_close_obj) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_trezorio_USB___del___obj) },
|
|
||||||
};
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_USB_locals_dict, mod_trezorio_USB_locals_dict_table);
|
|
||||||
|
|
||||||
STATIC const mp_obj_type_t mod_trezorio_USB_type = {
|
|
||||||
{ &mp_type_type },
|
|
||||||
.name = MP_QSTR_USB,
|
|
||||||
.make_new = mod_trezorio_USB_make_new,
|
|
||||||
.locals_dict = (void*)&mod_trezorio_USB_locals_dict,
|
|
||||||
};
|
|
105
embed/extmod/modtrezorio/modtrezorio-poll.h
Normal file
105
embed/extmod/modtrezorio/modtrezorio-poll.h
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Pavol Rusnak, SatoshiLabs
|
||||||
|
*
|
||||||
|
* Licensed under TREZOR License
|
||||||
|
* see LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <string.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include "usb.h"
|
||||||
|
|
||||||
|
#define TOUCH_IFACE (255)
|
||||||
|
#define POLL_READ (0x0000)
|
||||||
|
#define POLL_WRITE (0x0100)
|
||||||
|
|
||||||
|
#define CHECK_PARAM_RANGE(value, minimum, maximum) \
|
||||||
|
if (value < minimum || value > maximum) { \
|
||||||
|
mp_raise_ValueError(#value " is out of range"); \
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def poll(ifaces: Iterable[int], list_ref: List, timeout_us: int) -> bool:
|
||||||
|
/// '''
|
||||||
|
/// Wait until one of `ifaces` is ready to read or write (using masks
|
||||||
|
// `io.POLL_READ` and `io.POLL_WRITE`) and assign the result into
|
||||||
|
/// `list_ref`:
|
||||||
|
///
|
||||||
|
/// `list_ref[0]` - the interface number, including the mask
|
||||||
|
/// `list_ref[1]` - for touch event, tuple of (event_type, x_position, y_position)
|
||||||
|
/// - for USB read event, received bytes
|
||||||
|
///
|
||||||
|
/// If timeout occurs, False is returned, True otherwise.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_poll(mp_obj_t ifaces, mp_obj_t list_ref, mp_obj_t timeout_us) {
|
||||||
|
mp_obj_list_t *ret = MP_OBJ_TO_PTR(list_ref);
|
||||||
|
if (!MP_OBJ_IS_TYPE(list_ref, &mp_type_list) || ret->len < 2) {
|
||||||
|
mp_raise_TypeError("invalid list_ref");
|
||||||
|
}
|
||||||
|
|
||||||
|
const mp_uint_t timeout = mp_obj_get_int(timeout_us);
|
||||||
|
const mp_uint_t deadline = mp_hal_ticks_us() + timeout;
|
||||||
|
mp_obj_iter_buf_t iterbuf;
|
||||||
|
|
||||||
|
for (;;) {
|
||||||
|
mp_obj_t iter = mp_getiter(ifaces, &iterbuf);
|
||||||
|
mp_obj_t item;
|
||||||
|
while ((item = mp_iternext(iter)) != MP_OBJ_STOP_ITERATION) {
|
||||||
|
const mp_uint_t i = mp_obj_int_get_truncated(item);
|
||||||
|
const mp_uint_t iface = i & 0x00FF;
|
||||||
|
const mp_uint_t mode = i & 0xFF00;
|
||||||
|
|
||||||
|
if (iface == TOUCH_IFACE) {
|
||||||
|
const uint32_t evt = touch_read();
|
||||||
|
if (evt) {
|
||||||
|
mp_obj_tuple_t *tuple = MP_OBJ_TO_PTR(mp_obj_new_tuple(3, NULL));
|
||||||
|
tuple->items[0] = MP_OBJ_NEW_SMALL_INT((evt >> 24) & 0xFFU); // event type
|
||||||
|
tuple->items[1] = MP_OBJ_NEW_SMALL_INT((evt >> 12) & 0xFFFU); // x position
|
||||||
|
tuple->items[2] = MP_OBJ_NEW_SMALL_INT(evt & 0xFFFU); // y position
|
||||||
|
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
ret->items[1] = MP_OBJ_FROM_PTR(tuple);
|
||||||
|
return mp_const_true;
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (mode == POLL_READ) {
|
||||||
|
if (sectrue == usb_hid_can_read(iface)) {
|
||||||
|
uint8_t buf[64];
|
||||||
|
int len = usb_hid_read(iface, buf, sizeof(buf));
|
||||||
|
if (len > 0) {
|
||||||
|
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||||
|
return mp_const_true;
|
||||||
|
}
|
||||||
|
} else if (sectrue == usb_webusb_can_read(iface)) {
|
||||||
|
uint8_t buf[64];
|
||||||
|
int len = usb_webusb_read(iface, buf, sizeof(buf));
|
||||||
|
if (len > 0) {
|
||||||
|
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
ret->items[1] = mp_obj_new_bytes(buf, len);
|
||||||
|
return mp_const_true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
if (mode == POLL_WRITE) {
|
||||||
|
if (sectrue == usb_hid_can_write(iface)) {
|
||||||
|
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
ret->items[1] = mp_const_none;
|
||||||
|
return mp_const_true;
|
||||||
|
} else if (sectrue == usb_webusb_can_write(iface)) {
|
||||||
|
ret->items[0] = MP_OBJ_NEW_SMALL_INT(i);
|
||||||
|
ret->items[1] = mp_const_none;
|
||||||
|
return mp_const_true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mp_hal_ticks_us() >= deadline) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
MICROPY_EVENT_POLL_HOOK
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mp_const_false;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_3(mod_trezorio_poll_obj, mod_trezorio_poll);
|
232
embed/extmod/modtrezorio/modtrezorio-usb.h
Normal file
232
embed/extmod/modtrezorio/modtrezorio-usb.h
Normal file
@ -0,0 +1,232 @@
|
|||||||
|
void mp_hal_set_vcp_iface(int iface_num);
|
||||||
|
|
||||||
|
enum {
|
||||||
|
USB_CLOSED = 0,
|
||||||
|
USB_OPENED = 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// class USB:
|
||||||
|
/// '''
|
||||||
|
/// USB device configuration.
|
||||||
|
/// '''
|
||||||
|
typedef struct _mp_obj_USB_t {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
mp_obj_list_t ifaces;
|
||||||
|
usb_dev_info_t info;
|
||||||
|
mp_int_t state;
|
||||||
|
} mp_obj_USB_t;
|
||||||
|
|
||||||
|
static const char *get_0str(mp_obj_t o, size_t min_len, size_t max_len) {
|
||||||
|
size_t len;
|
||||||
|
const char *s = mp_obj_str_get_data(o, &len);
|
||||||
|
if ((len >= min_len) && (len <= max_len)) {
|
||||||
|
if (len == 0 && s == NULL) {
|
||||||
|
return "";
|
||||||
|
} else {
|
||||||
|
return s;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def __init__(self,
|
||||||
|
/// vendor_id: int,
|
||||||
|
/// product_id: int,
|
||||||
|
/// release_num: int,
|
||||||
|
/// manufacturer: str='',
|
||||||
|
/// product: str='',
|
||||||
|
/// serial_number: str='',
|
||||||
|
/// configuration: str='',
|
||||||
|
/// interface: str='') -> None:
|
||||||
|
/// '''
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_USB_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||||
|
|
||||||
|
STATIC const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_vendor_id, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_product_id, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_release_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_manufacturer, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
||||||
|
{ MP_QSTR_product, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
||||||
|
{ MP_QSTR_serial_number, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
||||||
|
{ MP_QSTR_configuration, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
||||||
|
{ MP_QSTR_interface, MP_ARG_KW_ONLY | MP_ARG_OBJ, {.u_obj = mp_const_empty_bytes} },
|
||||||
|
};
|
||||||
|
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
||||||
|
|
||||||
|
const mp_int_t vendor_id = vals[0].u_int;
|
||||||
|
const mp_int_t product_id = vals[1].u_int;
|
||||||
|
const mp_int_t release_num = vals[2].u_int;
|
||||||
|
const char *manufacturer = get_0str(vals[3].u_obj, 0, 32);
|
||||||
|
const char *product = get_0str(vals[4].u_obj, 0, 32);
|
||||||
|
const char *serial_number = get_0str(vals[5].u_obj, 0, 32);
|
||||||
|
const char *configuration = get_0str(vals[6].u_obj, 0, 32);
|
||||||
|
const char *interface = get_0str(vals[7].u_obj, 0, 32);
|
||||||
|
|
||||||
|
CHECK_PARAM_RANGE(vendor_id, 0, 65535)
|
||||||
|
CHECK_PARAM_RANGE(product_id, 0, 65535)
|
||||||
|
CHECK_PARAM_RANGE(release_num, 0, 65535)
|
||||||
|
if (manufacturer == NULL) {
|
||||||
|
mp_raise_ValueError("manufacturer is invalid");
|
||||||
|
}
|
||||||
|
if (product == NULL) {
|
||||||
|
mp_raise_ValueError("product is invalid");
|
||||||
|
}
|
||||||
|
if (serial_number == NULL) {
|
||||||
|
mp_raise_ValueError("serial_number is invalid");
|
||||||
|
}
|
||||||
|
if (configuration == NULL) {
|
||||||
|
mp_raise_ValueError("configuration is invalid");
|
||||||
|
}
|
||||||
|
if (interface == NULL) {
|
||||||
|
mp_raise_ValueError("interface is invalid");
|
||||||
|
}
|
||||||
|
|
||||||
|
mp_obj_USB_t *o = m_new_obj(mp_obj_USB_t);
|
||||||
|
o->base.type = type;
|
||||||
|
|
||||||
|
o->state = USB_CLOSED;
|
||||||
|
|
||||||
|
o->info.vendor_id = (uint16_t)(vendor_id);
|
||||||
|
o->info.product_id = (uint16_t)(product_id);
|
||||||
|
o->info.release_num = (uint16_t)(release_num);
|
||||||
|
o->info.manufacturer = (const uint8_t *)(manufacturer);
|
||||||
|
o->info.product = (const uint8_t *)(product);
|
||||||
|
o->info.serial_number = (const uint8_t *)(serial_number);
|
||||||
|
o->info.configuration = (const uint8_t *)(configuration);
|
||||||
|
o->info.interface = (const uint8_t *)(interface);
|
||||||
|
mp_obj_list_init(&o->ifaces, 0);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def add(self, iface: Union[HID, VCP, WebUSB]) -> None:
|
||||||
|
/// '''
|
||||||
|
/// Registers passed interface into the USB stack.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_USB_add(mp_obj_t self, mp_obj_t iface) {
|
||||||
|
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
|
||||||
|
if (o->state != USB_CLOSED) {
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "already initialized");
|
||||||
|
}
|
||||||
|
mp_obj_list_append(MP_OBJ_FROM_PTR(&o->ifaces), iface);
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_USB_add_obj, mod_trezorio_USB_add);
|
||||||
|
|
||||||
|
/// def open(self) -> None:
|
||||||
|
/// '''
|
||||||
|
/// Initializes the USB stack.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_USB_open(mp_obj_t self) {
|
||||||
|
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
|
||||||
|
if (o->state != USB_CLOSED) {
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "already initialized");
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t iface_cnt;
|
||||||
|
mp_obj_t *iface_objs;
|
||||||
|
mp_obj_get_array(MP_OBJ_FROM_PTR(&o->ifaces), &iface_cnt, &iface_objs);
|
||||||
|
|
||||||
|
// Initialize the USB stack
|
||||||
|
usb_init(&o->info);
|
||||||
|
|
||||||
|
int vcp_iface_num = -1;
|
||||||
|
|
||||||
|
// Add all interfaces
|
||||||
|
for (size_t i = 0; i < iface_cnt; i++) {
|
||||||
|
mp_obj_t iface = iface_objs[i];
|
||||||
|
|
||||||
|
if (MP_OBJ_IS_TYPE(iface, &mod_trezorio_HID_type)) {
|
||||||
|
mp_obj_HID_t *hid = MP_OBJ_TO_PTR(iface);
|
||||||
|
if (sectrue != usb_hid_add(&hid->info)) {
|
||||||
|
usb_deinit();
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "failed to add HID interface");
|
||||||
|
}
|
||||||
|
} else if (MP_OBJ_IS_TYPE(iface, &mod_trezorio_WebUSB_type)) {
|
||||||
|
mp_obj_WebUSB_t *webusb = MP_OBJ_TO_PTR(iface);
|
||||||
|
if (sectrue != usb_webusb_add(&webusb->info)) {
|
||||||
|
usb_deinit();
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "failed to add WebUSB interface");
|
||||||
|
}
|
||||||
|
} else if (MP_OBJ_IS_TYPE(iface, &mod_trezorio_VCP_type)) {
|
||||||
|
mp_obj_VCP_t *vcp = MP_OBJ_TO_PTR(iface);
|
||||||
|
if (sectrue != usb_vcp_add(&vcp->info)) {
|
||||||
|
usb_deinit();
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "failed to add VCP interface");
|
||||||
|
}
|
||||||
|
vcp_iface_num = vcp->info.iface_num;
|
||||||
|
} else {
|
||||||
|
usb_deinit();
|
||||||
|
mp_raise_TypeError("expected HID, WebUSB or VCP type");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the USB stack
|
||||||
|
usb_start();
|
||||||
|
o->state = USB_OPENED;
|
||||||
|
|
||||||
|
// If we found any VCP interfaces, use the last one for stdio,
|
||||||
|
// otherwise disable the stdio support
|
||||||
|
mp_hal_set_vcp_iface(vcp_iface_num);
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB_open_obj, mod_trezorio_USB_open);
|
||||||
|
|
||||||
|
/// def close(self) -> None:
|
||||||
|
/// '''
|
||||||
|
/// Cleans up the USB stack.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_USB_close(mp_obj_t self) {
|
||||||
|
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
|
||||||
|
if (o->state != USB_OPENED) {
|
||||||
|
mp_raise_msg(&mp_type_RuntimeError, "not initialized");
|
||||||
|
}
|
||||||
|
usb_stop();
|
||||||
|
usb_deinit();
|
||||||
|
mp_obj_list_set_len(MP_OBJ_FROM_PTR(&o->ifaces), 0);
|
||||||
|
mp_seq_clear(o->ifaces.items, 0, o->ifaces.alloc, sizeof(*o->ifaces.items));
|
||||||
|
o->info.vendor_id = 0;
|
||||||
|
o->info.product_id = 0;
|
||||||
|
o->info.release_num = 0;
|
||||||
|
o->info.manufacturer = NULL;
|
||||||
|
o->info.product = NULL;
|
||||||
|
o->info.serial_number = NULL;
|
||||||
|
o->state = USB_CLOSED;
|
||||||
|
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB_close_obj, mod_trezorio_USB_close);
|
||||||
|
|
||||||
|
STATIC mp_obj_t mod_trezorio_USB___del__(mp_obj_t self) {
|
||||||
|
mp_obj_USB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
if (o->state != USB_CLOSED) {
|
||||||
|
usb_stop();
|
||||||
|
usb_deinit();
|
||||||
|
o->state = USB_CLOSED;
|
||||||
|
}
|
||||||
|
return mp_const_none;
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_USB___del___obj, mod_trezorio_USB___del__);
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t mod_trezorio_USB_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_add), MP_ROM_PTR(&mod_trezorio_USB_add_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_open), MP_ROM_PTR(&mod_trezorio_USB_open_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_close), MP_ROM_PTR(&mod_trezorio_USB_close_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR___del__), MP_ROM_PTR(&mod_trezorio_USB___del___obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_USB_locals_dict, mod_trezorio_USB_locals_dict_table);
|
||||||
|
|
||||||
|
STATIC const mp_obj_type_t mod_trezorio_USB_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_USB,
|
||||||
|
.make_new = mod_trezorio_USB_make_new,
|
||||||
|
.locals_dict = (void*)&mod_trezorio_USB_locals_dict,
|
||||||
|
};
|
89
embed/extmod/modtrezorio/modtrezorio-vcp.h
Normal file
89
embed/extmod/modtrezorio/modtrezorio-vcp.h
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
void pendsv_kbd_intr(void);
|
||||||
|
|
||||||
|
/// class VCP:
|
||||||
|
/// '''
|
||||||
|
/// USB VCP interface configuration.
|
||||||
|
/// '''
|
||||||
|
typedef struct _mp_obj_VCP_t {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
usb_vcp_info_t info;
|
||||||
|
} mp_obj_VCP_t;
|
||||||
|
|
||||||
|
/// def __init__(self,
|
||||||
|
/// iface_num: int,
|
||||||
|
/// data_iface_num: int,
|
||||||
|
/// ep_in: int,
|
||||||
|
/// ep_out: int,
|
||||||
|
/// ep_cmd: int) -> None:
|
||||||
|
/// '''
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_VCP_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||||
|
|
||||||
|
STATIC const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_data_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_in, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_out, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_cmd, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
};
|
||||||
|
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
||||||
|
|
||||||
|
const mp_int_t iface_num = vals[0].u_int;
|
||||||
|
const mp_int_t data_iface_num = vals[1].u_int;
|
||||||
|
const mp_int_t ep_in = vals[2].u_int;
|
||||||
|
const mp_int_t ep_out = vals[3].u_int;
|
||||||
|
const mp_int_t ep_cmd = vals[4].u_int;
|
||||||
|
|
||||||
|
CHECK_PARAM_RANGE(iface_num, 0, 32)
|
||||||
|
CHECK_PARAM_RANGE(data_iface_num, 0, 32)
|
||||||
|
CHECK_PARAM_RANGE(ep_in, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(ep_out, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(ep_cmd, 0, 255)
|
||||||
|
|
||||||
|
const size_t vcp_buffer_len = 1024;
|
||||||
|
const size_t vcp_packet_len = 64;
|
||||||
|
|
||||||
|
mp_obj_VCP_t *o = m_new_obj(mp_obj_VCP_t);
|
||||||
|
o->base.type = type;
|
||||||
|
|
||||||
|
o->info.tx_packet = m_new(uint8_t, vcp_packet_len);
|
||||||
|
o->info.tx_buffer = m_new(uint8_t, vcp_buffer_len);
|
||||||
|
o->info.rx_packet = m_new(uint8_t, vcp_packet_len);
|
||||||
|
o->info.rx_buffer = m_new(uint8_t, vcp_buffer_len);
|
||||||
|
o->info.tx_buffer_len = vcp_buffer_len;
|
||||||
|
o->info.rx_buffer_len = vcp_buffer_len;
|
||||||
|
o->info.rx_intr_fn = pendsv_kbd_intr;
|
||||||
|
o->info.rx_intr_byte = 3; // Ctrl-C
|
||||||
|
o->info.iface_num = (uint8_t)(iface_num);
|
||||||
|
o->info.data_iface_num = (uint8_t)(data_iface_num);
|
||||||
|
o->info.ep_cmd = (uint8_t)(ep_cmd);
|
||||||
|
o->info.ep_in = (uint8_t)(ep_in);
|
||||||
|
o->info.ep_out = (uint8_t)(ep_out);
|
||||||
|
o->info.polling_interval = 10;
|
||||||
|
o->info.max_packet_len = (uint8_t)(vcp_packet_len);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def iface_num(self) -> int:
|
||||||
|
/// '''
|
||||||
|
/// Returns the configured number of this interface.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_VCP_iface_num(mp_obj_t self) {
|
||||||
|
mp_obj_VCP_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
return MP_OBJ_NEW_SMALL_INT(o->info.iface_num);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_VCP_iface_num_obj, mod_trezorio_VCP_iface_num);
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t mod_trezorio_VCP_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_iface_num), MP_ROM_PTR(&mod_trezorio_VCP_iface_num_obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_VCP_locals_dict, mod_trezorio_VCP_locals_dict_table);
|
||||||
|
|
||||||
|
STATIC const mp_obj_type_t mod_trezorio_VCP_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_VCP,
|
||||||
|
.make_new = mod_trezorio_VCP_make_new,
|
||||||
|
.locals_dict = (void*)&mod_trezorio_VCP_locals_dict,
|
||||||
|
};
|
99
embed/extmod/modtrezorio/modtrezorio-webusb.h
Normal file
99
embed/extmod/modtrezorio/modtrezorio-webusb.h
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
/// class WebUSB:
|
||||||
|
/// '''
|
||||||
|
/// USB WebUSB interface configuration.
|
||||||
|
/// '''
|
||||||
|
typedef struct _mp_obj_WebUSB_t {
|
||||||
|
mp_obj_base_t base;
|
||||||
|
usb_webusb_info_t info;
|
||||||
|
} mp_obj_WebUSB_t;
|
||||||
|
|
||||||
|
/// def __init__(self,
|
||||||
|
/// iface_num: int,
|
||||||
|
/// ep_in: int,
|
||||||
|
/// ep_out: int,
|
||||||
|
/// subclass: int = 0,
|
||||||
|
/// protocol: int = 0,
|
||||||
|
/// polling_interval: int = 1,
|
||||||
|
/// max_packet_len: int = 64) -> None:
|
||||||
|
/// '''
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_WebUSB_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *args) {
|
||||||
|
|
||||||
|
STATIC const mp_arg_t allowed_args[] = {
|
||||||
|
{ MP_QSTR_iface_num, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_in, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_ep_out, MP_ARG_REQUIRED | MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_subclass, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_protocol, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 0} },
|
||||||
|
{ MP_QSTR_polling_interval, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 1} },
|
||||||
|
{ MP_QSTR_max_packet_len, MP_ARG_KW_ONLY | MP_ARG_INT, {.u_int = 64} },
|
||||||
|
};
|
||||||
|
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
|
||||||
|
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args), allowed_args, vals);
|
||||||
|
|
||||||
|
const mp_int_t iface_num = vals[0].u_int;
|
||||||
|
const mp_int_t ep_in = vals[1].u_int;
|
||||||
|
const mp_int_t ep_out = vals[2].u_int;
|
||||||
|
const mp_int_t subclass = vals[3].u_int;
|
||||||
|
const mp_int_t protocol = vals[4].u_int;
|
||||||
|
const mp_int_t polling_interval = vals[5].u_int;
|
||||||
|
const mp_int_t max_packet_len = vals[6].u_int;
|
||||||
|
|
||||||
|
CHECK_PARAM_RANGE(iface_num, 0, 32)
|
||||||
|
CHECK_PARAM_RANGE(ep_in, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(ep_out, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(subclass, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(protocol, 0, 255)
|
||||||
|
CHECK_PARAM_RANGE(polling_interval, 1, 255)
|
||||||
|
CHECK_PARAM_RANGE(max_packet_len, 64, 64)
|
||||||
|
|
||||||
|
mp_obj_WebUSB_t *o = m_new_obj(mp_obj_WebUSB_t);
|
||||||
|
o->base.type = type;
|
||||||
|
|
||||||
|
o->info.rx_buffer = m_new(uint8_t, max_packet_len);
|
||||||
|
o->info.iface_num = (uint8_t)(iface_num);
|
||||||
|
o->info.ep_in = (uint8_t)(ep_in);
|
||||||
|
o->info.ep_out = (uint8_t)(ep_out);
|
||||||
|
o->info.subclass = (uint8_t)(subclass);
|
||||||
|
o->info.protocol = (uint8_t)(protocol);
|
||||||
|
o->info.polling_interval = (uint8_t)(polling_interval);
|
||||||
|
o->info.max_packet_len = (uint8_t)(max_packet_len);
|
||||||
|
|
||||||
|
return MP_OBJ_FROM_PTR(o);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// def iface_num(self) -> int:
|
||||||
|
/// '''
|
||||||
|
/// Returns the configured number of this interface.
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_WebUSB_iface_num(mp_obj_t self) {
|
||||||
|
mp_obj_WebUSB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
return MP_OBJ_NEW_SMALL_INT(o->info.iface_num);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_1(mod_trezorio_WebUSB_iface_num_obj, mod_trezorio_WebUSB_iface_num);
|
||||||
|
|
||||||
|
/// def write(self, msg: bytes) -> int:
|
||||||
|
/// '''
|
||||||
|
/// Sends message using USB WebUSB (device) or UDP (emulator).
|
||||||
|
/// '''
|
||||||
|
STATIC mp_obj_t mod_trezorio_WebUSB_write(mp_obj_t self, mp_obj_t msg) {
|
||||||
|
mp_obj_WebUSB_t *o = MP_OBJ_TO_PTR(self);
|
||||||
|
mp_buffer_info_t buf;
|
||||||
|
mp_get_buffer_raise(msg, &buf, MP_BUFFER_READ);
|
||||||
|
ssize_t r = usb_webusb_write(o->info.iface_num, buf.buf, buf.len);
|
||||||
|
return MP_OBJ_NEW_SMALL_INT(r);
|
||||||
|
}
|
||||||
|
STATIC MP_DEFINE_CONST_FUN_OBJ_2(mod_trezorio_WebUSB_write_obj, mod_trezorio_WebUSB_write);
|
||||||
|
|
||||||
|
STATIC const mp_rom_map_elem_t mod_trezorio_WebUSB_locals_dict_table[] = {
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_iface_num), MP_ROM_PTR(&mod_trezorio_WebUSB_iface_num_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_write), MP_ROM_PTR(&mod_trezorio_WebUSB_write_obj) },
|
||||||
|
};
|
||||||
|
STATIC MP_DEFINE_CONST_DICT(mod_trezorio_WebUSB_locals_dict, mod_trezorio_WebUSB_locals_dict_table);
|
||||||
|
|
||||||
|
STATIC const mp_obj_type_t mod_trezorio_WebUSB_type = {
|
||||||
|
{ &mp_type_type },
|
||||||
|
.name = MP_QSTR_WebUSB,
|
||||||
|
.make_new = mod_trezorio_WebUSB_make_new,
|
||||||
|
.locals_dict = (void*)&mod_trezorio_WebUSB_locals_dict,
|
||||||
|
};
|
@ -11,10 +11,16 @@
|
|||||||
|
|
||||||
#if MICROPY_PY_TREZORIO
|
#if MICROPY_PY_TREZORIO
|
||||||
|
|
||||||
|
#include "touch.h"
|
||||||
|
|
||||||
#include "modtrezorio-flash.h"
|
#include "modtrezorio-flash.h"
|
||||||
#include "modtrezorio-sbu.h"
|
#include "modtrezorio-sbu.h"
|
||||||
#include "modtrezorio-sdcard.h"
|
#include "modtrezorio-sdcard.h"
|
||||||
#include "modtrezorio-msg.h"
|
#include "modtrezorio-poll.h"
|
||||||
|
#include "modtrezorio-hid.h"
|
||||||
|
#include "modtrezorio-vcp.h"
|
||||||
|
#include "modtrezorio-webusb.h"
|
||||||
|
#include "modtrezorio-usb.h"
|
||||||
|
|
||||||
STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
||||||
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio) },
|
{ MP_ROM_QSTR(MP_QSTR___name__), MP_ROM_QSTR(MP_QSTR_trezorio) },
|
||||||
@ -28,13 +34,16 @@ STATIC const mp_rom_map_elem_t mp_module_trezorio_globals_table[] = {
|
|||||||
{ MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&mod_trezorio_USB_type) },
|
{ MP_ROM_QSTR(MP_QSTR_USB), MP_ROM_PTR(&mod_trezorio_USB_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_HID), MP_ROM_PTR(&mod_trezorio_HID_type) },
|
{ MP_ROM_QSTR(MP_QSTR_HID), MP_ROM_PTR(&mod_trezorio_HID_type) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_VCP), MP_ROM_PTR(&mod_trezorio_VCP_type) },
|
{ MP_ROM_QSTR(MP_QSTR_VCP), MP_ROM_PTR(&mod_trezorio_VCP_type) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_WebUSB), MP_ROM_PTR(&mod_trezorio_WebUSB_type) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mod_trezorio_poll_obj) },
|
{ MP_ROM_QSTR(MP_QSTR_poll), MP_ROM_PTR(&mod_trezorio_poll_obj) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_POLL_READ), MP_OBJ_NEW_SMALL_INT(POLL_READ) },
|
||||||
|
{ MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_OBJ_NEW_SMALL_INT(POLL_WRITE) },
|
||||||
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_TOUCH), MP_OBJ_NEW_SMALL_INT(TOUCH_IFACE) },
|
{ MP_ROM_QSTR(MP_QSTR_TOUCH), MP_OBJ_NEW_SMALL_INT(TOUCH_IFACE) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_TOUCH_START), MP_OBJ_NEW_SMALL_INT((TOUCH_START >> 24) & 0xFFU) },
|
{ MP_ROM_QSTR(MP_QSTR_TOUCH_START), MP_OBJ_NEW_SMALL_INT((TOUCH_START >> 24) & 0xFFU) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_TOUCH_MOVE), MP_OBJ_NEW_SMALL_INT((TOUCH_MOVE >> 24) & 0xFFU) },
|
{ MP_ROM_QSTR(MP_QSTR_TOUCH_MOVE), MP_OBJ_NEW_SMALL_INT((TOUCH_MOVE >> 24) & 0xFFU) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_TOUCH_END), MP_OBJ_NEW_SMALL_INT((TOUCH_END >> 24) & 0xFFU) },
|
{ MP_ROM_QSTR(MP_QSTR_TOUCH_END), MP_OBJ_NEW_SMALL_INT((TOUCH_END >> 24) & 0xFFU) },
|
||||||
{ MP_ROM_QSTR(MP_QSTR_POLL_READ), MP_OBJ_NEW_SMALL_INT(POLL_READ) },
|
|
||||||
{ MP_ROM_QSTR(MP_QSTR_POLL_WRITE), MP_OBJ_NEW_SMALL_INT(POLL_WRITE) },
|
|
||||||
};
|
};
|
||||||
|
|
||||||
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals, mp_module_trezorio_globals_table);
|
STATIC MP_DEFINE_CONST_DICT(mp_module_trezorio_globals, mp_module_trezorio_globals_table);
|
||||||
|
@ -53,7 +53,7 @@ void usb_init(const usb_dev_info_t *dev_info) {
|
|||||||
// Device descriptor
|
// Device descriptor
|
||||||
usb_dev_desc.bLength = sizeof(usb_device_descriptor_t);
|
usb_dev_desc.bLength = sizeof(usb_device_descriptor_t);
|
||||||
usb_dev_desc.bDescriptorType = USB_DESC_TYPE_DEVICE;
|
usb_dev_desc.bDescriptorType = USB_DESC_TYPE_DEVICE;
|
||||||
usb_dev_desc.bcdUSB = 0x0200; // USB 2.0
|
usb_dev_desc.bcdUSB = 0x0210; // USB 2.1
|
||||||
usb_dev_desc.bDeviceClass = 0xEF; // Composite Device Class
|
usb_dev_desc.bDeviceClass = 0xEF; // Composite Device Class
|
||||||
usb_dev_desc.bDeviceSubClass = 0x02; // Common Class
|
usb_dev_desc.bDeviceSubClass = 0x02; // Common Class
|
||||||
usb_dev_desc.bDeviceProtocol = 0x01; // Interface Association Descriptor
|
usb_dev_desc.bDeviceProtocol = 0x01; // Interface Association Descriptor
|
||||||
@ -157,6 +157,7 @@ static uint8_t usb_ep_clear_nak(USBD_HandleTypeDef *dev, uint8_t ep_num) {
|
|||||||
|
|
||||||
#include "usb_hid-impl.h"
|
#include "usb_hid-impl.h"
|
||||||
#include "usb_vcp-impl.h"
|
#include "usb_vcp-impl.h"
|
||||||
|
#include "usb_webusb-impl.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* USB configuration (device & string descriptors)
|
* USB configuration (device & string descriptors)
|
||||||
@ -202,6 +203,29 @@ static uint8_t *usb_get_interface_str_descriptor(USBD_SpeedTypeDef speed, uint16
|
|||||||
return usb_str_buf;
|
return usb_str_buf;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static uint8_t *usb_get_bos_descriptor(USBD_SpeedTypeDef speed, uint16_t *length) {
|
||||||
|
static const uint8_t bos[] = {
|
||||||
|
// usb_bos_descriptor {
|
||||||
|
0x05, // uint8_t bLength
|
||||||
|
USB_DESC_TYPE_BOS, // uint8_t bDescriptorType
|
||||||
|
0x1d, 0x0, // uint16_t wTotalLength
|
||||||
|
0x01, // uint8_t bNumDeviceCaps
|
||||||
|
// }
|
||||||
|
// usb_device_capability_descriptor {
|
||||||
|
0x18, // uint8_t bLength
|
||||||
|
USB_DESC_TYPE_DEVICE_CAPABILITY, // uint8_t bDescriptorType
|
||||||
|
USB_DEVICE_CAPABILITY_PLATFORM, // uint8_t bDevCapabilityType
|
||||||
|
0x00, // uint8_t bReserved
|
||||||
|
0x38, 0xb6, 0x08, 0x34, 0xa9, 0x09, 0xa0, 0x47, 0x8b, 0xfd, 0xa0, 0x76, 0x88, 0x15, 0xb6, 0x65, // uint128_t platformCompatibilityUUID
|
||||||
|
0x00, 0x01, // uint16_t bcdVersion
|
||||||
|
USB_WEBUSB_VENDOR_CODE, // uint8_t bVendorCode
|
||||||
|
USB_WEBUSB_LANDING_PAGE, // uint8_t iLandingPage
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
*length = sizeof(bos);
|
||||||
|
return UNCONST(bos);
|
||||||
|
}
|
||||||
|
|
||||||
static const USBD_DescriptorsTypeDef usb_descriptors = {
|
static const USBD_DescriptorsTypeDef usb_descriptors = {
|
||||||
.GetDeviceDescriptor = usb_get_dev_descriptor,
|
.GetDeviceDescriptor = usb_get_dev_descriptor,
|
||||||
.GetLangIDStrDescriptor = usb_get_langid_str_descriptor,
|
.GetLangIDStrDescriptor = usb_get_langid_str_descriptor,
|
||||||
@ -210,6 +234,7 @@ static const USBD_DescriptorsTypeDef usb_descriptors = {
|
|||||||
.GetSerialStrDescriptor = usb_get_serial_str_descriptor,
|
.GetSerialStrDescriptor = usb_get_serial_str_descriptor,
|
||||||
.GetConfigurationStrDescriptor = usb_get_configuration_str_descriptor,
|
.GetConfigurationStrDescriptor = usb_get_configuration_str_descriptor,
|
||||||
.GetInterfaceStrDescriptor = usb_get_interface_str_descriptor,
|
.GetInterfaceStrDescriptor = usb_get_interface_str_descriptor,
|
||||||
|
.GetBOSDescriptor = usb_get_bos_descriptor,
|
||||||
};
|
};
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -225,6 +250,9 @@ static uint8_t usb_class_init(USBD_HandleTypeDef *dev, uint8_t cfg_idx) {
|
|||||||
case USB_IFACE_TYPE_VCP:
|
case USB_IFACE_TYPE_VCP:
|
||||||
usb_vcp_class_init(dev, &usb_ifaces[i].vcp, cfg_idx);
|
usb_vcp_class_init(dev, &usb_ifaces[i].vcp, cfg_idx);
|
||||||
break;
|
break;
|
||||||
|
case USB_IFACE_TYPE_WEBUSB:
|
||||||
|
usb_webusb_class_init(dev, &usb_ifaces[i].webusb, cfg_idx);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -241,6 +269,9 @@ static uint8_t usb_class_deinit(USBD_HandleTypeDef *dev, uint8_t cfg_idx) {
|
|||||||
case USB_IFACE_TYPE_VCP:
|
case USB_IFACE_TYPE_VCP:
|
||||||
usb_vcp_class_deinit(dev, &usb_ifaces[i].vcp, cfg_idx);
|
usb_vcp_class_deinit(dev, &usb_ifaces[i].vcp, cfg_idx);
|
||||||
break;
|
break;
|
||||||
|
case USB_IFACE_TYPE_WEBUSB:
|
||||||
|
usb_webusb_class_deinit(dev, &usb_ifaces[i].webusb, cfg_idx);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -261,6 +292,8 @@ static uint8_t usb_class_setup(USBD_HandleTypeDef *dev, USBD_SetupReqTypedef *re
|
|||||||
return usb_hid_class_setup(dev, &usb_ifaces[req->wIndex].hid, req);
|
return usb_hid_class_setup(dev, &usb_ifaces[req->wIndex].hid, req);
|
||||||
case USB_IFACE_TYPE_VCP:
|
case USB_IFACE_TYPE_VCP:
|
||||||
return usb_vcp_class_setup(dev, &usb_ifaces[req->wIndex].vcp, req);
|
return usb_vcp_class_setup(dev, &usb_ifaces[req->wIndex].vcp, req);
|
||||||
|
case USB_IFACE_TYPE_WEBUSB:
|
||||||
|
return usb_webusb_class_setup(dev, &usb_ifaces[req->wIndex].webusb, req);
|
||||||
default:
|
default:
|
||||||
return USBD_FAIL;
|
return USBD_FAIL;
|
||||||
}
|
}
|
||||||
@ -275,6 +308,9 @@ static uint8_t usb_class_data_in(USBD_HandleTypeDef *dev, uint8_t ep_num) {
|
|||||||
case USB_IFACE_TYPE_VCP:
|
case USB_IFACE_TYPE_VCP:
|
||||||
usb_vcp_class_data_in(dev, &usb_ifaces[i].vcp, ep_num);
|
usb_vcp_class_data_in(dev, &usb_ifaces[i].vcp, ep_num);
|
||||||
break;
|
break;
|
||||||
|
case USB_IFACE_TYPE_WEBUSB:
|
||||||
|
usb_webusb_class_data_in(dev, &usb_ifaces[i].webusb, ep_num);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -291,6 +327,9 @@ static uint8_t usb_class_data_out(USBD_HandleTypeDef *dev, uint8_t ep_num) {
|
|||||||
case USB_IFACE_TYPE_VCP:
|
case USB_IFACE_TYPE_VCP:
|
||||||
usb_vcp_class_data_out(dev, &usb_ifaces[i].vcp, ep_num);
|
usb_vcp_class_data_out(dev, &usb_ifaces[i].vcp, ep_num);
|
||||||
break;
|
break;
|
||||||
|
case USB_IFACE_TYPE_WEBUSB:
|
||||||
|
usb_webusb_class_data_out(dev, &usb_ifaces[i].webusb, ep_num);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -108,15 +108,18 @@ typedef enum {
|
|||||||
USB_IFACE_TYPE_DISABLED = 0,
|
USB_IFACE_TYPE_DISABLED = 0,
|
||||||
USB_IFACE_TYPE_VCP = 1,
|
USB_IFACE_TYPE_VCP = 1,
|
||||||
USB_IFACE_TYPE_HID = 2,
|
USB_IFACE_TYPE_HID = 2,
|
||||||
|
USB_IFACE_TYPE_WEBUSB = 3,
|
||||||
} usb_iface_type_t;
|
} usb_iface_type_t;
|
||||||
|
|
||||||
#include "usb_hid-defs.h"
|
#include "usb_hid-defs.h"
|
||||||
#include "usb_vcp-defs.h"
|
#include "usb_vcp-defs.h"
|
||||||
|
#include "usb_webusb-defs.h"
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
union {
|
union {
|
||||||
usb_hid_state_t hid;
|
usb_hid_state_t hid;
|
||||||
usb_vcp_state_t vcp;
|
usb_vcp_state_t vcp;
|
||||||
|
usb_webusb_state_t webusb;
|
||||||
};
|
};
|
||||||
usb_iface_type_t type;
|
usb_iface_type_t type;
|
||||||
} usb_iface_t;
|
} usb_iface_t;
|
||||||
|
57
embed/trezorhal/usb_webusb-defs.h
Normal file
57
embed/trezorhal/usb_webusb-defs.h
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Jan Pochyla, SatoshiLabs
|
||||||
|
*
|
||||||
|
* Licensed under TREZOR License
|
||||||
|
* see LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define USB_WEBUSB_VENDOR_CODE 0x01 // arbitrary
|
||||||
|
#define USB_WEBUSB_LANDING_PAGE 0x01 // arbitrary
|
||||||
|
|
||||||
|
typedef struct __attribute__((packed)) {
|
||||||
|
usb_interface_descriptor_t iface;
|
||||||
|
usb_endpoint_descriptor_t ep_in;
|
||||||
|
usb_endpoint_descriptor_t ep_out;
|
||||||
|
} usb_webusb_descriptor_block_t;
|
||||||
|
|
||||||
|
/* usb_webusb_info_t contains all information for setting up a WebUSB interface. All
|
||||||
|
* passed pointers need to live at least until the interface is disabled
|
||||||
|
* (usb_stop is called). */
|
||||||
|
typedef struct {
|
||||||
|
uint8_t *rx_buffer; // With length of max_packet_len bytes
|
||||||
|
uint8_t iface_num; // Address of this WebUSB interface
|
||||||
|
uint8_t ep_in; // Address of IN endpoint (with the highest bit set)
|
||||||
|
uint8_t ep_out; // Address of OUT endpoint
|
||||||
|
uint8_t subclass; // usb_iface_subclass_t
|
||||||
|
uint8_t protocol; // usb_iface_protocol_t
|
||||||
|
uint8_t polling_interval; // In units of 1ms
|
||||||
|
uint8_t max_packet_len; // Length of the biggest report and of rx_buffer
|
||||||
|
} usb_webusb_info_t;
|
||||||
|
|
||||||
|
/* usb_webusb_state_t encapsulates all state used by enabled WebUSB interface. It
|
||||||
|
* needs to be completely initialized in usb_webusb_add and reset in
|
||||||
|
* usb_webusb_class_init. See usb_webusb_info_t for details of the configuration
|
||||||
|
* fields. */
|
||||||
|
typedef struct {
|
||||||
|
const usb_webusb_descriptor_block_t *desc_block;
|
||||||
|
uint8_t *rx_buffer;
|
||||||
|
uint8_t ep_in;
|
||||||
|
uint8_t ep_out;
|
||||||
|
uint8_t max_packet_len;
|
||||||
|
|
||||||
|
uint8_t protocol; // For SET_PROTOCOL/GET_PROTOCOL setup reqs
|
||||||
|
uint8_t idle_rate; // For SET_IDLE/GET_IDLE setup reqs
|
||||||
|
uint8_t alt_setting; // For SET_INTERFACE/GET_INTERFACE setup reqs
|
||||||
|
uint8_t last_read_len; // Length of data read into rx_buffer
|
||||||
|
uint8_t ep_in_is_idle; // Set to 1 after IN endpoint gets idle
|
||||||
|
} usb_webusb_state_t;
|
||||||
|
|
||||||
|
secbool __wur usb_webusb_add(const usb_webusb_info_t *webusb_info);
|
||||||
|
secbool __wur usb_webusb_can_read(uint8_t iface_num);
|
||||||
|
secbool __wur usb_webusb_can_write(uint8_t iface_num);
|
||||||
|
int __wur usb_webusb_read(uint8_t iface_num, uint8_t *buf, uint32_t len);
|
||||||
|
int __wur usb_webusb_write(uint8_t iface_num, const uint8_t *buf, uint32_t len);
|
||||||
|
|
||||||
|
int __wur usb_webusb_read_select(uint32_t timeout);
|
||||||
|
int __wur usb_webusb_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, int timeout);
|
||||||
|
int __wur usb_webusb_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, int timeout);
|
311
embed/trezorhal/usb_webusb-impl.h
Normal file
311
embed/trezorhal/usb_webusb-impl.h
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) Jan Pochyla, SatoshiLabs
|
||||||
|
*
|
||||||
|
* Licensed under TREZOR License
|
||||||
|
* see LICENSE file for details
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define USB_CLASS_WEBUSB 0xFF
|
||||||
|
|
||||||
|
#define USB_WEBUSB_REQ_SET_PROTOCOL 0x0B
|
||||||
|
#define USB_WEBUSB_REQ_GET_PROTOCOL 0x03
|
||||||
|
#define USB_WEBUSB_REQ_SET_IDLE 0x0A
|
||||||
|
#define USB_WEBUSB_REQ_GET_IDLE 0x02
|
||||||
|
|
||||||
|
#define USB_WEBUSB_REQ_GET_URL 0x02
|
||||||
|
#define USB_WEBUSB_DESCRIPTOR_TYPE_URL 0x03
|
||||||
|
#define USB_WEBUSB_URL_SCHEME_HTTP 0
|
||||||
|
#define USB_WEBUSB_URL_SCHEME_HTTPS 1
|
||||||
|
|
||||||
|
/* usb_webusb_add adds and configures new USB WebUSB interface according to
|
||||||
|
* configuration options passed in `info`. */
|
||||||
|
secbool usb_webusb_add(const usb_webusb_info_t *info) {
|
||||||
|
|
||||||
|
usb_iface_t *iface = usb_get_iface(info->iface_num);
|
||||||
|
|
||||||
|
if (iface == NULL) {
|
||||||
|
return secfalse; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (iface->type != USB_IFACE_TYPE_DISABLED) {
|
||||||
|
return secfalse; // Interface is already enabled
|
||||||
|
}
|
||||||
|
|
||||||
|
usb_webusb_descriptor_block_t *d = usb_desc_alloc_iface(sizeof(usb_webusb_descriptor_block_t));
|
||||||
|
|
||||||
|
if (d == NULL) {
|
||||||
|
return secfalse; // Not enough space in the configuration descriptor
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((info->ep_in & USB_EP_DIR_MSK) != USB_EP_DIR_IN) {
|
||||||
|
return secfalse; // IN EP is invalid
|
||||||
|
}
|
||||||
|
if ((info->ep_out & USB_EP_DIR_MSK) != USB_EP_DIR_OUT) {
|
||||||
|
return secfalse; // OUT EP is invalid
|
||||||
|
}
|
||||||
|
if (info->rx_buffer == NULL) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Interface descriptor
|
||||||
|
d->iface.bLength = sizeof(usb_interface_descriptor_t);
|
||||||
|
d->iface.bDescriptorType = USB_DESC_TYPE_INTERFACE;
|
||||||
|
d->iface.bInterfaceNumber = info->iface_num;
|
||||||
|
d->iface.bAlternateSetting = 0;
|
||||||
|
d->iface.bNumEndpoints = 2;
|
||||||
|
d->iface.bInterfaceClass = USB_CLASS_WEBUSB;
|
||||||
|
d->iface.bInterfaceSubClass = info->subclass;
|
||||||
|
d->iface.bInterfaceProtocol = info->protocol;
|
||||||
|
d->iface.iInterface = USBD_IDX_INTERFACE_STR;
|
||||||
|
|
||||||
|
// IN endpoint (sending)
|
||||||
|
d->ep_in.bLength = sizeof(usb_endpoint_descriptor_t);
|
||||||
|
d->ep_in.bDescriptorType = USB_DESC_TYPE_ENDPOINT;
|
||||||
|
d->ep_in.bEndpointAddress = info->ep_in;
|
||||||
|
d->ep_in.bmAttributes = USBD_EP_TYPE_INTR;
|
||||||
|
d->ep_in.wMaxPacketSize = info->max_packet_len;
|
||||||
|
d->ep_in.bInterval = info->polling_interval;
|
||||||
|
|
||||||
|
// OUT endpoint (receiving)
|
||||||
|
d->ep_out.bLength = sizeof(usb_endpoint_descriptor_t);
|
||||||
|
d->ep_out.bDescriptorType = USB_DESC_TYPE_ENDPOINT;
|
||||||
|
d->ep_out.bEndpointAddress = info->ep_out;
|
||||||
|
d->ep_out.bmAttributes = USBD_EP_TYPE_INTR;
|
||||||
|
d->ep_out.wMaxPacketSize = info->max_packet_len;
|
||||||
|
d->ep_out.bInterval = info->polling_interval;
|
||||||
|
|
||||||
|
// Config descriptor
|
||||||
|
usb_desc_add_iface(sizeof(usb_webusb_descriptor_block_t));
|
||||||
|
|
||||||
|
// Interface state
|
||||||
|
iface->type = USB_IFACE_TYPE_WEBUSB;
|
||||||
|
iface->webusb.desc_block = d;
|
||||||
|
iface->webusb.rx_buffer = info->rx_buffer;
|
||||||
|
iface->webusb.ep_in = info->ep_in;
|
||||||
|
iface->webusb.ep_out = info->ep_out;
|
||||||
|
iface->webusb.max_packet_len = info->max_packet_len;
|
||||||
|
iface->webusb.protocol = 0;
|
||||||
|
iface->webusb.idle_rate = 0;
|
||||||
|
iface->webusb.alt_setting = 0;
|
||||||
|
iface->webusb.last_read_len = 0;
|
||||||
|
iface->webusb.ep_in_is_idle = 1;
|
||||||
|
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool usb_webusb_can_read(uint8_t iface_num) {
|
||||||
|
usb_iface_t *iface = usb_get_iface(iface_num);
|
||||||
|
if (iface == NULL) {
|
||||||
|
return secfalse; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (iface->type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return secfalse; // Invalid interface type
|
||||||
|
}
|
||||||
|
if (iface->webusb.last_read_len == 0) {
|
||||||
|
return secfalse; // Nothing in the receiving buffer
|
||||||
|
}
|
||||||
|
if (usb_dev_handle.dev_state != USBD_STATE_CONFIGURED) {
|
||||||
|
return secfalse; // Device is not configured
|
||||||
|
}
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool usb_webusb_can_write(uint8_t iface_num) {
|
||||||
|
usb_iface_t *iface = usb_get_iface(iface_num);
|
||||||
|
if (iface == NULL) {
|
||||||
|
return secfalse; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (iface->type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return secfalse; // Invalid interface type
|
||||||
|
}
|
||||||
|
if (iface->webusb.ep_in_is_idle == 0) {
|
||||||
|
return secfalse; // Last transmission is not over yet
|
||||||
|
}
|
||||||
|
if (usb_dev_handle.dev_state != USBD_STATE_CONFIGURED) {
|
||||||
|
return secfalse; // Device is not configured
|
||||||
|
}
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
||||||
|
usb_iface_t *iface = usb_get_iface(iface_num);
|
||||||
|
if (iface == NULL) {
|
||||||
|
return -1; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (iface->type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return -2; // Invalid interface type
|
||||||
|
}
|
||||||
|
usb_webusb_state_t *state = &iface->webusb;
|
||||||
|
|
||||||
|
// Copy maximum possible amount of data and truncate the buffer length
|
||||||
|
if (len < state->last_read_len) {
|
||||||
|
return 0; // Not enough data in the read buffer
|
||||||
|
}
|
||||||
|
len = state->last_read_len;
|
||||||
|
state->last_read_len = 0;
|
||||||
|
memcpy(buf, state->rx_buffer, len);
|
||||||
|
|
||||||
|
// Clear NAK to indicate we are ready to read more data
|
||||||
|
usb_ep_clear_nak(&usb_dev_handle, state->ep_out);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
||||||
|
usb_iface_t *iface = usb_get_iface(iface_num);
|
||||||
|
if (iface == NULL) {
|
||||||
|
return -1; // Invalid interface number
|
||||||
|
}
|
||||||
|
if (iface->type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return -2; // Invalid interface type
|
||||||
|
}
|
||||||
|
usb_webusb_state_t *state = &iface->webusb;
|
||||||
|
|
||||||
|
state->ep_in_is_idle = 0;
|
||||||
|
USBD_LL_Transmit(&usb_dev_handle, state->ep_in, UNCONST(buf), (uint16_t)len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_read_select(uint32_t timeout) {
|
||||||
|
const uint32_t start = HAL_GetTick();
|
||||||
|
for (;;) {
|
||||||
|
for (int i = 0; i < USBD_MAX_NUM_INTERFACES; i++) {
|
||||||
|
if (sectrue == usb_webusb_can_read(i)) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (HAL_GetTick() - start >= timeout) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
__WFI(); // Enter sleep mode, waiting for interrupt
|
||||||
|
}
|
||||||
|
return -1; // Timeout
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_read_blocking(uint8_t iface_num, uint8_t *buf, uint32_t len, int timeout) {
|
||||||
|
const uint32_t start = HAL_GetTick();
|
||||||
|
while (sectrue != usb_webusb_can_read(iface_num)) {
|
||||||
|
if (timeout >= 0 && HAL_GetTick() - start >= timeout) {
|
||||||
|
return 0; // Timeout
|
||||||
|
}
|
||||||
|
__WFI(); // Enter sleep mode, waiting for interrupt
|
||||||
|
}
|
||||||
|
return usb_webusb_read(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_write_blocking(uint8_t iface_num, const uint8_t *buf, uint32_t len, int timeout) {
|
||||||
|
const uint32_t start = HAL_GetTick();
|
||||||
|
while (sectrue != usb_webusb_can_write(iface_num)) {
|
||||||
|
if (timeout >= 0 && HAL_GetTick() - start >= timeout) {
|
||||||
|
return 0; // Timeout
|
||||||
|
}
|
||||||
|
__WFI(); // Enter sleep mode, waiting for interrupt
|
||||||
|
}
|
||||||
|
return usb_webusb_write(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_webusb_class_init(USBD_HandleTypeDef *dev, usb_webusb_state_t *state, uint8_t cfg_idx) {
|
||||||
|
// Open endpoints
|
||||||
|
USBD_LL_OpenEP(dev, state->ep_in, USBD_EP_TYPE_INTR, state->max_packet_len);
|
||||||
|
USBD_LL_OpenEP(dev, state->ep_out, USBD_EP_TYPE_INTR, state->max_packet_len);
|
||||||
|
|
||||||
|
// Reset the state
|
||||||
|
state->protocol = 0;
|
||||||
|
state->idle_rate = 0;
|
||||||
|
state->alt_setting = 0;
|
||||||
|
state->last_read_len = 0;
|
||||||
|
state->ep_in_is_idle = 1;
|
||||||
|
|
||||||
|
// Prepare the OUT EP to receive next packet
|
||||||
|
USBD_LL_PrepareReceive(dev, state->ep_out, state->rx_buffer, state->max_packet_len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_webusb_class_deinit(USBD_HandleTypeDef *dev, usb_webusb_state_t *state, uint8_t cfg_idx) {
|
||||||
|
// Flush endpoints
|
||||||
|
USBD_LL_FlushEP(dev, state->ep_in);
|
||||||
|
USBD_LL_FlushEP(dev, state->ep_out);
|
||||||
|
// Close endpoints
|
||||||
|
USBD_LL_CloseEP(dev, state->ep_in);
|
||||||
|
USBD_LL_CloseEP(dev, state->ep_out);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_webusb_class_setup(USBD_HandleTypeDef *dev, usb_webusb_state_t *state, USBD_SetupReqTypedef *req) {
|
||||||
|
|
||||||
|
static const char url[] = {
|
||||||
|
3 + 15, // uint8_t bLength
|
||||||
|
USB_WEBUSB_DESCRIPTOR_TYPE_URL, // uint8_t bDescriptorType
|
||||||
|
USB_WEBUSB_URL_SCHEME_HTTPS, // uint8_t bScheme
|
||||||
|
't', 'r', 'e', 'z', 'o', 'r', '.', 'i', 'o', '/', 's', 't', 'a', 'r', 't', // char URL[]
|
||||||
|
};
|
||||||
|
|
||||||
|
switch (req->bmRequest & USB_REQ_TYPE_MASK) {
|
||||||
|
|
||||||
|
case USB_REQ_TYPE_CLASS: // Class request
|
||||||
|
|
||||||
|
switch (req->bRequest) {
|
||||||
|
case USB_WEBUSB_REQ_SET_PROTOCOL:
|
||||||
|
state->protocol = req->wValue;
|
||||||
|
break;
|
||||||
|
case USB_WEBUSB_REQ_GET_PROTOCOL:
|
||||||
|
USBD_CtlSendData(dev, &state->protocol, sizeof(state->protocol));
|
||||||
|
break;
|
||||||
|
case USB_WEBUSB_REQ_SET_IDLE:
|
||||||
|
state->idle_rate = req->wValue >> 8;
|
||||||
|
break;
|
||||||
|
case USB_WEBUSB_REQ_GET_IDLE:
|
||||||
|
USBD_CtlSendData(dev, &state->idle_rate, sizeof(state->idle_rate));
|
||||||
|
break;
|
||||||
|
case USB_WEBUSB_VENDOR_CODE:
|
||||||
|
switch (req->wIndex) {
|
||||||
|
case USB_WEBUSB_REQ_GET_URL:
|
||||||
|
// we should check whether req->wValue == USB_WEBUSB_LANDING_PAGE,
|
||||||
|
// but let's return always the same url for all indexes
|
||||||
|
USBD_CtlSendData(dev, (uint8_t *)url, sizeof(url));
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
USBD_CtlError(dev, req);
|
||||||
|
return USBD_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
USBD_CtlError(dev, req);
|
||||||
|
return USBD_FAIL;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case USB_REQ_TYPE_STANDARD: // Interface & Endpoint request
|
||||||
|
|
||||||
|
switch (req->bRequest) {
|
||||||
|
case USB_REQ_SET_INTERFACE:
|
||||||
|
state->alt_setting = req->wValue;
|
||||||
|
break;
|
||||||
|
case USB_REQ_GET_INTERFACE:
|
||||||
|
USBD_CtlSendData(dev, &state->alt_setting, sizeof(state->alt_setting));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return USBD_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_webusb_class_data_in(USBD_HandleTypeDef *dev, usb_webusb_state_t *state, uint8_t ep_num) {
|
||||||
|
if ((ep_num | USB_EP_DIR_IN) == state->ep_in) {
|
||||||
|
state->ep_in_is_idle = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void usb_webusb_class_data_out(USBD_HandleTypeDef *dev, usb_webusb_state_t *state, uint8_t ep_num) {
|
||||||
|
if (ep_num == state->ep_out) {
|
||||||
|
state->last_read_len = USBD_LL_GetRxDataSize(dev, ep_num);
|
||||||
|
|
||||||
|
// Prepare the OUT EP to receive next packet
|
||||||
|
// User should provide state->rx_buffer that is big enough for state->max_packet_len bytes
|
||||||
|
USBD_LL_PrepareReceive(dev, ep_num, state->rx_buffer, state->max_packet_len);
|
||||||
|
|
||||||
|
if (state->last_read_len > 0) {
|
||||||
|
// Block the OUT EP until we process received data
|
||||||
|
usb_ep_set_nak(dev, ep_num);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -68,6 +68,7 @@
|
|||||||
#define USBD_SUPPORT_USER_STRING 0
|
#define USBD_SUPPORT_USER_STRING 0
|
||||||
#define USBD_SELF_POWERED 0
|
#define USBD_SELF_POWERED 0
|
||||||
#define USBD_DEBUG_LEVEL 0
|
#define USBD_DEBUG_LEVEL 0
|
||||||
|
#define USBD_LPM_ENABLED 1
|
||||||
|
|
||||||
/* Exported macro ------------------------------------------------------------*/
|
/* Exported macro ------------------------------------------------------------*/
|
||||||
/* Memory management macros */
|
/* Memory management macros */
|
||||||
|
@ -99,7 +99,10 @@
|
|||||||
#define USB_DESC_TYPE_ENDPOINT 5
|
#define USB_DESC_TYPE_ENDPOINT 5
|
||||||
#define USB_DESC_TYPE_DEVICE_QUALIFIER 6
|
#define USB_DESC_TYPE_DEVICE_QUALIFIER 6
|
||||||
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7
|
#define USB_DESC_TYPE_OTHER_SPEED_CONFIGURATION 7
|
||||||
#define USB_DESC_TYPE_BOS 0x0F
|
#define USB_DESC_TYPE_BOS 15
|
||||||
|
#define USB_DESC_TYPE_DEVICE_CAPABILITY 16
|
||||||
|
|
||||||
|
#define USB_DEVICE_CAPABILITY_PLATFORM 5
|
||||||
|
|
||||||
#define USB_CONFIG_REMOTE_WAKEUP 2
|
#define USB_CONFIG_REMOTE_WAKEUP 2
|
||||||
#define USB_CONFIG_SELF_POWERED 1
|
#define USB_CONFIG_SELF_POWERED 1
|
||||||
@ -108,8 +111,6 @@
|
|||||||
#define USB_FEATURE_REMOTE_WAKEUP 1
|
#define USB_FEATURE_REMOTE_WAKEUP 1
|
||||||
#define USB_FEATURE_TEST_MODE 2
|
#define USB_FEATURE_TEST_MODE 2
|
||||||
|
|
||||||
#define USB_DEVICE_CAPABILITY_TYPE 0x10
|
|
||||||
|
|
||||||
#define USB_HS_MAX_PACKET_SIZE 512
|
#define USB_HS_MAX_PACKET_SIZE 512
|
||||||
#define USB_FS_MAX_PACKET_SIZE 64
|
#define USB_FS_MAX_PACKET_SIZE 64
|
||||||
#define USB_MAX_EP0_SIZE 64
|
#define USB_MAX_EP0_SIZE 64
|
||||||
|
135
embed/unix/usb.c
135
embed/unix/usb.c
@ -20,48 +20,68 @@ void __attribute__((noreturn)) __fatal_error(const char *expr, const char *msg,
|
|||||||
|
|
||||||
#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
|
#define ensure(expr, msg) (((expr) == sectrue) ? (void)0 : __fatal_error(#expr, msg, __FILE__, __LINE__, __func__))
|
||||||
|
|
||||||
#define TREZOR_UDP_IFACE 0
|
// emulator opens UDP server on TREZOR_UDP_PORT port
|
||||||
#define TREZOR_UDP_PORT 21324
|
// and emulates HID/WebUSB interface TREZOR_UDP_IFACE
|
||||||
|
// gracefully ignores all other USB interfaces
|
||||||
|
|
||||||
static int sock;
|
#define TREZOR_UDP_IFACE 0
|
||||||
|
#define TREZOR_UDP_PORT 21324
|
||||||
|
|
||||||
|
static usb_iface_type_t emulator_usb_iface_type = USB_IFACE_TYPE_DISABLED;
|
||||||
|
|
||||||
|
static int sock = -1;
|
||||||
static struct sockaddr_in si_me, si_other;
|
static struct sockaddr_in si_me, si_other;
|
||||||
static socklen_t slen = 0;
|
static socklen_t slen = 0;
|
||||||
|
|
||||||
void usb_init(const usb_dev_info_t *dev_info) {
|
void usb_init(const usb_dev_info_t *dev_info) {
|
||||||
(void)dev_info;
|
(void)dev_info;
|
||||||
|
|
||||||
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
|
||||||
ensure(sectrue * (sock >= 0), NULL);
|
|
||||||
|
|
||||||
fcntl(sock, F_SETFL, O_NONBLOCK);
|
|
||||||
|
|
||||||
si_me.sin_family = AF_INET;
|
|
||||||
const char *ip = getenv("TREZOR_UDP_IP");
|
|
||||||
if (ip) {
|
|
||||||
si_me.sin_addr.s_addr = inet_addr(ip);
|
|
||||||
} else {
|
|
||||||
si_me.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
|
||||||
}
|
|
||||||
const char *port = getenv("TREZOR_UDP_PORT");
|
|
||||||
if (port) {
|
|
||||||
si_me.sin_port = htons(atoi(port));
|
|
||||||
} else {
|
|
||||||
si_me.sin_port = htons(TREZOR_UDP_PORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
ensure(sectrue * (0 == bind(sock, (struct sockaddr*)&si_me, sizeof(si_me))), NULL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_deinit(void) {
|
void usb_deinit(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_start(void) {
|
void usb_start(void) {
|
||||||
|
// start server only if interface 0 is either HID or WebUSB
|
||||||
|
if (emulator_usb_iface_type == USB_IFACE_TYPE_HID || emulator_usb_iface_type == USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||||
|
ensure(sectrue * (sock >= 0), NULL);
|
||||||
|
|
||||||
|
fcntl(sock, F_SETFL, O_NONBLOCK);
|
||||||
|
|
||||||
|
si_me.sin_family = AF_INET;
|
||||||
|
const char *ip = getenv("TREZOR_UDP_IP");
|
||||||
|
if (ip) {
|
||||||
|
si_me.sin_addr.s_addr = inet_addr(ip);
|
||||||
|
} else {
|
||||||
|
si_me.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
|
||||||
|
}
|
||||||
|
const char *port = getenv("TREZOR_UDP_PORT");
|
||||||
|
if (port) {
|
||||||
|
si_me.sin_port = htons(atoi(port));
|
||||||
|
} else {
|
||||||
|
si_me.sin_port = htons(TREZOR_UDP_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(sectrue * (0 == bind(sock, (struct sockaddr*)&si_me, sizeof(si_me))), NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void usb_stop(void) {
|
void usb_stop(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool usb_hid_add(const usb_hid_info_t *info) {
|
secbool usb_hid_add(const usb_hid_info_t *info) {
|
||||||
|
// store iface type if it is the emulated iface
|
||||||
|
if (info->iface_num == TREZOR_UDP_IFACE) {
|
||||||
|
emulator_usb_iface_type = USB_IFACE_TYPE_HID;
|
||||||
|
}
|
||||||
|
return sectrue;
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool usb_webusb_add(const usb_webusb_info_t *info) {
|
||||||
|
// store iface type if it is the emulated iface
|
||||||
|
if (info->iface_num == TREZOR_UDP_IFACE) {
|
||||||
|
emulator_usb_iface_type = USB_IFACE_TYPE_WEBUSB;
|
||||||
|
}
|
||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,10 +89,7 @@ secbool usb_vcp_add(const usb_vcp_info_t *info) {
|
|||||||
return sectrue;
|
return sectrue;
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool usb_hid_can_read(uint8_t iface_num) {
|
static secbool usb_emulated_can_read(uint8_t iface_num) {
|
||||||
if (iface_num != TREZOR_UDP_IFACE) {
|
|
||||||
return secfalse;
|
|
||||||
}
|
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
{ sock, POLLIN, 0 },
|
{ sock, POLLIN, 0 },
|
||||||
};
|
};
|
||||||
@ -80,10 +97,21 @@ secbool usb_hid_can_read(uint8_t iface_num) {
|
|||||||
return sectrue * (r > 0);
|
return sectrue * (r > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
secbool usb_hid_can_write(uint8_t iface_num) {
|
secbool usb_hid_can_read(uint8_t iface_num) {
|
||||||
if (iface_num != TREZOR_UDP_IFACE) {
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_HID) {
|
||||||
return secfalse;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
return usb_emulated_can_read(iface_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool usb_webusb_can_read(uint8_t iface_num) {
|
||||||
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
return usb_emulated_can_read(iface_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static secbool usb_emulated_can_write(uint8_t iface_num) {
|
||||||
struct pollfd fds[] = {
|
struct pollfd fds[] = {
|
||||||
{ sock, POLLOUT, 0 },
|
{ sock, POLLOUT, 0 },
|
||||||
};
|
};
|
||||||
@ -91,10 +119,21 @@ secbool usb_hid_can_write(uint8_t iface_num) {
|
|||||||
return sectrue * (r > 0);
|
return sectrue * (r > 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
secbool usb_hid_can_write(uint8_t iface_num) {
|
||||||
if (iface_num != TREZOR_UDP_IFACE) {
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_HID) {
|
||||||
return 0;
|
return secfalse;
|
||||||
}
|
}
|
||||||
|
return usb_emulated_can_write(iface_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
secbool usb_webusb_can_write(uint8_t iface_num) {
|
||||||
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return secfalse;
|
||||||
|
}
|
||||||
|
return usb_emulated_can_write(iface_num);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_emulated_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
||||||
struct sockaddr_in si;
|
struct sockaddr_in si;
|
||||||
socklen_t sl = sizeof(si);
|
socklen_t sl = sizeof(si);
|
||||||
ssize_t r = recvfrom(sock, buf, len, MSG_DONTWAIT, (struct sockaddr *)&si, &sl);
|
ssize_t r = recvfrom(sock, buf, len, MSG_DONTWAIT, (struct sockaddr *)&si, &sl);
|
||||||
@ -114,10 +153,22 @@ int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
int usb_hid_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
||||||
if (iface_num != TREZOR_UDP_IFACE) {
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_HID) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
return usb_emulated_read(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_read(uint8_t iface_num, uint8_t *buf, uint32_t len) {
|
||||||
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usb_emulated_read(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
static int usb_emulated_write(uint8_t iface_num, const uint8_t *buf, uint32_t len)
|
||||||
|
{
|
||||||
ssize_t r = len;
|
ssize_t r = len;
|
||||||
if (slen > 0) {
|
if (slen > 0) {
|
||||||
r = sendto(sock, buf, len, MSG_DONTWAIT, (const struct sockaddr *)&si_other, slen);
|
r = sendto(sock, buf, len, MSG_DONTWAIT, (const struct sockaddr *)&si_other, slen);
|
||||||
@ -125,6 +176,20 @@ int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int usb_hid_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
||||||
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_HID) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usb_emulated_write(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
int usb_webusb_write(uint8_t iface_num, const uint8_t *buf, uint32_t len) {
|
||||||
|
if (iface_num != TREZOR_UDP_IFACE || emulator_usb_iface_type != USB_IFACE_TYPE_WEBUSB) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
return usb_emulated_write(iface_num, buf, len);
|
||||||
|
}
|
||||||
|
|
||||||
void pendsv_kbd_intr(void) {
|
void pendsv_kbd_intr(void) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
1
embed/unix/usb_webusb-defs.h
Symbolic link
1
embed/unix/usb_webusb-defs.h
Symbolic link
@ -0,0 +1 @@
|
|||||||
|
../trezorhal/usb_webusb-defs.h
|
24
src/main.py
24
src/main.py
@ -6,11 +6,19 @@ from trezor import loop
|
|||||||
from trezor import wire
|
from trezor import wire
|
||||||
from trezor import workflow
|
from trezor import workflow
|
||||||
|
|
||||||
|
USE_WEBUSB = False
|
||||||
|
|
||||||
log.level = log.DEBUG
|
log.level = log.DEBUG
|
||||||
|
|
||||||
# initialize the USB stack
|
# initialize the USB stack
|
||||||
|
|
||||||
if True:
|
if USE_WEBUSB:
|
||||||
|
usb_wire = io.WebUSB(
|
||||||
|
iface_num=0,
|
||||||
|
ep_in=0x81,
|
||||||
|
ep_out=0x01,
|
||||||
|
)
|
||||||
|
else:
|
||||||
usb_wire = io.HID(
|
usb_wire = io.HID(
|
||||||
iface_num=0,
|
iface_num=0,
|
||||||
ep_in=0x81,
|
ep_in=0x81,
|
||||||
@ -60,7 +68,13 @@ usb_u2f = io.HID(
|
|||||||
)
|
)
|
||||||
|
|
||||||
if __debug__:
|
if __debug__:
|
||||||
if True:
|
if USE_WEBUSB:
|
||||||
|
usb_debug = io.WebUSB(
|
||||||
|
iface_num=4,
|
||||||
|
ep_in=0x85,
|
||||||
|
ep_out=0x04,
|
||||||
|
)
|
||||||
|
else:
|
||||||
usb_debug = io.HID(
|
usb_debug = io.HID(
|
||||||
iface_num=4,
|
iface_num=4,
|
||||||
ep_in=0x85,
|
ep_in=0x85,
|
||||||
@ -84,6 +98,7 @@ if __debug__:
|
|||||||
0xc0, # END_COLLECTION
|
0xc0, # END_COLLECTION
|
||||||
]),
|
]),
|
||||||
)
|
)
|
||||||
|
|
||||||
usb_vcp = io.VCP(
|
usb_vcp = io.VCP(
|
||||||
iface_num=1,
|
iface_num=1,
|
||||||
data_iface_num=2,
|
data_iface_num=2,
|
||||||
@ -95,10 +110,11 @@ if __debug__:
|
|||||||
usb = io.USB(
|
usb = io.USB(
|
||||||
vendor_id=0x1209,
|
vendor_id=0x1209,
|
||||||
product_id=0x53C1,
|
product_id=0x53C1,
|
||||||
release_num=0x0002,
|
release_num=0x0200,
|
||||||
manufacturer="SatoshiLabs",
|
manufacturer="SatoshiLabs",
|
||||||
product="TREZOR",
|
product="TREZOR",
|
||||||
serial_number="000000000000000000000000",
|
serial_number="44DD6B2C788D760538A78ECA",
|
||||||
|
interface="TREZOR Interface",
|
||||||
)
|
)
|
||||||
|
|
||||||
usb.add(usb_wire)
|
usb.add(usb_wire)
|
||||||
|
Loading…
Reference in New Issue
Block a user