diff --git a/CHANGELOG.md b/CHANGELOG.md index b35050cb9ea9b3d0c317d855da707de4cd669c16..1951bda2c6af8670d8bd6ff2b1fa18407c6f311c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,35 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). ## [Unreleased] +## [v1.7] - 2019-08-24 21:48 - [Garlic] +[Garlic]: https://card10.badge.events.ccc.de/release/card10-v1.7-Garlic.zip + +### Added +- **ESB**: Epic Serial Bus (Better than USB!), stability improvements of the + USB module. Preparation for mass-storage access in the Firmware. +- Enabled the Hardware Watchdog; Card10 will reset itself if the firmware crashes +- Log messages when BLE is pairing / connected. +- The name of the offending app is printed to the serial console, if an app + crashes the metatdata parser. + +### Changed +- Improved log messages in cases of lock-contention. +- Menu will show an error message if a crash occurs. + +### Fixed +- Fixed race-conditions in serial writes by using a queue. +- "Card10 Nickname" crashing if only `nickname.txt` exists. +- Lockup when debug prints are enabled. +- Delayed BHI160 startup a bit so the PMIC task can check the battery first. +- Relaxed the PMIC lock-timeouts so other task can take a little more time. +- Fixed off-by-one error in `gfx_line()`. +- Fixed the API interrupts sometimes getting stuck. +- Fixed binary building on MacOS. +- Fixed race-conditions in serial console prints by introducing a queue. +- Fixed API & MAX30001 mutexes being initialized late sometimes. +- Fixed wrong stripe width in bi flag. + + ## [v1.6] - 2019-08-23 20:30 - [Fennel] [Fennel]: https://card10.badge.events.ccc.de/release/card10-v1.6-Fennel.zip @@ -124,7 +153,8 @@ fbf7c8c0 fix(menu.py) Refactored menu.py based on !138 ## [v1.0] - 2019-08-21 00:50 Initial release. -[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...master +[Unreleased]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.7...master +[v1.7]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.6...v1.7 [v1.6]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.5...v1.6 [v1.5]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.4...v1.5 [v1.4]: https://git.card10.badge.events.ccc.de/card10/firmware/compare/v1.3...v1.4 diff --git a/Documentation/bluetooth/file-transfer.rst b/Documentation/bluetooth/file-transfer.rst index 439237b883ea69a2655f30511612949c64ab4b95..49191d2da8f25e610d0ef9f96696b73692a2d74f 100644 --- a/Documentation/bluetooth/file-transfer.rst +++ b/Documentation/bluetooth/file-transfer.rst @@ -77,9 +77,11 @@ CHUNK_ACK: ===== === 0 1-4 ----- --- - C CRC + C CRC(*) ===== === +CRC32 of the whole CHUNK packet including first byte, offset and payload. + FINISH: === === diff --git a/Documentation/conf.py b/Documentation/conf.py index ae5361ac408293b054274b0051bbad963da2a738..bb211aa0cf679dd8486cc21ed5bcbc56418a3d7f 100644 --- a/Documentation/conf.py +++ b/Documentation/conf.py @@ -88,9 +88,11 @@ html_context = { # -- Options for Auto-Doc ---------------------------------------------------- {{{ autodoc_mock_imports = [ + "buttons", + "interrupt", "sys_display", "sys_leds", - "buttons", + "sys_max30001", "ucollections", "urandom", "utime", diff --git a/Documentation/how-to-build.rst b/Documentation/how-to-build.rst index 1db20eda46d7496588a15b56576b9f2a071821b3..0b616ab2624888f18b7460221d229e6a3513a57d 100644 --- a/Documentation/how-to-build.rst +++ b/Documentation/how-to-build.rst @@ -28,20 +28,23 @@ Dependencies .. code-block:: shell-session dnf install arm-none-eabi-gcc arm-none-eabi-binutils arm-none-eabi-newlib - + - macOS (Note: The card10 firmware team used Linux so far. macOS recommendations here are experimental.) - - You can use `Homebrew`_ to install the required tools. - The version of the Arm crosscompiler tool chain is quite important; with the wrong version, e.g. strip and/or ld might throw strange errors. - + + You can use `Homebrew`_ to install the required tools. The version of the + ARM crosscompiler tool chain is quite important; with the wrong version, + e.g. strip and/or ld might throw strange errors. + .. code-block:: shell-session - + brew tap px4/px4 brew install px4/px4/gcc-arm-none-eabi-63 brew install coreutils + .. _Homebrew: https://brew.sh/ + - Alternative: Download `ARM's GNU toolchain`_. **TODO** -.. _Homebrew: https://brew.sh/ + * **python3**: For meson and various scripts needed for building. * **meson** (>0.43.0) & **ninja**: Unfortunately most distros only have very old versions @@ -140,3 +143,13 @@ In order to do a rebuild you can issue a clean command to ninja via $ ninja -C build/ -t clean Otherwise, rerunning ``./bootstrap.sh`` will also clean the build-directory. + +.. note:: + + If you try to flash pycardium_epicardium.bin (renamed to card10.bin) + and the bootloader does not finish updating, the file might be too large. + ~700kB is the normal size, but problems were reported where the file size + was >1MB. This was caused by the ``tr`` tool in the build process + (it's supposed to create a large file with 0xff in it) - this requires the + LC_ALL environment variable to be not set, or set to "C" + (but not UTF8 or similar). \ No newline at end of file diff --git a/Documentation/pycardium/max30001.rst b/Documentation/pycardium/max30001.rst index 1fb0ddb954a53dfd04282e16aa549a2de5a9a726..5eccd6a448ddda30d663dedbaab266f3bfd89d74 100644 --- a/Documentation/pycardium/max30001.rst +++ b/Documentation/pycardium/max30001.rst @@ -1,5 +1,5 @@ ``max30001`` - MAX30001 -===================== +======================= .. automodule:: max30001 :members: diff --git a/bootloader/build_multi_image.sh b/bootloader/build_multi_image.sh index e6ae042a08dcd94205f55977086869b20f50299b..b584dfcc5ff92411275f7ced9f9a74c81ce45c0a 100755 --- a/bootloader/build_multi_image.sh +++ b/bootloader/build_multi_image.sh @@ -6,7 +6,7 @@ BIN1="$2" BIN2="$3" BINOUT="$4" -dd if=/dev/zero ibs=1k count=448 2>/dev/null | LANG=C LC_CTYPE=C tr "\000" "\377" > "$BINOUT" +dd if=/dev/zero ibs=1k count=448 2>/dev/null | LANG=C LC_CTYPE=C LC_ALL=C LC_COLLATE=C tr "\000" "\377" > "$BINOUT" dd if="$BIN1" of="$BINOUT" conv=notrunc 2>/dev/null dd if="$BIN2" >> "$BINOUT" 2>/dev/null diff --git a/epicardium/FreeRTOSConfig.h b/epicardium/FreeRTOSConfig.h index b92b12b9ec223da13082f23fcad849a1337ba316..f1d3aa46915e9c4373d281a1a4ab37707a918bf9 100644 --- a/epicardium/FreeRTOSConfig.h +++ b/epicardium/FreeRTOSConfig.h @@ -52,7 +52,7 @@ #define INCLUDE_vTaskSuspend 1 #define INCLUDE_vTaskDelay 1 #define INCLUDE_uxTaskGetStackHighWaterMark 1 - +#define INCLUDE_xTimerPendFunctionCall 1 /* Allow static allocation of data structures */ #define configSUPPORT_STATIC_ALLOCATION 1 diff --git a/epicardium/api/common.h b/epicardium/api/common.h index ff0c9f3abf4da6822ca89c890c3a6bb55fab7ce6..5ebb29431fa09580fe44875ee0f76f908a21180a 100644 --- a/epicardium/api/common.h +++ b/epicardium/api/common.h @@ -38,7 +38,7 @@ struct api_call_mem { api_id_t id; /* ID of the current interrupt */ - api_int_id_t int_id; + volatile api_int_id_t int_id; /* * Buffer for arguments/return value. This buffer will be diff --git a/epicardium/ble/app/app_main.c b/epicardium/ble/app/app_main.c index f795c4e7222adfeb7cbe5b19e15f3c1659b7827c..b279c758bb5857eb3d649fe7cc8c36f7e16ef35f 100644 --- a/epicardium/ble/app/app_main.c +++ b/epicardium/ble/app/app_main.c @@ -37,6 +37,8 @@ #include "app_main.h" #include "app_ui.h" +#include "modules/log.h" + /************************************************************************************************** Global Variables **************************************************************************************************/ @@ -283,6 +285,8 @@ void AppHandleNumericComparison(dmSecCnfIndEvt_t *pCnfInd) { uint32_t confirm = DmSecGetCompareValue(pCnfInd->confirm); + LOG_INFO("ble", "Confirm Value: %ld", confirm); + /* display confirmation value */ AppUiDisplayConfirmValue(confirm); diff --git a/epicardium/ble/ble_main.c b/epicardium/ble/ble_main.c index af041f9cfd7c27fcd69774d3d7a9eaf882c990c6..07783d8619cc05c688db056ec0d17d06b72413dc 100644 --- a/epicardium/ble/ble_main.c +++ b/epicardium/ble/ble_main.c @@ -38,6 +38,8 @@ #include "hrps/hrps_api.h" #include "rscp/rscp_api.h" +#include "modules/log.h" + /************************************************************************************************** Macros **************************************************************************************************/ @@ -373,6 +375,7 @@ static void bleSetup(bleMsg_t *pMsg) static void bleProcMsg(bleMsg_t *pMsg) { uint8_t uiEvent = APP_UI_NONE; + hciLeConnCmplEvt_t *connOpen; switch(pMsg->hdr.event) { @@ -395,36 +398,88 @@ static void bleProcMsg(bleMsg_t *pMsg) break; case DM_ADV_START_IND: + LOG_INFO("ble", "Advertisement started"); uiEvent = APP_UI_ADV_START; break; case DM_ADV_STOP_IND: + LOG_INFO("ble", "Advertisement stopped"); uiEvent = APP_UI_ADV_STOP; break; case DM_CONN_OPEN_IND: + connOpen = &pMsg->dm.connOpen; + LOG_INFO("ble", "connection from %02X:%02X:%02X:%02X:%02X:%02X opened", + connOpen->peerAddr[0], connOpen->peerAddr[1], + connOpen->peerAddr[2], connOpen->peerAddr[3], + connOpen->peerAddr[4], connOpen->peerAddr[5]); BasProcMsg(&pMsg->hdr); uiEvent = APP_UI_CONN_OPEN; break; case DM_CONN_CLOSE_IND: + switch (pMsg->dm.connClose.reason) + { + case HCI_ERR_CONN_TIMEOUT: + LOG_INFO("ble", "Connection closed (0x%02X), Connection timeout", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_LOCAL_TERMINATED: + LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated by local host", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_REMOTE_TERMINATED: + LOG_INFO("ble", "Connection closed (0x%02X), Remote user terminated connection", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_CONN_FAIL: + LOG_INFO("ble", "Connection closed (0x%02X), Connection failed to be established", + pMsg->dm.connClose.reason); + break; + case HCI_ERR_MIC_FAILURE: + LOG_INFO("ble", "Connection closed (0x%02X), Connection terminated due to MIC failure", + pMsg->dm.connClose.reason); + break; + default: + LOG_INFO("ble", "Connection closed (0x%02X)", + pMsg->dm.connClose.reason); + break; + } bleClose(pMsg); uiEvent = APP_UI_CONN_CLOSE; break; case DM_SEC_PAIR_CMPL_IND: + LOG_INFO("ble", "Secure pairing successful, auth: 0x%02X", + pMsg->dm.pairCmpl.auth); uiEvent = APP_UI_SEC_PAIR_CMPL; break; case DM_SEC_PAIR_FAIL_IND: + switch (pMsg->hdr.status) { + case SMP_ERR_TIMEOUT: + LOG_INFO("ble", "Secure pairing failed (0x%02X), Transaction timeout", + pMsg->hdr.status); + break; + case SMP_ERR_ATTEMPTS: + LOG_INFO("ble", "Secure pairing failed (0x%02X), Repeated attempts", + pMsg->hdr.status); + break; + default: + LOG_INFO("ble", "Secure pairing failed (0x%02X)", + pMsg->hdr.status); + break; + } uiEvent = APP_UI_SEC_PAIR_FAIL; break; case DM_SEC_ENCRYPT_IND: + LOG_INFO("ble", "Encrypted handshake successful"); uiEvent = APP_UI_SEC_ENCRYPT; break; case DM_SEC_ENCRYPT_FAIL_IND: + LOG_INFO("ble", "Encrypted handshake failed"); uiEvent = APP_UI_SEC_ENCRYPT_FAIL; break; @@ -441,6 +496,7 @@ static void bleProcMsg(bleMsg_t *pMsg) break; case DM_HW_ERROR_IND: + LOG_ERR("ble", "HW Error"); uiEvent = APP_UI_HW_ERROR; break; diff --git a/epicardium/cdcacm.c b/epicardium/cdcacm.c deleted file mode 100644 index 1cbbb154452a9ac96b9300bdded5071353121260..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.c +++ /dev/null @@ -1,382 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - * $Id: main.c 32120 2017-11-28 23:51:11Z lorne.smith $ - * - ******************************************************************************* - */ - -/** - * @file main.c - * @brief USB CDC-ACM example - * @details This project creates a virtual COM port, which loops back data sent to it. - * Load the project, connect a cable from the PC to the USB connector - * on the Evaluation Kit, and observe that the PC now recognizes a new COM port. - * A driver for the COM port, if needed, is located in the Driver/ subdirectory. - * - */ - -#include <stdio.h> -#include <stddef.h> -#include "mxc_config.h" -#include "mxc_sys.h" -#include "mxc_delay.h" -#include "board.h" -#include "led.h" -#include "usb.h" -#include "usb_event.h" -#include "enumerate.h" -#include "cdc_acm.h" -#include "descriptors.h" - -#include "modules/modules.h" -#include "modules/log.h" -#include <errno.h> - -/***** Definitions *****/ -#define EVENT_ENUM_COMP MAXUSB_NUM_EVENTS - -#define BUFFER_SIZE 64 - -#define STRINGIFY(x) #x -#define TOSTRING(x) STRINGIFY(x) - -/***** Global Data *****/ -//SWYM: rename to CDC_xy or put into struct CDC_state -volatile int configured; //SWYM: actually unused... -volatile int suspended; -volatile unsigned int event_flags; -int remote_wake_en; - -/***** Function Prototypes *****/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata); -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata); -static int event_callback(maxusb_event_t evt, void *data); -static void usb_app_sleep(void); -static void usb_app_wakeup(void); -static int usb_read_callback(void); -//static void echo_usb(void); - -/***** File Scope Variables *****/ - -/* This EP assignment must match the Configuration Descriptor */ -static const acm_cfg_t acm_cfg = { - 1, /* EP OUT */ - MXC_USBHS_MAX_PACKET, /* OUT max packet size */ - 2, /* EP IN */ - MXC_USBHS_MAX_PACKET, /* IN max packet size */ - 3, /* EP Notify */ - MXC_USBHS_MAX_PACKET, /* Notify max packet size */ -}; - -static volatile int usb_read_complete; - -int usb_startup_cb() -{ - const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; - return SYS_USBHS_Init(&sys_usbhs_cfg); -} - -int usb_shutdown_cb() -{ - return SYS_USBHS_Shutdown(); -} - -/* User-supplied function to delay usec micro-seconds */ -void delay_us(unsigned int usec) -{ - /* mxc_delay() takes unsigned long, so can't use it directly */ - mxc_delay(usec); -} - -/******************************************************************************/ -int cdcacm_init(void) -{ - maxusb_cfg_options_t usb_opts; - - /* Initialize state */ - configured = 0; - suspended = 0; - event_flags = 0; - remote_wake_en = 0; - - /* Start out in full speed */ - usb_opts.enable_hs = 0; - usb_opts.delay_us = - delay_us; /* Function which will be used for delays */ - usb_opts.init_callback = usb_startup_cb; - usb_opts.shutdown_callback = usb_shutdown_cb; - - /* Initialize the usb module */ - if (usb_init(&usb_opts) != 0) { - LOG_ERR("cdcacm", "usb_init() failed"); - return -EIO; - } - - /* Initialize the enumeration module */ - if (enum_init() != 0) { - LOG_ERR("cdcacm", "enum_init() failed"); - return -EIO; - } - - /* Register enumeration data */ - enum_register_descriptor( - ENUM_DESC_DEVICE, (uint8_t *)&device_descriptor, 0 - ); - enum_register_descriptor( - ENUM_DESC_CONFIG, (uint8_t *)&config_descriptor, 0 - ); - enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); - enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); - enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); - - /* Handle configuration */ - enum_register_callback(ENUM_SETCONFIG, setconfig_callback, NULL); - - /* Handle feature set/clear */ - enum_register_callback(ENUM_SETFEATURE, setfeature_callback, NULL); - enum_register_callback(ENUM_CLRFEATURE, clrfeature_callback, NULL); - - /* Initialize the class driver */ - if (acm_init(&config_descriptor.comm_interface_descriptor) != 0) { - LOG_ERR("cdcacm", "acm_init() failed"); - return -EIO; - } - - /* Register callbacks */ - usb_event_enable(MAXUSB_EVENT_NOVBUS, event_callback, NULL); - usb_event_enable(MAXUSB_EVENT_VBUS, event_callback, NULL); - acm_register_callback(ACM_CB_READ_READY, usb_read_callback); - usb_read_complete = 0; - - /* Start with USB in low power mode */ - usb_app_sleep(); - /* TODO: Fix priority */ - NVIC_SetPriority(USB_IRQn, 6); - NVIC_EnableIRQ(USB_IRQn); - - return 0; -} - -int cdcacm_num_read_avail(void) -{ - return acm_canread(); -} - -uint8_t cdcacm_read(void) -{ - while (acm_canread() <= 0) { - } - - uint8_t buf; - acm_read(&buf, 1); - return buf; -} - -void cdcacm_write(uint8_t *data, int len) -{ - static int lockup_disable = 0; - if (acm_present() && !lockup_disable) { - int ret = acm_write(data, len); - if (ret < 0) { - lockup_disable = 1; - LOG_ERR("cdcacm", "fifo lockup detected"); - } else if (ret != len) { - LOG_WARN( - "cdcacm", "write length mismatch, got %d", ret - ); - } - } -} - -/******************************************************************************/ -#if 0 -static void echo_usb(void) -{ - int chars; - uint8_t buffer[BUFFER_SIZE]; - - if ((chars = acm_canread()) > 0) { - - if (chars > BUFFER_SIZE) { - chars = BUFFER_SIZE; - } - - // Read the data from USB - if (acm_read(buffer, chars) != chars) { - printf("acm_read() failed\n"); - return; - } - - // Echo it back - if (acm_present()) { - if (acm_write(buffer, chars) != chars) { - printf("acm_write() failed\n"); - } - } - } -} -#endif -/******************************************************************************/ -static int setconfig_callback(usb_setup_pkt *sud, void *cbdata) -{ - /* Confirm the configuration value */ - if (sud->wValue == - config_descriptor.config_descriptor.bConfigurationValue) { - configured = 1; - MXC_SETBIT(&event_flags, EVENT_ENUM_COMP); - return acm_configure(&acm_cfg); /* Configure the device class */ - } else if (sud->wValue == 0) { - configured = 0; - return acm_deconfigure(); - } - - return -1; -} - -/******************************************************************************/ -static int setfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if (sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 1; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static int clrfeature_callback(usb_setup_pkt *sud, void *cbdata) -{ - if (sud->wValue == FEAT_REMOTE_WAKE) { - remote_wake_en = 0; - } else { - // Unknown callback - return -1; - } - - return 0; -} - -/******************************************************************************/ -static void usb_app_sleep(void) -{ - /* TODO: Place low-power code here */ - suspended = 1; -} - -/******************************************************************************/ -static void usb_app_wakeup(void) -{ - /* TODO: Place low-power code here */ - suspended = 0; -} - -/******************************************************************************/ -static int event_callback(maxusb_event_t evt, void *data) -{ - /* Set event flag */ - MXC_SETBIT(&event_flags, evt); - - switch (evt) { - case MAXUSB_EVENT_NOVBUS: - usb_event_disable(MAXUSB_EVENT_BRST); - usb_event_disable(MAXUSB_EVENT_SUSP); - usb_event_disable(MAXUSB_EVENT_DPACT); - usb_disconnect(); - configured = 0; - enum_clearconfig(); - acm_deconfigure(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_VBUS: - usb_event_clear(MAXUSB_EVENT_BRST); - usb_event_enable(MAXUSB_EVENT_BRST, event_callback, NULL); - usb_event_clear(MAXUSB_EVENT_SUSP); - usb_event_enable(MAXUSB_EVENT_SUSP, event_callback, NULL); - usb_connect(); - usb_app_sleep(); - break; - case MAXUSB_EVENT_BRST: - usb_app_wakeup(); - enum_clearconfig(); - acm_deconfigure(); - configured = 0; - suspended = 0; - break; - case MAXUSB_EVENT_SUSP: - usb_app_sleep(); - break; - case MAXUSB_EVENT_DPACT: - usb_app_wakeup(); - break; - default: - break; - } - - return 0; -} - -/******************************************************************************/ -static int usb_read_callback(void) -{ - usb_read_complete = 1; - return 0; -} - -/******************************************************************************/ -#include "FreeRTOS.h" -#include "task.h" - -void USB_IRQHandler(void) -{ - usb_event_handler(); - - if (serial_task_id != NULL) { - BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR( - serial_task_id, &xHigherPriorityTaskWoken - ); - portYIELD_FROM_ISR(xHigherPriorityTaskWoken); - } -} - -/******************************************************************************/ -/* TODO: We probably need to fix something related to this */ -#if 0 -void SysTick_Handler(void) -{ - mxc_delay_handler(); -} -#endif /* 0 */ diff --git a/epicardium/cdcacm.h b/epicardium/cdcacm.h deleted file mode 100644 index 1c25d75027cebffd593f32364ebab7eec65fa7e6..0000000000000000000000000000000000000000 --- a/epicardium/cdcacm.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef CDCACM_H -#define CDCACM_H -#include <stdint.h> - -int cdcacm_init(void); -int cdcacm_num_read_avail(void); -uint8_t cdcacm_read(void); -void cdcacm_write(uint8_t *data, int len); - -#endif diff --git a/epicardium/descriptors.h b/epicardium/descriptors.h deleted file mode 100644 index c081c4010e65c7c86980ad94a096a81febb69f61..0000000000000000000000000000000000000000 --- a/epicardium/descriptors.h +++ /dev/null @@ -1,227 +0,0 @@ -/******************************************************************************* - * Copyright (C) 2016 Maxim Integrated Products, Inc., All Rights Reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the "Software"), - * to deal in the Software without restriction, including without limitation - * the rights to use, copy, modify, merge, publish, distribute, sublicense, - * and/or sell copies of the Software, and to permit persons to whom the - * Software is furnished to do so, subject to the following conditions: - * - * The above copyright notice and this permission notice shall be included - * in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. - * IN NO EVENT SHALL MAXIM INTEGRATED BE LIABLE FOR ANY CLAIM, DAMAGES - * OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, - * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR - * OTHER DEALINGS IN THE SOFTWARE. - * - * Except as contained in this notice, the name of Maxim Integrated - * Products, Inc. shall not be used except as stated in the Maxim Integrated - * Products, Inc. Branding Policy. - * - * The mere transfer of this software does not imply any licenses - * of trade secrets, proprietary technology, copyrights, patents, - * trademarks, maskwork rights, or any other form of intellectual - * property whatsoever. Maxim Integrated Products, Inc. retains all - * ownership rights. - * - * Description: Communications Device Class ACM (Serial Port) over USB - * $Id: descriptors.h 36682 2018-08-06 21:14:03Z michael.bayern $ - * - ******************************************************************************* - */ - -#ifndef _DESCRIPTORS_H_ -#define _DESCRIPTORS_H_ - -#include <stdint.h> -#include "usb.h" -#include "hid_kbd.h" - -usb_device_descriptor_t __attribute__((aligned(4))) device_descriptor = { - 0x12, /* bLength = 18 */ - 0x01, /* bDescriptorType = Device */ - 0x0110, /* bcdUSB USB spec rev (BCD) */ - 0x02, /* bDeviceClass = comm class (2) */ - 0x00, /* bDeviceSubClass */ - 0x00, /* bDeviceProtocol */ - 0x40, /* bMaxPacketSize0 is 64 bytes */ - 0x0B6A, /* idVendor (Maxim Integrated) */ - 0x003C, /* idProduct */ - 0x0100, /* bcdDevice */ - 0x01, /* iManufacturer Descriptor ID */ - 0x02, /* iProduct Descriptor ID */ - 0x00, /* iSerialNumber = (0) No string */ - 0x01 /* bNumConfigurations */ -}; - -__attribute__((aligned(4))) -struct __attribute__((packed)) { - usb_configuration_descriptor_t config_descriptor; - usb_interface_descriptor_t comm_interface_descriptor; - uint8_t header_functional_descriptor[5]; - uint8_t call_management_descriptor[5]; - uint8_t acm_functional_descriptor[4]; - uint8_t union_functional_descriptor[5]; - usb_endpoint_descriptor_t endpoint_descriptor_3; - usb_interface_descriptor_t data_interface_descriptor; - usb_endpoint_descriptor_t endpoint_descriptor_1; - usb_endpoint_descriptor_t endpoint_descriptor_2; -} config_descriptor = -{ - { - 0x09, /* bLength = 9 */ - 0x02, /* bDescriptorType = Config (2) */ - 0x0043, /* wTotalLength(L/H) */ - 0x02, /* bNumInterfaces */ - 0x01, /* bConfigValue */ - 0x00, /* iConfiguration */ - 0xE0, /* bmAttributes (self-powered, remote wakeup) */ - 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ - }, - { /* First Interface Descriptor For Comm Class Interface */ - 0x09, /* bLength = 9 */ - 0x04, /* bDescriptorType = Interface (4) */ - 0x00, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x01, /* bNumEndpoints (one for OUT) */ - 0x02, /* bInterfaceClass = Communications Interface Class (2) */ - 0x02, /* bInterfaceSubClass = Abstract Control Model (2) */ - 0x01, /* bInterfaceProtocol = Common "AT" commands (1), no class specific protocol (0) */ - 0x00, /* iInterface */ - }, - { /* Header Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x00, /* bDescriptorSubtype */ - 0x10, 0x01, /* bcdCDC */ - }, - { /* Call Management Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x01, /* bDescriptorSubtype */ - 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ - 0x01, /* bmDataInterface */ - }, - { /* Abstract Control Management Functional Descriptor */ - 0x04, /* bFunctionalLength = 4 */ - 0x24, /* bDescriptorType */ - 0x02, /* bDescriptorSubtype */ - 0x02, /* bmCapabilities */ - }, - { /* Union Functional Descriptor */ - 0x05, /* bFunctionalLength = 5 */ - 0x24, /* bDescriptorType */ - 0x06, /* bDescriptorSubtype */ - 0x00, /* bmMasterInterface */ - 0x01, /* bmSlaveInterface0 */ - }, - { /* IN Endpoint 3 (Descriptor #1) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x83, /* bEndpointAddress (EP3-IN) */ - 0x03, /* bmAttributes (interrupt) */ - 0x0040, /* wMaxPacketSize */ - 0xff, /* bInterval (milliseconds) */ - }, - { /* Second Interface Descriptor For Data Interface */ - 0x09, /* bLength */ - 0x04, /* bDescriptorType (Interface) */ - 0x01, /* bInterfaceNumber */ - 0x00, /* bAlternateSetting */ - 0x02, /* bNumEndpoints */ - 0x0a, /* bInterfaceClass = Data Interface (10) */ - 0x00, /* bInterfaceSubClass = none (0) */ - 0x00, /* bInterfaceProtocol = No class specific protocol (0) */ - 0x00, /* biInterface = No Text String (0) */ - }, - { /* OUT Endpoint 1 (Descriptor #2) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x01, /* bEndpointAddress (EP1-OUT) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00, /* bInterval (N/A) */ - }, - { /* IN Endpoint 2 (Descriptor #3) */ - 0x07, /* bLength */ - 0x05, /* bDescriptorType (Endpoint) */ - 0x82, /* bEndpointAddress (EP2-IN) */ - 0x02, /* bmAttributes (bulk) */ - 0x0040, /* wMaxPacketSize */ - 0x00 /* bInterval (N/A) */ - } -}; - -__attribute__((aligned(4))) -uint8_t lang_id_desc[] = { - 0x04, /* bLength */ - 0x03, /* bDescriptorType */ - 0x09, 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ -}; - -__attribute__((aligned(4))) -uint8_t mfg_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'a', 0, - 'x', 0, - 'i', 0, - 'm', 0, - ' ', 0, - 'I', 0, - 'n', 0, - 't', 0, - 'e', 0, - 'g', 0, - 'r', 0, - 'a', 0, - 't', 0, - 'e', 0, - 'd', 0, -}; - -__attribute__((aligned(4))) -uint8_t prod_id_desc[] = { - 0x22, /* bLength */ - 0x03, /* bDescriptorType */ - 'M', 0, - 'A', 0, - 'X', 0, - '3', 0, - '2', 0, - '6', 0, - '6', 0, - '5', 0, - ' ', 0, - 'C', 0, - 'D', 0, - 'C', 0, - '-', 0, - 'A', 0, - 'C', 0, - 'M', 0, -}; - -/* Not currently used (see device descriptor), but could be enabled if desired */ -__attribute__((aligned(4))) -uint8_t serial_id_desc[] = { - 0x14, /* bLength */ - 0x03, /* bDescriptorType */ - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '0', 0, - '1', 0 -}; - -#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/epicardium.h b/epicardium/epicardium.h index 4eea7d3fee2f2af194c669d4b24b768be41928ca..74b0c814653b0ad0193d2ffd11dc50f0d7fae8d7 100644 --- a/epicardium/epicardium.h +++ b/epicardium/epicardium.h @@ -131,6 +131,10 @@ typedef _Bool bool; #define API_MAX86150_GET_DATA 0x0101 #define API_MAX86150_SET_LED_AMPLITUDE 0x0102 +#define API_USB_SHUTDOWN 0x110 +#define API_USB_STORAGE 0x111 +#define API_USB_CDCACM 0x112 + /* clang-format on */ typedef uint32_t api_int_id_t; @@ -1638,8 +1642,8 @@ API_ISR(EPIC_INT_RTC_ALARM, epic_isr_rtc_alarm); API(API_TRNG_READ, int epic_trng_read(uint8_t *dest, size_t size)); /** - * MAX30001 API - * ---------- + * MAX30001 + * ======== */ /** @@ -1700,5 +1704,20 @@ API(API_MAX30001_DISABLE, int epic_max30001_disable_sensor( void )); +/** + * De-initialize the currently configured USB device (if any) + * + */ +API(API_USB_SHUTDOWN, int epic_usb_shutdown(void)); +/** + * Configure the USB peripheral to export the internal FLASH + * as a Mass Storage device + */ +API(API_USB_STORAGE, int epic_usb_storage(void)); +/** + * Configure the USB peripheral to provide card10's stdin/stdout + * on a USB CDC-ACM device + */ +API(API_USB_CDCACM, int epic_usb_cdcacm(void)); #endif /* _EPICARDIUM_H */ diff --git a/epicardium/fs/filesystem_fat.c b/epicardium/fs/filesystem_fat.c index 7d95510360324ac5e105a53aa90a3a96ee0936c1..ccaec62157174cdfab14c957d157941b063acc64 100644 --- a/epicardium/fs/filesystem_fat.c +++ b/epicardium/fs/filesystem_fat.c @@ -16,6 +16,7 @@ #include <FreeRTOS.h> #include <semphr.h> +#include <timers.h> #include "fs/internal.h" #include "modules/filesystem.h" @@ -57,7 +58,7 @@ struct FatObject { struct EpicFileSystem { struct FatObject pool[EPIC_FAT_MAX_OPENED]; uint32_t generationCount; - bool initialized; + bool attached; FATFS FatFs; int lockCoreMask; }; @@ -102,6 +103,11 @@ static StaticSemaphore_t s_globalLockBuffer; static SemaphoreHandle_t s_globalLock = NULL; +static void cb_attachTimer(void *a, uint32_t b) +{ + fatfs_attach(); +} + void fatfs_init() { static volatile bool s_initCalled = false; @@ -117,6 +123,7 @@ void fatfs_init() #else s_globalLock = xSemaphoreCreateMutex(); #endif + s_globalFileSystem.generationCount = 1; fatfs_attach(); } @@ -137,11 +144,11 @@ int fatfs_attach() int rc = 0; if (globalLockAccquire()) { EpicFileSystem *fs = &s_globalFileSystem; - if (!fs->initialized) { + if (!fs->attached) { ff_res = f_mount(&fs->FatFs, "/", 0); if (ff_res == FR_OK) { - fs->initialized = true; - SSLOG_DEBUG("FatFs mounted\n"); + fs->attached = true; + SSLOG_INFO("attached\n"); } else { SSLOG_ERR( "f_mount error %s\n", @@ -158,25 +165,38 @@ int fatfs_attach() return rc; } +void fatfs_schedule_attach(void) +{ + //if we're running in thread context, cont't call the *FromISR version + if (xPortIsInsideInterrupt()) { + xTimerPendFunctionCallFromISR(cb_attachTimer, NULL, 0, NULL); + } else { + xTimerPendFunctionCall( + cb_attachTimer, NULL, 0, 1); //wait 1 tick + } +} + void fatfs_detach() { FRESULT ff_res; EpicFileSystem *fs; if (efs_lock_global(&fs) == 0) { - efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); - - //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj - ff_res = f_mount(NULL, "/", 0); - if (ff_res != FR_OK) { - SSLOG_ERR( - "f_mount (unmount) error %s\n", - f_get_rc_string(ff_res) - ); - } + if (fs->attached) { + efs_close_all(fs, EPICARDIUM_COREMASK_BOTH); + + //unmount by passing NULL as fs object, will destroy our sync object via ff_del_syncobj + ff_res = f_mount(NULL, "/", 0); + if (ff_res != FR_OK) { + SSLOG_ERR( + "f_mount (unmount) error %s\n", + f_get_rc_string(ff_res) + ); + } - fs->initialized = false; - disk_deinitialize(); - SSLOG_INFO("detached\n"); + fs->attached = false; + disk_deinitialize(); + SSLOG_INFO("detached\n"); + } efs_unlock_global(fs); } } @@ -223,7 +243,7 @@ int efs_lock_global(EpicFileSystem **fs) if (!globalLockAccquire()) { return -EBUSY; } - if (!s_globalFileSystem.initialized) { + if (!s_globalFileSystem.attached) { globalLockRelease(); return -ENODEV; } diff --git a/epicardium/main.c b/epicardium/main.c index c7f58a05c889fc0c47eafb97d1f2995a8afce881..dadad9bf44ed4de087e21416b8536d5daf3a4c1f 100644 --- a/epicardium/main.c +++ b/epicardium/main.c @@ -12,15 +12,22 @@ int main(void) { + watchdog_init(); + LOG_INFO("startup", "Epicardium startup ..."); LOG_INFO("startup", "Version " CARD10_VERSION); LOG_DEBUG("startup", "Initializing hardware ..."); hardware_early_init(); - char *version_buf = CARD10_VERSION; - epic_disp_print(0, 5, "epicardium:", 0xffff, 0x0000); - epic_disp_print(0, 24, version_buf, 0xffff, 0x0000); + /* + * Version Splash + */ + const char *version_buf = CARD10_VERSION; + const int offset = (160 - (int)strlen(version_buf) * 14) / 2; + epic_disp_clear(0x3b7); + epic_disp_print(10, 20, "Epicardium", 0x290, 0x3b7); + epic_disp_print(offset > 0 ? offset : 0, 40, version_buf, 0x290, 0x3b7); epic_disp_update(); mxc_delay(2000000); @@ -32,7 +39,7 @@ int main(void) (const char *)"Serial", configMINIMAL_STACK_SIZE * 2, NULL, - tskIDLE_PRIORITY + 1, + tskIDLE_PRIORITY + 3, NULL) != pdPASS) { LOG_CRIT("startup", "Failed to create %s task!", "Serial"); abort(); @@ -127,13 +134,10 @@ int main(void) abort(); } - /* Watchdog petting */ -#if 0 /* - * Disabled for this release. + * Initialize serial driver data structures. */ - watchdog_clearer_init(); -#endif + serial_init(); LOG_DEBUG("startup", "Starting FreeRTOS ..."); vTaskStartScheduler(); diff --git a/epicardium/meson.build b/epicardium/meson.build index 1aa5556010941d2267f8b10aafcaf9ec7683c8a2..cf67023b9f7143336875d973108d8ee0a080ffd1 100644 --- a/epicardium/meson.build +++ b/epicardium/meson.build @@ -80,7 +80,9 @@ endif elf = executable( name + '.elf', - 'cdcacm.c', + 'usb/epc_usb.c', + 'usb/cdcacm.c', + 'usb/mass_storage.c', 'main.c', 'support.c', 'fs/filesystem_fat.c', diff --git a/epicardium/modules/bhi.c b/epicardium/modules/bhi.c index 7d3689502e07c3de554744b1f10684d9d0af256b..66f8bb7ea42f4f679f9247c85b323d04ddf621f9 100644 --- a/epicardium/modules/bhi.c +++ b/epicardium/modules/bhi.c @@ -412,6 +412,11 @@ void vBhi160Task(void *pvParameters) bhi160_task_id = xTaskGetCurrentTaskHandle(); bhi160_mutex = xSemaphoreCreateMutexStatic(&bhi160_mutex_data); + /* + * Wait a little before initializing BHI160. + */ + vTaskDelay(pdMS_TO_TICKS(3)); + int lockret = hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)); if (lockret < 0) { LOG_CRIT("bhi160", "Failed to acquire I2C lock!"); diff --git a/epicardium/modules/dispatcher.c b/epicardium/modules/dispatcher.c index 1f504eca52e20bfb8c118281c772942e72ac7a18..03f8534cc0a1c02dccc61f8c03e486e2ab472ff8 100644 --- a/epicardium/modules/dispatcher.c +++ b/epicardium/modules/dispatcher.c @@ -13,14 +13,17 @@ TaskHandle_t dispatcher_task_id; static StaticSemaphore_t api_mutex_data; SemaphoreHandle_t api_mutex = NULL; +void dispatcher_mutex_init(void) +{ + api_mutex = xSemaphoreCreateMutexStatic(&api_mutex_data); +} + /* * API dispatcher task. This task will sleep until an API call is issued and * then wake up to dispatch it. */ void vApiDispatcher(void *pvParameters) { - api_mutex = xSemaphoreCreateMutexStatic(&api_mutex_data); - LOG_DEBUG("dispatcher", "Ready."); while (1) { if (api_dispatcher_poll()) { diff --git a/epicardium/modules/filesystem.h b/epicardium/modules/filesystem.h index 720147ecd04a8f07755052d05adb90871b360bd7..db54a2895621d0e316dee83c362faac737b1a9e2 100644 --- a/epicardium/modules/filesystem.h +++ b/epicardium/modules/filesystem.h @@ -16,9 +16,19 @@ void fatfs_init(void); /** * initialize and mount the FLASH storage + * + * NOTE: not safe to be called from an ISR */ int fatfs_attach(void); + +/** + * asynchronously attach the FLASH storage + * + * safe to be called from an ISR + */ +void fatfs_schedule_attach(void); + /** close all opened FDs, sync and deinitialize FLASH layer */ void fatfs_detach(void); diff --git a/epicardium/modules/hardware.c b/epicardium/modules/hardware.c index 6148513eb03014b19ff01873701ae3aedefb582f..17c707217283c41f61bfad79b9b6fb69d7b20d11 100644 --- a/epicardium/modules/hardware.c +++ b/epicardium/modules/hardware.c @@ -2,7 +2,7 @@ #include "api/dispatcher.h" #include "api/interrupt-sender.h" -#include "cdcacm.h" +#include "usb/epc_usb.h" #include "modules/filesystem.h" #include "modules/log.h" #include "modules/modules.h" @@ -32,24 +32,7 @@ int hardware_early_init(void) /* * Watchdog timer */ -#if 0 - /* - * Disabled for this release. - */ - sys_cfg_wdt_t wdt_cfg = NULL; - WDT_Init(MXC_WDT0, wdt_cfg); - - if (WDT_GetResetFlag(MXC_WDT0)) { - WDT_ClearResetFlag(MXC_WDT0); - LOG_INFO("watchdog", "Reset due to watchdog timeout"); - } - - WDT_Enable(MXC_WDT0, 1); - WDT_SetResetPeriod( - MXC_WDT0, - WDT_PERIOD_2_27); /* Clocked by PCLK at 50MHz, reset at 2^27 ticks = 2.7 seconds */ - WDT_EnableReset(MXC_WDT0, 1); -#endif + watchdog_init(); /* * I2C bus for onboard peripherals (ie. PMIC, BMA400, BHI160, BME680, @@ -169,8 +152,8 @@ int hardware_early_init(void) /* * USB-Serial */ - if (cdcacm_init() < 0) { - LOG_ERR("init", "USB-Serial unavailable"); + if (epic_usb_cdcacm() < 0) { + LOG_ERR("startup", "USB-Serial unavailable"); } /* @@ -194,6 +177,16 @@ int hardware_early_init(void) */ hwlock_init(); + /* + * API Dispatcher Mutex + */ + dispatcher_mutex_init(); + + /* + * MAX30001 mutex init + */ + max30001_mutex_init(); + return 0; } @@ -207,6 +200,9 @@ int hardware_early_init(void) */ int hardware_init(void) { + /* Watchdog clearer software timer */ + watchdog_clearer_init(); + /* Light Sensor */ LOG_DEBUG("init", "Starting light sensor ..."); epic_light_sensor_run(); diff --git a/epicardium/modules/hw-lock.c b/epicardium/modules/hw-lock.c index f02a5882b1bdb2aabddeb2ccea77d59fe14939c4..ce88d921748302edd849eea0b923e6c61eab8dec 100644 --- a/epicardium/modules/hw-lock.c +++ b/epicardium/modules/hw-lock.c @@ -25,13 +25,30 @@ int hwlock_acquire(enum hwlock_periph p, TickType_t wait) if (p >= _HWLOCK_MAX) { return -EINVAL; } + TaskHandle_t task = xTaskGetCurrentTaskHandle(); if (xSemaphoreTake(hwlock_mutex[p], wait) != pdTRUE) { - LOG_WARN("hwlock", "Lock %u is busy.", p); + /* Don't print warnings for 0 wait acquires */ + if (wait == 0) { + return -EBUSY; + } + + LOG_WARN( + "hwlock", + "Lock %u is busy! Held by \"%s\" and attempted to accquire by \"%s\"", + p, + pcTaskGetName(hwlock_tasks[p]), + pcTaskGetName(task) + ); + LOG_DEBUG( + "hwlock", + "...attempted to lock from pc %p", + __builtin_return_address(0) + ); return -EBUSY; } - hwlock_tasks[p] = xTaskGetCurrentTaskHandle(); + hwlock_tasks[p] = task; return 0; } diff --git a/epicardium/modules/max30001.c b/epicardium/modules/max30001.c index 5cc3ef3654e19b6ea298ab7ba8e019676dfc4332..811af4bf8aa274443abb1415a7586c2766660991 100644 --- a/epicardium/modules/max30001.c +++ b/epicardium/modules/max30001.c @@ -370,10 +370,14 @@ static void max300001_interrupt_callback(void *_) } /* }}} */ +void max30001_mutex_init(void) +{ + max30001_mutex = xSemaphoreCreateMutexStatic(&max30001_mutex_data); +} + void vMAX30001Task(void *pvParameters) { max30001_task_id = xTaskGetCurrentTaskHandle(); - max30001_mutex = xSemaphoreCreateMutexStatic(&max30001_mutex_data); int lockret = hwlock_acquire(HWLOCK_SPI_ECG, pdMS_TO_TICKS(100)); if (lockret < 0) { diff --git a/epicardium/modules/meson.build b/epicardium/modules/meson.build index 1aa7494ea0eff9003b48f0aa41a63257b1ab3833..693ca9a8f94a432ff64d770f91d9e9005c88d42a 100644 --- a/epicardium/modules/meson.build +++ b/epicardium/modules/meson.build @@ -21,4 +21,5 @@ module_sources = files( 'trng.c', 'vibra.c', 'watchdog.c', + 'usb.c' ) diff --git a/epicardium/modules/modules.h b/epicardium/modules/modules.h index 144b18fb765a78580d66a8e8b59a9593d8127a77..2d51f786373cea0bb108212b225df7badf0bdc25 100644 --- a/epicardium/modules/modules.h +++ b/epicardium/modules/modules.h @@ -9,6 +9,7 @@ /* ---------- Dispatcher --------------------------------------------------- */ void vApiDispatcher(void *pvParameters); +void dispatcher_mutex_init(void); extern SemaphoreHandle_t api_mutex; extern TaskHandle_t dispatcher_task_id; @@ -23,10 +24,18 @@ void return_to_menu(void); /* ---------- Serial ------------------------------------------------------- */ #define SERIAL_READ_BUFFER_SIZE 128 +#define SERIAL_WRITE_STREAM_BUFFER_SIZE 512 +void serial_init(); void vSerialTask(void *pvParameters); void serial_enqueue_char(char chr); extern TaskHandle_t serial_task_id; +// For the eSetBit xTaskNotify task semaphore trigger +enum serial_notify{ + SERIAL_WRITE_NOTIFY = 0x01, + SERIAL_READ_NOTIFY = 0x02, +}; + /* ---------- LED Animation / Personal States ------------------------------ */ #define PERSONAL_STATE_LED 14 void vLedTask(void *pvParameters); @@ -36,6 +45,7 @@ int personal_state_enabled(); void vPmicTask(void *pvParameters); /* ---------- Watchdog ----------------------------------------------------- */ +void watchdog_init(); void watchdog_clearer_init(); /* Critical battery voltage */ @@ -90,7 +100,9 @@ void disp_forcelock(); #define BHI160_MUTEX_WAIT_MS 50 void vBhi160Task(void *pvParameters); +/* ---------- MAX30001 ----------------------------------------------------- */ #define MAX30001_MUTEX_WAIT_MS 50 void vMAX30001Task(void *pvParameters); +void max30001_mutex_init(void); #endif /* MODULES_H */ diff --git a/epicardium/modules/pmic.c b/epicardium/modules/pmic.c index 093e97ecdad2bd250636150c59ac18fcb16ad279..4df12ea91d49aaf666e73e609160a0bd5e3024e4 100644 --- a/epicardium/modules/pmic.c +++ b/epicardium/modules/pmic.c @@ -16,6 +16,9 @@ #include "timers.h" #include <stdio.h> +#include <string.h> + +#define LOCK_WAIT pdMS_TO_TICKS(1000) /* Task ID for the pmic handler */ static TaskHandle_t pmic_task_id = NULL; @@ -50,12 +53,12 @@ int pmic_read_amux(enum pmic_amux_signal sig, float *result) return -EINVAL; } - int adc_ret = hwlock_acquire(HWLOCK_ADC, pdMS_TO_TICKS(100)); + int adc_ret = hwlock_acquire(HWLOCK_ADC, LOCK_WAIT); if (adc_ret < 0) { ret = adc_ret; goto done; } - i2c_ret = hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)); + i2c_ret = hwlock_acquire(HWLOCK_I2C, LOCK_WAIT); if (i2c_ret < 0) { ret = i2c_ret; goto done; @@ -70,10 +73,9 @@ int pmic_read_amux(enum pmic_amux_signal sig, float *result) * release the I2C mutex. */ hwlock_release(HWLOCK_I2C); - i2c_ret = 0; vTaskDelay(pdMS_TO_TICKS(5)); - i2c_ret = hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(100)); + i2c_ret = hwlock_acquire(HWLOCK_I2C, LOCK_WAIT); if (i2c_ret < 0) { ret = i2c_ret; goto done; @@ -137,7 +139,7 @@ done: static void pmic_poll_interrupts(TickType_t *button_start_tick, TickType_t duration) { - while (hwlock_acquire(HWLOCK_I2C, pdMS_TO_TICKS(500)) < 0) { + while (hwlock_acquire(HWLOCK_I2C, LOCK_WAIT) < 0) { LOG_WARN("pmic", "Failed to acquire I2C. Retrying ..."); xTaskNotify(pmic_task_id, PMIC_NOTIFY_IRQ, eSetBits); return; @@ -207,7 +209,10 @@ static void pmic_check_battery() res = pmic_read_amux(PMIC_AMUX_BATT_U, &u_batt); if (res < 0) { - LOG_ERR("pmic", "Failed reading battery voltage: %d", res); + LOG_ERR("pmic", + "Failed reading battery voltage: %s (%d)", + strerror(-res), + res); return; } diff --git a/epicardium/modules/serial.c b/epicardium/modules/serial.c index a7392056f31705e9c6f334f60952ece4dee22d10..3292ab9330c973a83243b9aad0c3e7b63a6d0200 100644 --- a/epicardium/modules/serial.c +++ b/epicardium/modules/serial.c @@ -4,32 +4,121 @@ #include "modules/modules.h" #include "max32665.h" -#include "cdcacm.h" +#include "usb/cdcacm.h" #include "uart.h" #include "FreeRTOS.h" #include "task.h" #include "queue.h" +#include "stream_buffer.h" #include <stdint.h> #include <stdio.h> +/* The serial console in use (UART0) */ +extern mxc_uart_regs_t *ConsoleUart; + /* Task ID for the serial handler */ TaskHandle_t serial_task_id = NULL; -/* The serial console in use (UART0) */ -extern mxc_uart_regs_t *ConsoleUart; /* Read queue, filled by both UART and CDCACM */ static QueueHandle_t read_queue; +/* Stream Buffer for handling all writes to serial */ +static StreamBufferHandle_t write_stream_buffer = NULL; + +void serial_init() +{ + /* Setup read queue */ + static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE]; + static StaticQueue_t read_queue_data; + read_queue = xQueueCreateStatic( + SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data + ); + + /* Setup write queue */ + static uint8_t ucWrite_stream_buffer[SERIAL_WRITE_STREAM_BUFFER_SIZE]; + static StaticStreamBuffer_t xStream_buffer_struct; + write_stream_buffer = xStreamBufferCreateStatic( + sizeof(ucWrite_stream_buffer), + 1, + ucWrite_stream_buffer, + &xStream_buffer_struct + ); +} /* * API-call to write a string. Output goes to both CDCACM and UART */ void epic_uart_write_str(const char *str, intptr_t length) { - UART_Write(ConsoleUart, (uint8_t *)str, length); - cdcacm_write((uint8_t *)str, length); - ble_uart_write((uint8_t *)str, length); + if (length == 0) { + return; + } + + /* + * Check if the stream buffer is even initialized yet + */ + if (write_stream_buffer == NULL) { + UART_Write(ConsoleUart, (uint8_t *)str, length); + cdcacm_write((uint8_t *)str, length); + return; + } + + if (xPortIsInsideInterrupt()) { + BaseType_t resched1 = pdFALSE; + BaseType_t resched2 = pdFALSE; + + /* + * Enter a critial section so no other task can write to the + * stream buffer. + */ + uint32_t basepri = __get_BASEPRI(); + taskENTER_CRITICAL_FROM_ISR(); + + xStreamBufferSendFromISR( + write_stream_buffer, str, length, &resched1 + ); + + taskEXIT_CRITICAL_FROM_ISR(basepri); + + if (serial_task_id != NULL) { + xTaskNotifyFromISR( + serial_task_id, + SERIAL_WRITE_NOTIFY, + eSetBits, + &resched2 + ); + } + + /* Yield if this write woke up a higher priority task */ + portYIELD_FROM_ISR(resched1 || resched2); + } else { + size_t bytes_sent = 0; + size_t index = 0; + do { + taskENTER_CRITICAL(); + /* + * Wait time needs to be zero, because we are in a + * critical section. + */ + bytes_sent = xStreamBufferSend( + write_stream_buffer, + &str[index], + length - index, + 0 + ); + index += bytes_sent; + taskEXIT_CRITICAL(); + if (serial_task_id != NULL) { + xTaskNotify( + serial_task_id, + SERIAL_WRITE_NOTIFY, + eSetBits + ); + portYIELD(); + } + } while (index < length); + } } /* @@ -87,7 +176,12 @@ void UART0_IRQHandler(void) static void uart_callback(uart_req_t *req, int error) { BaseType_t xHigherPriorityTaskWoken = pdFALSE; - vTaskNotifyGiveFromISR(serial_task_id, &xHigherPriorityTaskWoken); + xTaskNotifyFromISR( + serial_task_id, + SERIAL_READ_NOTIFY, + eSetBits, + &xHigherPriorityTaskWoken + ); portYIELD_FROM_ISR(xHigherPriorityTaskWoken); } @@ -108,16 +202,8 @@ void serial_enqueue_char(char chr) void vSerialTask(void *pvParameters) { - static uint8_t buffer[sizeof(char) * SERIAL_READ_BUFFER_SIZE]; - static StaticQueue_t read_queue_data; - serial_task_id = xTaskGetCurrentTaskHandle(); - /* Setup read queue */ - read_queue = xQueueCreateStatic( - SERIAL_READ_BUFFER_SIZE, sizeof(char), buffer, &read_queue_data - ); - /* Setup UART interrupt */ NVIC_ClearPendingIRQ(UART0_IRQn); NVIC_DisableIRQ(UART0_IRQn); @@ -131,6 +217,9 @@ void vSerialTask(void *pvParameters) .callback = uart_callback, }; + uint8_t rx_data[20]; + size_t received_bytes; + while (1) { int ret = UART_ReadAsync(ConsoleUart, &read_req); if (ret != E_NO_ERROR && ret != E_BUSY) { @@ -138,18 +227,55 @@ void vSerialTask(void *pvParameters) vTaskDelay(portMAX_DELAY); } - ulTaskNotifyTake(pdTRUE, portTICK_PERIOD_MS * 1000); + ret = ulTaskNotifyTake(pdTRUE, portMAX_DELAY); - if (read_req.num > 0) { - serial_enqueue_char(*read_req.data); - } + if (ret & SERIAL_WRITE_NOTIFY) { + do { + received_bytes = xStreamBufferReceive( + write_stream_buffer, + (void *)rx_data, + sizeof(rx_data), + 0 + ); + + if (received_bytes == 0) { + break; + } + + /* + * The SDK-driver for UART is not reentrant + * which means we need to perform UART writes + * in a critical section. + */ + taskENTER_CRITICAL(); + UART_Write( + ConsoleUart, + (uint8_t *)&rx_data, + received_bytes + ); + taskEXIT_CRITICAL(); - while (UART_NumReadAvail(ConsoleUart) > 0) { - serial_enqueue_char(UART_ReadByte(ConsoleUart)); + cdcacm_write( + (uint8_t *)&rx_data, received_bytes + ); + ble_uart_write( + (uint8_t *)&rx_data, received_bytes + ); + } while (received_bytes > 0); } - while (cdcacm_num_read_avail() > 0) { - serial_enqueue_char(cdcacm_read()); + if (ret & SERIAL_READ_NOTIFY) { + if (read_req.num > 0) { + serial_enqueue_char(*read_req.data); + } + + while (UART_NumReadAvail(ConsoleUart) > 0) { + serial_enqueue_char(UART_ReadByte(ConsoleUart)); + } + + while (cdcacm_num_read_avail() > 0) { + serial_enqueue_char(cdcacm_read()); + } } } } diff --git a/epicardium/modules/usb.c b/epicardium/modules/usb.c new file mode 100644 index 0000000000000000000000000000000000000000..e14032e4e3dccb91342630317786d8c35d363b3a --- /dev/null +++ b/epicardium/modules/usb.c @@ -0,0 +1,322 @@ +/** + * + * Implementation of public epic_usb_ interface + * + * Configuration and FLASH-specific implementation of USB mass storage device + * Configuration of USB CDCACM device + * + * USB descriptors for both are defined here. + * + */ + +#include "epicardium.h" + +#include "modules/filesystem.h" + +#include "usb/cdcacm.h" +#include "usb/mass_storage.h" +#include "usb/descriptors.h" +#include "usb/epc_usb.h" + +#include "modules/log.h" + +#include "mx25lba.h" +#include "msc.h" + +/* memory access callbacks for the mass storage (FLASH) device */ +static int mscmem_init(); +static uint32_t mscmem_size(void); +static int mscmem_read(uint32_t lba, uint8_t *buffer); +static int mscmem_write(uint32_t lba, uint8_t *buffer); +static int mscmem_start(); +static int mscmem_stop(); +static int mscmem_ready(); + +/* USB descriptors, definition at the end of this file */ +static usb_device_descriptor_t device_descriptor_cdcacm + __attribute__((aligned(4))); +static struct config_descriptor_cdcacm config_descriptor_cdcacm + __attribute__((aligned(4))); +static usb_device_descriptor_t device_descriptor_msc + __attribute__((aligned(4))); +static struct config_descriptor_msc config_descriptor_msc + __attribute__((aligned(4))); + +/* definitions of config structs */ +static const msc_mem_t s_mscCallbacks = { + mscmem_init, mscmem_start, mscmem_stop, mscmem_ready, + mscmem_size, mscmem_read, mscmem_write, +}; + +static struct esb_device_descriptors s_dd_cdcacm = { + .device = &device_descriptor_cdcacm, .cdcacm = &config_descriptor_cdcacm +}; + +static struct esb_device_descriptors s_dd_msc = { + .device = &device_descriptor_msc, .msc = &config_descriptor_msc +}; + +static struct esb_config s_cfg_cdcacm = { + .name = "cdcacm", + .init = esb_cdc_init, + .configure = esb_cdc_configure, + .deconfigure = esb_cdc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_cdcacm, + .deviceData = NULL, +}; + +static struct esb_config s_cfg_msc = { + .name = "msc FLASH", + .init = esb_msc_init, + .configure = esb_msc_configure, + .deconfigure = esb_msc_deconfigure, + .deinit = NULL, + + .descriptors = &s_dd_msc, + .deviceData = (void *)&s_mscCallbacks, +}; + +/* function implementations */ + +static int dirty = 0; +static int mscmem_init() +{ + LOG_DEBUG("msc", "mem.init"); + fatfs_detach(); + return mx25_init(); +} + +static uint32_t mscmem_size(void) +{ + return mx25_size(); +} + +static int mscmem_read(uint32_t lba, uint8_t *buffer) +{ + return mx25_read(lba, buffer); +} + +static int mscmem_write(uint32_t lba, uint8_t *buffer) +{ + if (dirty == 0) { + //bootloader_dirty(); + } + dirty = 2; + return mx25_write(lba, buffer); +} + +static int mscmem_start() +{ + return mx25_start(); +} + +static int mscmem_stop() +{ + LOG_DEBUG("msc", "mem.stop"); + int ret = mx25_stop(); + fatfs_schedule_attach(); + return ret; +} + +static int mscmem_ready() +{ + if (dirty) { + dirty--; + if (dirty == 0) { + LOG_DEBUG("msc", "sync"); + mx25_sync(); + //bootloader_clean(); + } + } + return mx25_ready(); +} + +static bool s_fsDetached = false; +int epic_usb_shutdown(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return 0; +} + +int epic_usb_storage(void) +{ + esb_deinit(); + s_fsDetached = true; + return esb_init(&s_cfg_msc); +} + +int epic_usb_cdcacm(void) +{ + esb_deinit(); + if (s_fsDetached) { + fatfs_attach(); + } + return esb_init(&s_cfg_cdcacm); +} + +/* clang-format off */ +static usb_device_descriptor_t device_descriptor_cdcacm = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0110, + .bDeviceClass = CLS_COMM, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x003C, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x00, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_cdcacm config_descriptor_cdcacm = { + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0043, /* sizeof(struct config_descriptor_cdcacm) */ + .bNumInterfaces = 0x02, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xE0, /* (self-powered, remote wakeup) */ + .bMaxPower = 0x01, /* MaxPower is 2ma (units are 2ma/bit) */ + }, + .comm_interface = { /* First Interface Descriptor For Comm Class Interface */ + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x01, + .bInterfaceClass = CLS_COMM, + .bInterfaceSubClass = SCLS_ACM, + .bInterfaceProtocol = PROT_AT_CMDS, + .iInterface = 0x00, + }, + .header_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x00, /* bDescriptorSubtype */ + 0x10, 0x01, /* bcdCDC */ + }, + .call_management = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x01, /* bDescriptorSubtype */ + 0x03, /* bmCapabilities = Device handles call management itself (0x01), management over data class (0x02) */ + 0x01, /* bmDataInterface */ + }, + .acm_functional = { + 0x04, /* bFunctionalLength = 4 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x02, /* bDescriptorSubtype */ + 0x02, /* bmCapabilities */ + }, + .union_functional = { + 0x05, /* bFunctionalLength = 5 */ + DT_FUNCTIONAL,/* bDescriptorType */ + 0x06, /* bDescriptorSubtype */ + 0x00, /* bmMasterInterface */ + 0x01, /* bmSlaveInterface0 */ + }, + .endpoint_notify = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_NOTIFY, + .bmAttributes = ATTR_INTERRUPT, + .wMaxPacketSize = 0x0040, + .bInterval = 0xff, + }, + .data_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x01, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_DATA, + .bInterfaceSubClass = 0x00, + .bInterfaceProtocol = 0x00, + .iInterface = 0x00, //not 1? + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_CDCACM_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_CDCACM_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; + +static usb_device_descriptor_t device_descriptor_msc = { + .bLength = 0x12, + .bDescriptorType = DT_DEVICE, + .bcdUSB = 0x0200, + .bDeviceClass = CLS_UNSPECIFIED, + .bDeviceSubClass = 0x00, + .bDeviceProtocol = 0x00, + .bMaxPacketSize = 0x40, + .idVendor = VNDR_MAXIM, + .idProduct = 0x4402, + .bcdDevice = 0x0100, + .iManufacturer = 0x01, + .iProduct = 0x02, + .iSerialNumber = 0x03, + .bNumConfigurations = 0x01 +}; + +static struct config_descriptor_msc config_descriptor_msc = +{ + .config = { + .bLength = 0x09, + .bDescriptorType = DT_CONFIG, + .wTotalLength = 0x0020, + .bNumInterfaces = 0x01, + .bConfigurationValue= 0x01, + .iConfiguration = 0x00, + .bmAttributes = 0xC0, /* (self-powered, no remote wakeup) */ + .bMaxPower = 0x32, + }, + .msc_interface = { + .bLength = 0x09, + .bDescriptorType = DT_INTERFACE, + .bInterfaceNumber = 0x00, + .bAlternateSetting = 0x00, + .bNumEndpoints = 0x02, + .bInterfaceClass = CLS_MASS_STOR, + .bInterfaceSubClass = SCLS_SCSI_CMDS, + .bInterfaceProtocol = PROT_BULK_TRANS, + .iInterface = 0x00, + }, + .endpoint_out = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = ESB_ENDPOINT_MSC_OUT, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00, + }, + .endpoint_in = { + .bLength = 0x07, + .bDescriptorType = DT_ENDPOINT, + .bEndpointAddress = 0x80 | ESB_ENDPOINT_MSC_IN, + .bmAttributes = ATTR_BULK, + .wMaxPacketSize = 0x0040, + .bInterval = 0x00 + } +}; +/* clang-format on */ \ No newline at end of file diff --git a/epicardium/modules/watchdog.c b/epicardium/modules/watchdog.c index a538035d685673ff1e3976037211c1e8e766bb3c..fa8792cce1f8d6dc9caccf7531d9d6034439b7b4 100644 --- a/epicardium/modules/watchdog.c +++ b/epicardium/modules/watchdog.c @@ -14,8 +14,34 @@ static void watchdog_clearer_callback() WDT_ResetTimer(MXC_WDT0); } +void watchdog_init() +{ + /* + * Don't enable the the watchdog when a debugger is connected. + */ + if ((CoreDebug->DHCSR & CoreDebug_DHCSR_C_DEBUGEN_Msk) != 0) { + return; + } + + sys_cfg_wdt_t wdt_cfg = NULL; + WDT_Init(MXC_WDT0, wdt_cfg); + + if (WDT_GetResetFlag(MXC_WDT0)) { + WDT_ClearResetFlag(MXC_WDT0); + LOG_INFO("watchdog", "Last reset was due to watchdog timeout"); + } + + WDT_Enable(MXC_WDT0, 1); + WDT_SetResetPeriod( + MXC_WDT0, + WDT_PERIOD_2_28); /* Clocked by PCLK at 50MHz, reset at 2^28 ticks = 5.4 seconds */ + WDT_EnableReset(MXC_WDT0, 1); +} + void watchdog_clearer_init() { + WDT_ResetTimer(MXC_WDT0); + clearer_timer = xTimerCreateStatic( "watchdog_clearer_timer", CLEAR_PERIOD, diff --git a/epicardium/usb/cdcacm.c b/epicardium/usb/cdcacm.c new file mode 100644 index 0000000000000000000000000000000000000000..ea046721a99b672c114831b5c6da752a6579a5b6 --- /dev/null +++ b/epicardium/usb/cdcacm.c @@ -0,0 +1,130 @@ +/** + * + * epicardium-specific implemnetation of cdcacm device + * services CDCACM API + */ + +#include "usb/epc_usb.h" +#include "usb/cdcacm.h" +#include "usb/descriptors.h" + +#include <stdio.h> +#include <stddef.h> +#include <errno.h> + +#include "usb.h" +#include "usb_event.h" +#include "cdc_acm.h" + +#include "modules/log.h" +#include "modules/modules.h" + +#include "FreeRTOS.h" +#include "task.h" + +static inline struct config_descriptor_cdcacm * +descriptors(struct esb_config *self) +{ + return self->descriptors->cdcacm; +} + +/******************************************************************************/ +static volatile int usb_read_complete; // SWYM: only ever written to +static int cb_acm_read_ready(void) +{ + usb_read_complete = 1; + return 0; +} + +int esb_cdc_init(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "init"); + struct config_descriptor_cdcacm *dsc = descriptors(self); + usb_read_complete = 0; + acm_register_callback( + ACM_CB_READ_READY, + cb_acm_read_ready); //SWYM: actually not needed + return acm_init(&dsc->comm_interface); +} + +int esb_cdc_configure(struct esb_config *self) +{ + struct config_descriptor_cdcacm *dsc = descriptors(self); + + //acm_configure does not keep the amc_cfg_t pointer around + //so stack-local lifetime is fine + acm_cfg_t acm_cfg = { + .in_ep = dsc->endpoint_in.bEndpointAddress & 0x0f, + .in_maxpacket = MXC_USBHS_MAX_PACKET, + .out_ep = dsc->endpoint_out.bEndpointAddress, + .out_maxpacket = MXC_USBHS_MAX_PACKET, + .notify_ep = dsc->endpoint_notify.bEndpointAddress & 0x0f, + .notify_maxpacket = MXC_USBHS_MAX_PACKET, + }; + + LOG_DEBUG( + "cdcacm", + "configure, endpoints %d,%d,%d", + acm_cfg.in_ep, + acm_cfg.out_ep, + acm_cfg.notify_ep + ); + + return acm_configure(&acm_cfg); +} + +int esb_cdc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("cdcacm", "deconfigure"); + return acm_deconfigure(); +} + +int cdcacm_num_read_avail(void) +{ + return acm_canread(); +} + +uint8_t cdcacm_read(void) +{ + while (acm_canread() <= 0) { + } + + uint8_t buf; + acm_read(&buf, 1); + return buf; +} + +void cdcacm_write(uint8_t *data, int len) +{ + static int lockup_disable = 0; + if (acm_present() && !lockup_disable) { + int ret = acm_write(data, len); + if (ret < 0) { + LOG_ERR("cdcacm", "fifo lockup detected"); + lockup_disable = 1; + } else if (ret != len) { + LOG_WARN( + "cdcacm", "write length mismatch, got %d", ret + ); + } + } +} + +/******************************************************************************/ + +extern TaskHandle_t serial_task_id; +void USB_IRQHandler(void) +{ + usb_event_handler(); + + if (serial_task_id != NULL) { + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + xTaskNotifyFromISR( + serial_task_id, + SERIAL_READ_NOTIFY, + eSetBits, + &xHigherPriorityTaskWoken + ); + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} diff --git a/epicardium/usb/cdcacm.h b/epicardium/usb/cdcacm.h new file mode 100644 index 0000000000000000000000000000000000000000..6683e05558aa06ffef793e3f5a8ab4fe03df1a35 --- /dev/null +++ b/epicardium/usb/cdcacm.h @@ -0,0 +1,15 @@ +#ifndef CDCACM_H +#define CDCACM_H + +#include "usb/epc_usb.h" + +// callbacks for esb_config +int esb_cdc_init(struct esb_config* self); +int esb_cdc_configure(struct esb_config* self); +int esb_cdc_deconfigure(struct esb_config* self); + +int cdcacm_num_read_avail(void); +uint8_t cdcacm_read(void); +void cdcacm_write(uint8_t *data, int len); + +#endif diff --git a/epicardium/usb/descriptors.h b/epicardium/usb/descriptors.h new file mode 100644 index 0000000000000000000000000000000000000000..2fb2cc452ffec6cfe35106c5e311df1fc09a67ac --- /dev/null +++ b/epicardium/usb/descriptors.h @@ -0,0 +1,83 @@ +#ifndef _DESCRIPTORS_H_ +#define _DESCRIPTORS_H_ + +#include <stdint.h> +#include "usb.h" + +#define ESB_ENDPOINT_MSC_IN 2 +#define ESB_ENDPOINT_MSC_OUT 1 +#define ESB_ENDPOINT_CDCACM_NOTIFY 3 +#define ESB_ENDPOINT_CDCACM_IN 2 +#define ESB_ENDPOINT_CDCACM_OUT 1 + +/* device types */ +#define DT_DEVICE 0x01 +#define DT_CONFIG 0x02 +#define DT_INTERFACE 0x04 +#define DT_ENDPOINT 0x05 +#define DT_FUNCTIONAL 0x24 + +/* interface classes */ +#define CLS_UNSPECIFIED 0x00 +#define CLS_COMM 0x02 +#define CLS_MASS_STOR 0x08 +#define CLS_DATA 0x0a + +/* sub-classes */ +#define SCLS_NONE 0x00 +#define SCLS_ACM 0x02 +#define SCLS_SCSI_CMDS 0x06 + +/* interface protocols */ +#define PROT_AT_CMDS 0x01 +#define PROT_BULK_TRANS 0x50 + +/* endpoint attributes */ +#define ATTR_BULK 0x02 +#define ATTR_INTERRUPT 0x03 + + +#define VNDR_MAXIM 0x0B6A + +#if defined(__GNUC__) +#define PACKED_STRUCT struct __attribute__((packed)) +#else +#define PACKED_STRUCT __packed struct +#endif + + + + +PACKED_STRUCT +config_descriptor_cdcacm { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t comm_interface; + uint8_t header_functional[5]; + uint8_t call_management[5]; + uint8_t acm_functional[4]; + uint8_t union_functional[5]; + usb_endpoint_descriptor_t endpoint_notify; + usb_interface_descriptor_t data_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +PACKED_STRUCT +config_descriptor_msc { + usb_configuration_descriptor_t config; + usb_interface_descriptor_t msc_interface; + usb_endpoint_descriptor_t endpoint_out; + usb_endpoint_descriptor_t endpoint_in; +}; + +struct esb_device_descriptors { + usb_device_descriptor_t* device; + union { + usb_configuration_descriptor_t* config; + struct config_descriptor_cdcacm* cdcacm; + struct config_descriptor_msc* msc; + }; +}; + + +#endif /* _DESCRIPTORS_H_ */ diff --git a/epicardium/usb/epc_usb.c b/epicardium/usb/epc_usb.c new file mode 100644 index 0000000000000000000000000000000000000000..b99c34a075689a1f470fd66119359f844547bd6a --- /dev/null +++ b/epicardium/usb/epc_usb.c @@ -0,0 +1,459 @@ +/** + * + * + * Core of the USB implementation: + * + * - device independent + * - handles setup of MAXUSB stack + * - handles events and state of the MAXUSB stack + * + * Also contains definitions of device-independent String descriptors + * + * + * + * + * + * swym's USB ramblings: + * + * setting up USB basically consists of two parts: + * + * - set up descriptors + * - initialize the corresponding MAXUSB stacks - acm, msc... + * + * at the moment, the descriptors are statically configured in + * descriptors.h several descriptors point to enumeration descriptors + * via iFooBar indices. Those enumeration descriptors are registered + * via enum_register_descriptor(type, desc, idx), where the idx passed + * corresponds to the iFooBar index mentioned above. + * + * There are several callbacks, some of which do not perform anything meaningful + * at the moment. For example usb_write_callback sets a global flag that is never + * read from. These will be removed later on. + * + * The initialization routines of acm & msc refer to the descriptors from which, + * among other things, they get their enpoint IDs. These are hard-coded right now + * but it would make sense to allocate these IDs at runtime. There is, AFAICT, no + * reason for these endpoint IDs to be 3, 1, 2 in the CDCACM case for example. + * + * Allocating those at runtime would also make the setup less rigid: we can have all + * combinations of {cdcacm, storage} interfaces without the awkward descriptor hackery + * in epc_usb_init. + * + * To generalize even further, the descriptors could be malloced. The total length of + * the descriptor structure can easily be derived from the number of interfaces to be + * initialized. The memory allocated will then be split up (with alignment of the single + * descriptor structs taken care of) in a simple bump-allocator strategy on demand. + * + * + */ +#include "usb/epc_usb.h" + +#include <assert.h> +#include <stdio.h> +#include <stddef.h> + +#include <FreeRTOS.h> +#include <task.h> +#include <timers.h> + +#include "mxc_config.h" +#include "mxc_sys.h" +#include "mxc_delay.h" + +// MAXUSB includes +#include "usb.h" +#include "usb_event.h" +#include "enumerate.h" +#include "msc.h" +#include "cdc_acm.h" + +#include "usb/descriptors.h" +#include "modules/log.h" +#include "modules/filesystem.h" + +//#define USE_REMOTE_WAKE_ENABLE + +/***** Function Prototypes *****/ +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata); +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata); +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata); +#endif +static int cb_usb_event(maxusb_event_t evt, void *data); +static int cb_usb_shutdown(); +static int cb_usb_init(); +static void usb_app_sleep(void); +static void usb_app_wakeup(void); +/* needed for usb_opts. mxc_delay() takes unsigned long, so can't use it directly */ +static void delay_us(unsigned int usec); +static void device_deinit(); +static bool device_deconfigure(); +static int device_configure(); + +volatile int suspended; +#ifdef USE_REMOTE_WAKE_ENABLE +int remote_wake_en; //SWYM: unused, only written to! +#endif + +static StaticTimer_t x; +static TimerHandle_t timerWakeup = NULL; +static void cb_timerReset(TimerHandle_t t); + +static struct esb_config s_device = { 0 }; + +__attribute__((aligned(4))) uint8_t lang_id_desc[] = { + 0x04, /* bLength */ + 0x03, /* bDescriptorType */ + 0x09, + 0x04 /* bString = wLANGID (see usb_20.pdf 9.6.7 String) */ +}; + +__attribute__((aligned(4))) uint8_t mfg_id_desc[] = { + 0x22, /* bLength */ + 0x03, /* bDescriptorType */ + 'M', 0, 'a', 0, 'x', 0, 'i', 0, 'm', 0, ' ', 0, 'I', 0, 'n', 0, + 't', 0, 'e', 0, 'g', 0, 'r', 0, 'a', 0, 't', 0, 'e', 0, 'd', 0, +}; + +__attribute__((aligned(4))) uint8_t prod_id_desc[] = { + 0x24, /* bLength */ + 0x03, /* bDescriptorType */ + 'C', 0, 'A', 0, 'R', 0, 'D', 0, '1', 0, '0', 0, ' ', 0, 'U', 0, 'S', 0, + 'B', 0, ' ', 0, 'G', 0, 'A', 0, 'D', 0, 'G', 0, 'E', 0, 'T', 0, +}; + +/* Not currently used (see device descriptor), but could be enabled if desired */ +__attribute__((aligned(4))) +uint8_t serial_id_desc[] = { 0x14, /* bLength */ + 0x03, /* bDescriptorType */ + 'c', 0, 'a', 0, 'a', 0, 'a', 0, 'a', + 0, 'd', 0, '1', 0, '0', 0, '0', 0 }; + +int cb_usb_init() +{ + const sys_cfg_usbhs_t sys_usbhs_cfg = NULL; + LOG_DEBUG("usb", "init"); + return SYS_USBHS_Init(&sys_usbhs_cfg); +} + +volatile bool shutDownCompleted; +void do_usb_shutdown() +{ + LOG_DEBUG("usb", "shutting down..."); + shutDownCompleted = false; + usb_shutdown(); + for (int i = 0; i < 10000 && !shutDownCompleted; ++i) { + } +} +int cb_usb_shutdown() +{ + SYS_USBHS_Shutdown(); + SYS_Reset_Periph(SYS_RESET_USB); + device_deinit(); + LOG_DEBUG("usb", "shutdown complete"); + shutDownCompleted = true; + return 0; +} + +/* User-supplied function to delay usec micro-seconds */ +static void delay_us(unsigned int usec) +{ + /* mxc_delay() takes unsigned long, so can't use it directly */ + mxc_delay(usec); +} + +static void device_deinit() +{ + esb_cfg_handler deinit = s_device.deinit; + s_device.deinit = NULL; + s_device.configure = NULL; + s_device.deconfigure = NULL; + if (deinit) { + LOG_DEBUG("usb", "deinit"); + deinit(&s_device); + } +} + +//de-configure last device, if any +static volatile bool s_configured = false; +static bool device_deconfigure() +{ + if (s_configured) { + s_configured = false; + if (s_device.deconfigure) { + LOG_DEBUG("usb", "deconfigure"); + enum_clearconfig(); + s_device.deconfigure(&s_device); + return true; + } + } + return false; +} + +static int device_configure() +{ + if (!s_configured && s_device.configure) { + s_configured = true; + LOG_DEBUG("usb", "configure"); + return s_device.configure(&s_device); + } + return 0; +} + +static volatile bool s_connected = false; +static void disconnect() +{ + if (s_connected) { + LOG_DEBUG("usb", "disconnect"); + s_connected = false; + usb_disconnect(); + // SYS_Reset_Periph(SYS_RESET_USB); + mxc_delay(10000); + } +} +static void connect() +{ + s_connected = true; + usb_connect(); +} + +static volatile bool s_initialized = false; +void esb_deinit(void) +{ + if (s_initialized) { + s_initialized = false; + disconnect(); + device_deconfigure(); + do_usb_shutdown(); + device_deinit(); + } +} +int esb_init(struct esb_config *cfg) +{ + esb_deinit(); + if (cfg == NULL) { + return 0; + } + if (timerWakeup == NULL) { + timerWakeup = xTimerCreateStatic( + "timerWakeup", /* name */ + pdMS_TO_TICKS(50), /* period/time */ + pdFALSE, /* auto reload */ + NULL, /* timer ID */ + cb_timerReset, + &x); + } + + maxusb_cfg_options_t usb_opts; + + /* Initialize state */ + suspended = 0; +#ifdef USE_REMOTE_WAKE_ENABLE + remote_wake_en = 0; +#endif + + /* Start out in full speed */ + usb_opts.enable_hs = 0; + usb_opts.delay_us = delay_us; + usb_opts.init_callback = cb_usb_init; + usb_opts.shutdown_callback = cb_usb_shutdown; + + /* Initialize the usb module */ + if (usb_init(&usb_opts) != 0) { + LOG_ERR("usb", "usb_init() failed"); + return -EIO; + } + + /* Initialize the enumeration module */ + if (enum_init() != 0) { + do_usb_shutdown(); + LOG_ERR("usb", "enum_init() failed"); + return -EIO; + } + + LOG_INFO("usb", "initializing device %s", cfg->name); + if (cfg->init(cfg)) { + enum_clearconfig(); + do_usb_shutdown(); + LOG_ERR("usb", "device init failed"); + return -EIO; + } + + s_initialized = true; + s_device = *cfg; + + enum_register_descriptor( + ENUM_DESC_DEVICE, (uint8_t *)cfg->descriptors->device, 0 + ); + enum_register_descriptor( + ENUM_DESC_CONFIG, (uint8_t *)cfg->descriptors->config, 0 + ); + enum_register_descriptor(ENUM_DESC_STRING, lang_id_desc, 0); + enum_register_descriptor(ENUM_DESC_STRING, mfg_id_desc, 1); + enum_register_descriptor(ENUM_DESC_STRING, prod_id_desc, 2); + enum_register_descriptor(ENUM_DESC_STRING, serial_id_desc, 3); + //enum_register_descriptor(ENUM_DESC_STRING, cdcacm_func_desc, 4); + //enum_register_descriptor(ENUM_DESC_STRING, msc_func_desc, 5); + + /* Handle configuration */ + enum_register_callback(ENUM_SETCONFIG, cb_usb_setconfig, NULL); +#ifdef USE_REMOTE_WAKE_ENABLE + /* Handle feature set/clear */ + enum_register_callback(ENUM_SETFEATURE, cb_usb_setfeature, NULL); + enum_register_callback(ENUM_CLRFEATURE, cb_usb_clrfeature, NULL); +#endif + + /* Register callbacks */ + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); + + /* Start with USB in low power mode */ + usb_app_sleep(); + /* TODO: Fix priority */ + NVIC_SetPriority(USB_IRQn, 6); + NVIC_EnableIRQ(USB_IRQn); + + return 0; +} + +static int cb_usb_setconfig(usb_setup_pkt *sud, void *cbdata) +{ + LOG_DEBUG("usb", "setconfig %d", sud->wValue); + if (sud->wValue == s_device.descriptors->config->bConfigurationValue) { + return device_configure(); + } else if (sud->wValue == 0) { + device_deconfigure(); + return 0; + } + return -1; +} + +#ifdef USE_REMOTE_WAKE_ENABLE +static int cb_usb_setfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 1; + return 0; + } + return -1; +} + +static int cb_usb_clrfeature(usb_setup_pkt *sud, void *cbdata) +{ + if (sud->wValue == FEAT_REMOTE_WAKE) { + remote_wake_en = 0; + return 0; + } + return -1; +} +#endif + +/******************************************************************************/ +static void usb_app_sleep(void) +{ + /* TODO: Place low-power code here */ + suspended = 1; +} + +/******************************************************************************/ +static void usb_app_wakeup(void) +{ + /* TODO: Place low-power code here */ + suspended = 0; +} + +static void cb_timerReset(TimerHandle_t t) +{ + (void)t; + LOG_DEBUG("usb", "cb_timerReset %08x", usb_get_status()); + + LOG_DEBUG("usb", "SYS_USBHS_Shutdown"); + SYS_USBHS_Shutdown(); + LOG_DEBUG("usb", "SYS_Reset_Periph"); + SYS_Reset_Periph(SYS_RESET_USB); + + //copy-paste from esb_init(), need to refactor + maxusb_cfg_options_t usb_opts; + usb_opts.enable_hs = 0; + usb_opts.delay_us = delay_us; + usb_opts.init_callback = cb_usb_init; + usb_opts.shutdown_callback = cb_usb_shutdown; + + /* Initialize the usb module */ + if (usb_init(&usb_opts) != 0) { + LOG_ERR("usb", "usb_init() failed"); + return; + } + usb_event_enable(MAXUSB_EVENT_NOVBUS, cb_usb_event, NULL); + usb_event_enable(MAXUSB_EVENT_VBUS, cb_usb_event, NULL); +} + +static void scheduleReset() +{ + LOG_DEBUG("usb", "scheduleReset"); + xTimerChangePeriodFromISR(timerWakeup, pdMS_TO_TICKS(500), NULL); +} + +#if LOG_ENABLE_DEBUG +static const char *maxusb_event_string(maxusb_event_t evt) +{ + static const char *names[MAXUSB_NUM_EVENTS] = { + [MAXUSB_EVENT_DPACT] = "DPACT", + [MAXUSB_EVENT_RWUDN] = "RWUDN", + [MAXUSB_EVENT_BACT] = "BACT", + [MAXUSB_EVENT_BRST] = "BRST", + [MAXUSB_EVENT_SUSP] = "SUSP", + [MAXUSB_EVENT_NOVBUS] = "NOVBUS", + [MAXUSB_EVENT_VBUS] = "VBUS", + [MAXUSB_EVENT_BRSTDN] = "BRSTDN", + [MAXUSB_EVENT_SUDAV] = "SUDAV", + }; + return evt < MAXUSB_NUM_EVENTS ? names[evt] : "<INVALID>"; +} +#endif + +/******************************************************************************/ +static int cb_usb_event(maxusb_event_t evt, void *data) +{ + static int s_VBUS_SUSP_sequence = 0; + LOG_DEBUG("usb", "event %s (%d)\n", maxusb_event_string(evt), evt); + + switch (evt) { + case MAXUSB_EVENT_NOVBUS: + usb_event_disable(MAXUSB_EVENT_BRST); + usb_event_disable(MAXUSB_EVENT_SUSP); + usb_event_disable(MAXUSB_EVENT_DPACT); + usb_disconnect(); + device_deconfigure(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_VBUS: + s_VBUS_SUSP_sequence = 1; + usb_event_clear(MAXUSB_EVENT_BRST); + usb_event_enable(MAXUSB_EVENT_BRST, cb_usb_event, NULL); + usb_event_clear(MAXUSB_EVENT_SUSP); + usb_event_enable(MAXUSB_EVENT_SUSP, cb_usb_event, NULL); + connect(); + usb_app_sleep(); + break; + case MAXUSB_EVENT_BRST: + usb_app_wakeup(); + device_deconfigure(); + suspended = 0; + break; + case MAXUSB_EVENT_SUSP: + if (!s_VBUS_SUSP_sequence) { + scheduleReset(); + } + s_VBUS_SUSP_sequence = 0; + //usb_app_sleep(); + break; + case MAXUSB_EVENT_DPACT: + usb_app_wakeup(); + break; + default: + break; + } + + return 0; +} diff --git a/epicardium/usb/epc_usb.h b/epicardium/usb/epc_usb.h new file mode 100644 index 0000000000000000000000000000000000000000..c6917bd328b270f240ab38a7c8e11f10aa4fea21 --- /dev/null +++ b/epicardium/usb/epc_usb.h @@ -0,0 +1,32 @@ +#ifndef EPICARDIUM_USB_EPC_USB_H_INCLUDED +#define EPICARDIUM_USB_EPC_USB_H_INCLUDED + +/** + * + * EPC - it's not just a Universal Serial Bus, + * it's an Epic Serial Bus! + */ + + +#include <stdbool.h> +#include <stddef.h> +#include <stdint.h> + +struct esb_config; +typedef int (*esb_cfg_handler)(struct esb_config* self); + +struct esb_config { + const char* name; + esb_cfg_handler init; + esb_cfg_handler configure; + esb_cfg_handler deconfigure; + esb_cfg_handler deinit; + + struct esb_device_descriptors* descriptors; + void* deviceData; +}; + +int esb_init(struct esb_config* cfg); +void esb_deinit(void); + +#endif//EPICARDIUM_USB_EPC_USB_H_INCLUDED diff --git a/epicardium/usb/mass_storage.c b/epicardium/usb/mass_storage.c new file mode 100644 index 0000000000000000000000000000000000000000..b3c6c0f6cd04efb5cf02e8eb898b3eef7b02efe3 --- /dev/null +++ b/epicardium/usb/mass_storage.c @@ -0,0 +1,55 @@ +/** + * backend-independent implementation of the mass storage device + * services MSC API and esb_cfg API + */ + +#include "usb/mass_storage.h" +#include "usb/epc_usb.h" +#include "usb/descriptors.h" + +#include "modules/log.h" + +#include "msc.h" +#include "usb.h" +#include "max32665.h" + +#define VENDOR_STRING "CCC" +#define PRODUCT_STRING "card10" +#define VERSION_STRING "1.0" + +static inline struct config_descriptor_msc *descriptors(struct esb_config *self) +{ + return self->descriptors->msc; +} + +int esb_msc_configure(struct esb_config *self) +{ + LOG_DEBUG("msc", "configure"); + struct config_descriptor_msc *dsc = descriptors(self); + const msc_cfg_t msc_cfg = { + dsc->endpoint_out.bEndpointAddress, + MXC_USBHS_MAX_PACKET, /* OUT max packet size */ + dsc->endpoint_in.bEndpointAddress & 0x0f, + MXC_USBHS_MAX_PACKET, /* IN max packet size */ + }; + return msc_configure(&msc_cfg); +} + +int esb_msc_deconfigure(struct esb_config *self) +{ + LOG_DEBUG("msc", "deconfigure"); + (void)self; + return msc_deconfigure(); +} + +int esb_msc_init(struct esb_config *self) +{ + LOG_DEBUG("msc", "init"); + static const msc_idstrings_t ids = { VENDOR_STRING, + PRODUCT_STRING, + VERSION_STRING }; + + msc_mem_t *mem = self->deviceData; + struct config_descriptor_msc *dsc = descriptors(self); + return msc_init(&dsc->msc_interface, &ids, mem); +} diff --git a/epicardium/usb/mass_storage.h b/epicardium/usb/mass_storage.h new file mode 100644 index 0000000000000000000000000000000000000000..77c36bcecd433be56305a94a180945b6dba38dff --- /dev/null +++ b/epicardium/usb/mass_storage.h @@ -0,0 +1,10 @@ +#ifndef EPICARDIUM_USB_MSC_H_INCLUDED +#define EPICARDIUM_USB_MSC_H_INCLUDED + +#include "usb/epc_usb.h" + +int esb_msc_configure(struct esb_config* self); +int esb_msc_deconfigure(struct esb_config* self); +int esb_msc_init(struct esb_config* self); + +#endif//EPICARDIUM_USB_MSC_H_INCLUDED diff --git a/lib/gfx/gfx.c b/lib/gfx/gfx.c index dd92ecdc0cd736ba26ab15640b9afd7066e3cbed..9d27d2a5530e168e0ba4cbc795c2fcf27e135885 100644 --- a/lib/gfx/gfx.c +++ b/lib/gfx/gfx.c @@ -198,7 +198,7 @@ static void plot_line_low( int d = 2 * dy - dx; int y = y0; - for (int x = x0; x < x1; x++) { + for (int x = x0; x <= x1; x++) { if (t > 1) { gfx_circle_fill(reg, x, y, t, c); } else { @@ -231,7 +231,7 @@ static void plot_line_high( int d = 2 * dx - dy; int x = x0; - for (int y = y0; y < y1; y++) { + for (int y = y0; y <= y1; y++) { if (t > 1) { gfx_circle_fill(reg, x, y, t, c); } else { diff --git a/preload/apps/card10_nickname/__init__.py b/preload/apps/card10_nickname/__init__.py index 0e89c38916b21c2b45d92cf60fe5bcdf31fce8dd..16fc8ad00869017bca56a5a329e3eeb13b074225 100644 --- a/preload/apps/card10_nickname/__init__.py +++ b/preload/apps/card10_nickname/__init__.py @@ -343,4 +343,6 @@ else: ([255, 255, 255], [255, 255, 255]), ([0, 0, 0], [0, 0, 0]), ([0, 0, 0], [0, 0, 0]), + 0, + (0, [255, 0, 255], [255, 0, 255], [255, 0, 255]), ) diff --git a/preload/menu.py b/preload/menu.py index 890b888403cfc618aaacb18ccc21490febfaf5b8..e0a84a84169f6a0da3f1e64d6a4aa9eb3a4f8ee4 100644 --- a/preload/menu.py +++ b/preload/menu.py @@ -31,7 +31,8 @@ def read_metadata(app_folder): with open(info_file) as f: information = f.read() return ujson.loads(information) - except BaseException as e: + except Exception as e: + print("Failed to read metadata for %s" % (app_folder)) sys.print_exception(e) return { "author": "", @@ -267,4 +268,14 @@ def main(): if __name__ == "__main__": - main() + try: + main() + except Exception as e: + sys.print_exception(e) + with display.open() as d: + d.clear(color.COMMYELLOW) + d.print("Menu", posx=52, posy=20, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) + d.print("crashed", posx=31, posy=40, fg=color.COMMYELLOW_DARK, bg=color.COMMYELLOW) + d.update() + utime.sleep(2) + os.exit(1) diff --git a/pycardium/modules/py/leds.py b/pycardium/modules/py/leds.py index 0cbf2c3fe3351088e6c89d16fbf01730c5718d49..ad5563cfb2a3c8fca7bf4a4909ac460ba0e86808 100644 --- a/pycardium/modules/py/leds.py +++ b/pycardium/modules/py/leds.py @@ -41,7 +41,13 @@ def set_flashlight(on): This LED can serve as a flashlight if worn on the left wrist or as a rad tattoo illuminator if worn on the right wrist. - :param bool on: Side LED on if true. + .. warning:: + + Because of a small error in the Harmonic Board layout, we could not + populate the flashlight LEDs in the production run. You can handsolder + it, though you have to reverse the direction. + + :param bool on: Side LED on if ``True``. """ sys_leds.set_flashlight(on) diff --git a/pycardium/modules/py/pride.py b/pycardium/modules/py/pride.py index 3bfb29b601a41c77a921463908a0b1d5b509e105..867e16c05d27f0881765c070303a823e20dae95a 100644 --- a/pycardium/modules/py/pride.py +++ b/pycardium/modules/py/pride.py @@ -14,7 +14,7 @@ flags["trans"] = [ [247, 168, 184], [85, 205, 252], ] -flags["bi"] = [[214, 2, 112], [155, 79, 150], [0, 56, 168]] +flags["bi"] = [[214, 2, 112], [214, 2, 112], [155, 79, 150], [0, 56, 168], [0, 56, 168]] flags["ace"] = [[1, 1, 1], [164, 164, 164], [255, 255, 255], [150, 0, 150]] flags["greyace"] = [ [150, 0, 150], diff --git a/pycardium/modules/py/simple_menu.py b/pycardium/modules/py/simple_menu.py index 6450e6a1cafd0cf686a4ac1930a1b15ab65ce544..42b117cc7998ac94397bee646f1c3fa82538af11 100644 --- a/pycardium/modules/py/simple_menu.py +++ b/pycardium/modules/py/simple_menu.py @@ -167,7 +167,7 @@ class Menu: ) self.disp.line(4, 22, 11, 29, col=self.color_sel, size=2) - self.disp.line(3, 37, 11, 29, col=self.color_sel, size=2) + self.disp.line(4, 36, 11, 29, col=self.color_sel, size=2) self.disp.update()