/* * This file is part of the MicroPython project, http://micropython.org/ * * The MIT License (MIT) * * Copyright (c) 2020 Scott Shawcroft for Adafruit Industries * * 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 THE * AUTHORS OR COPYRIGHT HOLDERS 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. */ #include #include "py/mpconfig.h" #include "py/mpstate.h" #include "py/stackctrl.h" #include "supervisor/background_callback.h" #include "supervisor/workflow.h" #include "supervisor/serial.h" #include "supervisor/shared/workflow.h" #if CIRCUITPY_BLEIO #include "shared-bindings/_bleio/__init__.h" #include "supervisor/shared/bluetooth/bluetooth.h" #endif #if CIRCUITPY_USB #include "supervisor/usb.h" #include "tusb.h" #endif #if CIRCUITPY_WEB_WORKFLOW #include "supervisor/shared/web_workflow/web_workflow.h" static background_callback_t workflow_background_cb = {NULL, NULL}; #endif // Called during a VM reset. Doesn't actually reset things. void supervisor_workflow_reset(void) { #if CIRCUITPY_BLEIO supervisor_start_bluetooth(); #endif #if CIRCUITPY_WEB_WORKFLOW bool result = supervisor_start_web_workflow(true); if (workflow_background_cb.fun) { if (result) { supervisor_workflow_request_background(); } } #endif } void supervisor_workflow_request_background(void) { #if CIRCUITPY_WEB_WORKFLOW if (workflow_background_cb.fun) { workflow_background_cb.data = NULL; background_callback_add_core(&workflow_background_cb); } else { // Unblock polling thread if necessary socketpool_socket_poll_resume(); } #endif } // Return true if host has completed connection to us (such as USB enumeration). bool supervisor_workflow_active(void) { #if CIRCUITPY_USB // Eventually there might be other non-USB workflows, such as BLE. // tud_ready() checks for usb mounted and not suspended. return tud_ready(); #else return false; #endif } void supervisor_workflow_start(void) { // Start USB after giving boot.py a chance to tweak behavior. #if CIRCUITPY_USB // Setup USB connection after heap is available. // It needs the heap to build descriptors. usb_init(); #endif // Set up any other serial connection. serial_init(); #if CIRCUITPY_BLEIO bleio_reset(); supervisor_bluetooth_enable_workflow(); supervisor_start_bluetooth(); #endif #if CIRCUITPY_WEB_WORKFLOW if (supervisor_start_web_workflow(false)) { // Enable background callbacks if web_workflow startup successful memset(&workflow_background_cb, 0, sizeof(workflow_background_cb)); workflow_background_cb.fun = supervisor_web_workflow_background; } #endif #if CIRCUITPY_USB_KEYBOARD_WORKFLOW usb_keyboard_init(); #endif } FRESULT supervisor_workflow_mkdir_parents(FATFS *fs, char *path) { FRESULT result = FR_OK; // Make parent directories. for (size_t j = 1; j < strlen(path); j++) { if (path[j] == '/') { path[j] = '\0'; result = f_mkdir(fs, path); path[j] = '/'; if (result != FR_OK && result != FR_EXIST) { return result; } } } // Make the target directory. return f_mkdir(fs, path); } FRESULT supervisor_workflow_delete_directory_contents(FATFS *fs, const TCHAR *path) { FF_DIR dir; FILINFO file_info; // Check the stack since we're putting paths on it. if (mp_stack_usage() >= MP_STATE_THREAD(stack_limit)) { return FR_INT_ERR; } FRESULT res = FR_OK; while (res == FR_OK) { res = f_opendir(fs, &dir, path); if (res != FR_OK) { break; } res = f_readdir(&dir, &file_info); // We close and reopen the directory every time since we're deleting // entries and it may invalidate the directory handle. f_closedir(&dir); if (res != FR_OK || file_info.fname[0] == '\0') { break; } size_t pathlen = strlen(path); size_t fnlen = strlen(file_info.fname); TCHAR full_path[pathlen + 1 + fnlen]; memcpy(full_path, path, pathlen); full_path[pathlen] = '/'; size_t full_pathlen = pathlen + 1 + fnlen; memcpy(full_path + pathlen + 1, file_info.fname, fnlen); full_path[full_pathlen] = '\0'; if ((file_info.fattrib & AM_DIR) != 0) { res = supervisor_workflow_delete_directory_contents(fs, full_path); } if (res != FR_OK) { break; } res = f_unlink(fs, full_path); } f_closedir(&dir); return res; }