summaryrefslogtreecommitdiffstatshomepage
path: root/stmhal/usb.c
diff options
context:
space:
mode:
Diffstat (limited to 'stmhal/usb.c')
-rw-r--r--stmhal/usb.c269
1 files changed, 235 insertions, 34 deletions
diff --git a/stmhal/usb.c b/stmhal/usb.c
index 8ea5fb561c..220d01c4e8 100644
--- a/stmhal/usb.c
+++ b/stmhal/usb.c
@@ -34,72 +34,104 @@
#include "usbd_cdc_interface.h"
#include "usbd_msc_storage.h"
+#include "py/objstr.h"
#include "py/runtime.h"
#include "py/stream.h"
#include "bufhelper.h"
#include "usb.h"
#include "pybioctl.h"
+// this will be persistent across a soft-reset
+mp_uint_t pyb_usb_flags = 0;
+
#ifdef USE_DEVICE_MODE
USBD_HandleTypeDef hUSBDDevice;
+pyb_usb_storage_medium_t pyb_usb_storage_medium = PYB_USB_STORAGE_MEDIUM_NONE;
#endif
-STATIC int dev_is_enabled = 0;
+// predefined hid mouse data
+STATIC const mp_obj_str_t pyb_usb_hid_mouse_desc_obj = {
+ {&mp_type_bytes},
+ 0, // hash not valid
+ USBD_HID_MOUSE_REPORT_DESC_SIZE,
+ USBD_HID_MOUSE_ReportDesc,
+};
+const mp_obj_tuple_t pyb_usb_hid_mouse_obj = {
+ {&mp_type_tuple},
+ 5,
+ {
+ MP_OBJ_NEW_SMALL_INT(USBD_PID_SECONDARY),
+ MP_OBJ_NEW_SMALL_INT(1), // subclass: boot
+ MP_OBJ_NEW_SMALL_INT(2), // protocol: mouse
+ MP_OBJ_NEW_SMALL_INT(USBD_HID_MOUSE_MAX_PACKET),
+ (mp_obj_t)&pyb_usb_hid_mouse_desc_obj,
+ },
+};
+
+// predefined hid keyboard data
+STATIC const mp_obj_str_t pyb_usb_hid_keyboard_desc_obj = {
+ {&mp_type_bytes},
+ 0, // hash not valid
+ USBD_HID_KEYBOARD_REPORT_DESC_SIZE,
+ USBD_HID_KEYBOARD_ReportDesc,
+};
+const mp_obj_tuple_t pyb_usb_hid_keyboard_obj = {
+ {&mp_type_tuple},
+ 5,
+ {
+ MP_OBJ_NEW_SMALL_INT(USBD_PID_SECONDARY),
+ MP_OBJ_NEW_SMALL_INT(1), // subclass: boot
+ MP_OBJ_NEW_SMALL_INT(1), // protocol: keyboard
+ MP_OBJ_NEW_SMALL_INT(USBD_HID_KEYBOARD_MAX_PACKET),
+ (mp_obj_t)&pyb_usb_hid_keyboard_desc_obj,
+ },
+};
void pyb_usb_init0(void) {
// create an exception object for interrupting by VCP
MP_STATE_PORT(mp_const_vcp_interrupt) = mp_obj_new_exception(&mp_type_KeyboardInterrupt);
USBD_CDC_SetInterrupt(-1, MP_STATE_PORT(mp_const_vcp_interrupt));
+ MP_STATE_PORT(pyb_hid_report_desc) = MP_OBJ_NULL;
}
-void pyb_usb_dev_init(usb_device_mode_t mode, usb_storage_medium_t medium) {
+void pyb_usb_dev_init(uint16_t pid, usb_device_mode_t mode, USBD_HID_ModeInfoTypeDef *hid_info) {
#ifdef USE_DEVICE_MODE
- if (!dev_is_enabled) {
+ if (!(pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED)) {
// only init USB once in the device's power-lifetime
- // Windows needs a different PID to distinguish different device
- // configurations, so we set it here depending on mode.
- if (mode == USB_DEVICE_MODE_CDC_MSC) {
- USBD_SelectMode(USBD_MODE_CDC_MSC);
- USBD_SetPID(0x9800);
- } else {
- USBD_SelectMode(USBD_MODE_CDC_HID);
- USBD_SetPID(0x9801);
- }
+ USBD_SetPID(pid);
+ USBD_SelectMode(mode, hid_info);
USBD_Init(&hUSBDDevice, (USBD_DescriptorsTypeDef*)&VCP_Desc, 0);
USBD_RegisterClass(&hUSBDDevice, &USBD_CDC_MSC_HID);
USBD_CDC_RegisterInterface(&hUSBDDevice, (USBD_CDC_ItfTypeDef*)&USBD_CDC_fops);
+ switch (pyb_usb_storage_medium) {
#if MICROPY_HW_HAS_SDCARD
- if (medium == USB_STORAGE_MEDIUM_FLASH) {
- USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_FLASH_STORAGE_fops);
- } else {
- USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_SDCARD_STORAGE_fops);
- }
-#else
- USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_FLASH_STORAGE_fops);
+ case PYB_USB_STORAGE_MEDIUM_SDCARD:
+ USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_SDCARD_STORAGE_fops);
+ break;
#endif
+ default:
+ USBD_MSC_RegisterStorage(&hUSBDDevice, (USBD_StorageTypeDef*)&USBD_FLASH_STORAGE_fops);
+ break;
+ }
USBD_Start(&hUSBDDevice);
}
- dev_is_enabled = 1;
+ pyb_usb_flags |= PYB_USB_FLAG_DEV_ENABLED;
#endif
}
-void pyb_usb_dev_stop(void) {
- if (dev_is_enabled) {
+void pyb_usb_dev_deinit(void) {
+ if (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) {
USBD_Stop(&hUSBDDevice);
- dev_is_enabled = 0;
+ pyb_usb_flags &= ~PYB_USB_FLAG_DEV_ENABLED;
}
}
bool usb_vcp_is_enabled(void) {
- return dev_is_enabled;
-}
-
-bool usb_vcp_is_connected(void) {
- return USBD_CDC_IsConnected();
+ return (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) != 0;
}
void usb_vcp_set_interrupt_char(int c) {
- if (dev_is_enabled) {
+ if (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) {
if (c != -1) {
mp_obj_exception_clear_traceback(MP_STATE_PORT(mp_const_vcp_interrupt));
}
@@ -113,7 +145,7 @@ int usb_vcp_recv_byte(uint8_t *c) {
void usb_vcp_send_strn(const char *str, int len) {
#ifdef USE_DEVICE_MODE
- if (dev_is_enabled) {
+ if (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) {
USBD_CDC_TxAlways((const uint8_t*)str, len);
}
#endif
@@ -121,7 +153,7 @@ void usb_vcp_send_strn(const char *str, int len) {
void usb_vcp_send_strn_cooked(const char *str, int len) {
#ifdef USE_DEVICE_MODE
- if (dev_is_enabled) {
+ if (pyb_usb_flags & PYB_USB_FLAG_DEV_ENABLED) {
for (const char *top = str + len; str < top; str++) {
if (*str == '\n') {
USBD_CDC_TxAlways((const uint8_t*)"\r\n", 2);
@@ -133,11 +165,86 @@ void usb_vcp_send_strn_cooked(const char *str, int len) {
#endif
}
-void usb_hid_send_report(uint8_t *buf) {
-#ifdef USE_DEVICE_MODE
- USBD_HID_SendReport(&hUSBDDevice, buf, 4);
+/******************************************************************************/
+// Micro Python bindings for USB
+
+/*
+TODO think about how to expose the USB device. Currently we have:
+ pyb.usb_mode(None) # disable USB
+ pyb.usb_mode('CDC+MSC')
+ pyb.usb_mode('CDC+HID') # defaults to mouse
+ pyb.usb_mode('CDC+HID', pyb.hid_mouse)
+ pyb.usb_mode('CDC+HID', pyb.hid_keyboard)
+ pyb.usb_mode('CDC+HID', (pid, subclass, protocol, max_packet_len, report_desc))
+ pyb.usb_mode('host', ...)
+ vcp = pyb.USB_VCP() # get the VCP device for read/write
+ hid = pyb.USB_HID() # get the HID device for write/poll
+
+We could use a more class based approach, like UART and others:
+ usb = pyb.USB('CDC+MSC')
+ usb = pyb.USB('CDC+HID', pyb.USB.hid_mouse)
+ usb = pyb.USB('CDC+HID', pyb.USB.hid_keyboard)
+ usb = pyb.USB('CDC+HID', (pid, subclass, protocol, max_packet_len, report_desc))
+ usb = pyb.USB('host', ...)
+ usb = pyb.USB() # get currently configured object
+ vcp = usb.VCP() # get VCP device
+ hid = usb.HID() # get HID device
+*/
+
+STATIC mp_obj_t pyb_usb_mode(mp_uint_t n_args, const mp_obj_t *args) {
+ pyb_usb_flags |= PYB_USB_FLAG_USB_MODE_CALLED;
+
+ if (args[0] == mp_const_none) {
+ // disable usb
+ #if defined(USE_DEVICE_MODE)
+ pyb_usb_dev_deinit();
+ #endif
+ return mp_const_none;
+ }
+
+ const char *mode_str = mp_obj_str_get_str(args[0]);
+#if defined(USE_HOST_MODE)
+ // USB host
+ if (strcmp(mode_str, "host") == 0) {
+ pyb_usb_host_init();
+ } else {
+ goto bad_mode;
+ }
+#elif defined(USE_DEVICE_MODE)
+ // USB device
+ if (strcmp(mode_str, "CDC+MSC") == 0) {
+ pyb_usb_dev_init(USBD_PID_DEFAULT, USBD_MODE_CDC_MSC, NULL);
+ } else if (strcmp(mode_str, "CDC+HID") == 0) {
+ mp_obj_t hid_info_obj = (mp_obj_t)&pyb_usb_hid_mouse_obj; // default is mouse mode
+ if (n_args == 2) {
+ hid_info_obj = args[1];
+ }
+ mp_obj_t *items;
+ mp_obj_get_array_fixed_n(hid_info_obj, 5, &items);
+ USBD_HID_ModeInfoTypeDef hid_info;
+ mp_int_t pid = mp_obj_get_int(items[0]);
+ hid_info.subclass = mp_obj_get_int(items[1]);
+ hid_info.protocol = mp_obj_get_int(items[2]);
+ hid_info.max_packet_len = mp_obj_get_int(items[3]);
+ MP_STATE_PORT(pyb_hid_report_desc) = items[4]; // need to keep a copy of this so report_desc does not get GC'd
+ mp_buffer_info_t bufinfo;
+ mp_get_buffer_raise(items[4], &bufinfo, MP_BUFFER_READ);
+ hid_info.report_desc = bufinfo.buf;
+ hid_info.report_desc_len = bufinfo.len;
+ pyb_usb_dev_init(pid, USBD_MODE_CDC_HID, &hid_info);
+ } else {
+ goto bad_mode;
+ }
+#else
+ goto bad_mode;
#endif
+
+ return mp_const_none;
+
+bad_mode:
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "bad USB mode"));
}
+MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_usb_mode_obj, 1, 2, pyb_usb_mode);
/******************************************************************************/
// Micro Python bindings for USB VCP
@@ -175,6 +282,17 @@ STATIC mp_obj_t pyb_usb_vcp_setinterrupt(mp_obj_t self_in, mp_obj_t int_chr_in)
}
STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_usb_vcp_setinterrupt_obj, pyb_usb_vcp_setinterrupt);
+STATIC mp_obj_t pyb_usb_vcp_isconnected(mp_obj_t self_in) {
+ return MP_BOOL(USBD_CDC_IsConnected());
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_1(pyb_usb_vcp_isconnected_obj, pyb_usb_vcp_isconnected);
+
+// deprecated in favour of USB_VCP.isconnected
+STATIC mp_obj_t pyb_have_cdc(void) {
+ return pyb_usb_vcp_isconnected(MP_OBJ_NULL);
+}
+MP_DEFINE_CONST_FUN_OBJ_0(pyb_have_cdc_obj, pyb_have_cdc);
+
/// \method any()
/// Return `True` if any characters waiting, else `False`.
STATIC mp_obj_t pyb_usb_vcp_any(mp_obj_t self_in) {
@@ -254,6 +372,7 @@ STATIC MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(pyb_usb_vcp___exit___obj, 4, 4, pyb_u
STATIC const mp_map_elem_t pyb_usb_vcp_locals_dict_table[] = {
{ MP_OBJ_NEW_QSTR(MP_QSTR_setinterrupt), (mp_obj_t)&pyb_usb_vcp_setinterrupt_obj },
+ { MP_OBJ_NEW_QSTR(MP_QSTR_isconnected), (mp_obj_t)&pyb_usb_vcp_isconnected_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_any), (mp_obj_t)&pyb_usb_vcp_any_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_usb_vcp_send_obj },
{ MP_OBJ_NEW_QSTR(MP_QSTR_recv), (mp_obj_t)&pyb_usb_vcp_recv_obj },
@@ -330,6 +449,88 @@ const mp_obj_type_t pyb_usb_vcp_type = {
};
/******************************************************************************/
+// Micro Python bindings for USB HID
+
+typedef struct _pyb_usb_hid_obj_t {
+ mp_obj_base_t base;
+} pyb_usb_hid_obj_t;
+
+STATIC const pyb_usb_hid_obj_t pyb_usb_hid_obj = {{&pyb_usb_hid_type}};
+
+STATIC mp_obj_t pyb_usb_hid_make_new(mp_obj_t type_in, mp_uint_t n_args, mp_uint_t n_kw, const mp_obj_t *args) {
+ // check arguments
+ mp_arg_check_num(n_args, n_kw, 0, 0, false);
+
+ // return the USB HID object
+ return (mp_obj_t)&pyb_usb_hid_obj;
+}
+
+STATIC mp_obj_t pyb_usb_hid_send(mp_obj_t self_in, mp_obj_t report_in) {
+#ifdef USE_DEVICE_MODE
+ mp_buffer_info_t bufinfo;
+ byte temp_buf[8];
+ // get the buffer to send from
+ // we accept either a byte array, or a tuple/list of integers
+ if (!mp_get_buffer(report_in, &bufinfo, MP_BUFFER_READ)) {
+ mp_obj_t *items;
+ mp_obj_get_array(report_in, &bufinfo.len, &items);
+ if (bufinfo.len > sizeof(temp_buf)) {
+ nlr_raise(mp_obj_new_exception_msg(&mp_type_ValueError, "tuple/list too large for HID report; use bytearray instead"));
+ }
+ for (int i = 0; i < bufinfo.len; i++) {
+ temp_buf[i] = mp_obj_get_int(items[i]);
+ }
+ bufinfo.buf = temp_buf;
+ }
+
+ // send the data
+ USBD_HID_SendReport(&hUSBDDevice, bufinfo.buf, bufinfo.len);
+#endif
+
+ return mp_const_none;
+}
+STATIC MP_DEFINE_CONST_FUN_OBJ_2(pyb_usb_hid_send_obj, pyb_usb_hid_send);
+
+// deprecated in favour of USB_HID.send
+STATIC mp_obj_t pyb_hid_send_report(mp_obj_t arg) {
+ return pyb_usb_hid_send(MP_OBJ_NULL, arg);
+}
+MP_DEFINE_CONST_FUN_OBJ_1(pyb_hid_send_report_obj, pyb_hid_send_report);
+
+STATIC const mp_map_elem_t pyb_usb_hid_locals_dict_table[] = {
+ { MP_OBJ_NEW_QSTR(MP_QSTR_send), (mp_obj_t)&pyb_usb_hid_send_obj },
+};
+
+STATIC MP_DEFINE_CONST_DICT(pyb_usb_hid_locals_dict, pyb_usb_hid_locals_dict_table);
+
+STATIC mp_uint_t pyb_usb_hid_ioctl(mp_obj_t self_in, mp_uint_t request, mp_uint_t arg, int *errcode) {
+ mp_uint_t ret;
+ if (request == MP_IOCTL_POLL) {
+ mp_uint_t flags = arg;
+ ret = 0;
+ if ((flags & MP_IOCTL_POLL_WR) && USBD_HID_CanSendReport(&hUSBDDevice)) {
+ ret |= MP_IOCTL_POLL_WR;
+ }
+ } else {
+ *errcode = EINVAL;
+ ret = MP_STREAM_ERROR;
+ }
+ return ret;
+}
+
+STATIC const mp_stream_p_t pyb_usb_hid_stream_p = {
+ .ioctl = pyb_usb_hid_ioctl,
+};
+
+const mp_obj_type_t pyb_usb_hid_type = {
+ { &mp_type_type },
+ .name = MP_QSTR_USB_HID,
+ .make_new = pyb_usb_hid_make_new,
+ .stream_p = &pyb_usb_hid_stream_p,
+ .locals_dict = (mp_obj_t)&pyb_usb_hid_locals_dict,
+};
+
+/******************************************************************************/
// code for experimental USB OTG support
#ifdef USE_HOST_MODE