summaryrefslogtreecommitdiff
path: root/supervisor
diff options
context:
space:
mode:
Diffstat (limited to 'supervisor')
-rw-r--r--supervisor/shared/display.c51
-rw-r--r--supervisor/shared/rgb_led_status.c125
-rw-r--r--supervisor/shared/usb/tusb_config.h4
-rw-r--r--supervisor/shared/usb/usb_desc.c42
-rw-r--r--supervisor/shared/usb/usb_desc.h43
-rw-r--r--supervisor/shared/usb/usb_msc_flash.c96
6 files changed, 242 insertions, 119 deletions
diff --git a/supervisor/shared/display.c b/supervisor/shared/display.c
index c3afb3e00..6a39cbe8e 100644
--- a/supervisor/shared/display.c
+++ b/supervisor/shared/display.c
@@ -71,6 +71,8 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
if (tiles == NULL) {
return;
}
+ grid->y = 0;
+ grid->top_left_y = 0;
if (remaining_pixels > 0) {
grid->y -= (grid->tile_height - remaining_pixels);
}
@@ -80,8 +82,7 @@ void supervisor_start_terminal(uint16_t width_px, uint16_t height_px) {
grid->pixel_height = height_in_tiles * grid->tile_height;
grid->tiles = tiles;
- supervisor_terminal.cursor_x = 0;
- supervisor_terminal.cursor_y = 0;
+ common_hal_terminalio_terminal_construct(&supervisor_terminal, grid, &supervisor_terminal_font);
}
void supervisor_stop_terminal(void) {
@@ -145,17 +146,49 @@ displayio_bitmap_t blinka_bitmap = {
.read_only = true
};
-uint32_t blinka_transparency[1] = {0x80000000};
-
-// These colors are RGB 565 with the bytes swapped.
-uint32_t blinka_colors[8] = {0x78890000, 0x9F86B8FC, 0xffff0D5A, 0x0000f501,
- 0x00000000, 0x00000000, 0x00000000, 0x00000000};
+_displayio_color_t blinka_colors[7] = {
+ {
+ .rgb888 = 0x000000,
+ .rgb565 = 0x0000,
+ .luma = 0x00,
+ .transparent = true
+ },
+ {
+ .rgb888 = 0x8428bc,
+ .rgb565 = 0x7889,
+ .luma = 0xff // We cheat the luma here. It is actually 0x60
+ },
+ {
+ .rgb888 = 0xff89bc,
+ .rgb565 = 0xB8FC,
+ .luma = 0xb5
+ },
+ {
+ .rgb888 = 0x7beffe,
+ .rgb565 = 0x9F86,
+ .luma = 0xe0
+ },
+ {
+ .rgb888 = 0x51395f,
+ .rgb565 = 0x0D5A,
+ .luma = 0x47
+ },
+ {
+ .rgb888 = 0xffffff,
+ .rgb565 = 0xffff,
+ .luma = 0xff
+ },
+ {
+ .rgb888 = 0x0736a0,
+ .rgb565 = 0xf501,
+ .luma = 0x44
+ },
+};
displayio_palette_t blinka_palette = {
.base = {.type = &displayio_palette_type },
- .opaque = blinka_transparency,
.colors = blinka_colors,
- .color_count = 16,
+ .color_count = 7,
.needs_refresh = false
};
diff --git a/supervisor/shared/rgb_led_status.c b/supervisor/shared/rgb_led_status.c
index 209eef55e..0e69cd2e0 100644
--- a/supervisor/shared/rgb_led_status.c
+++ b/supervisor/shared/rgb_led_status.c
@@ -50,10 +50,28 @@ busio_spi_obj_t status_apa102;
#endif
#endif
-#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+#if defined(CP_RGB_STATUS_R) || defined(CP_RGB_STATUS_G) || defined(CP_RGB_STATUS_B)
+#define CP_RGB_STATUS_LED
+
+#include "shared-bindings/pulseio/PWMOut.h"
+#include "shared-bindings/microcontroller/Pin.h"
+
+pulseio_pwmout_obj_t rgb_status_r;
+pulseio_pwmout_obj_t rgb_status_g;
+pulseio_pwmout_obj_t rgb_status_b;
+
+uint8_t rgb_status_brightness = 0xFF;
+
+uint16_t status_rgb_color[3] = {
+ 0 /* red */, 0 /* green */, 0 /* blue */
+};
+#endif
+
+#if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
static uint32_t current_status_color = 0;
#endif
+
void rgb_led_status_init() {
#ifdef MICROPY_HW_NEOPIXEL
common_hal_digitalio_digitalinout_construct(&status_neopixel, MICROPY_HW_NEOPIXEL);
@@ -93,7 +111,34 @@ void rgb_led_status_init() {
#endif
#endif
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+
+ #if defined(CP_RGB_STATUS_LED)
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_R)) {
+ pwmout_result_t red_result = common_hal_pulseio_pwmout_construct(&rgb_status_r, CP_RGB_STATUS_R, 0, 50000, false);
+
+ if (PWMOUT_OK == red_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_r);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_G)) {
+ pwmout_result_t green_result = common_hal_pulseio_pwmout_construct(&rgb_status_g, CP_RGB_STATUS_G, 0, 50000, false);
+
+ if (PWMOUT_OK == green_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_g);
+ }
+ }
+
+ if (common_hal_mcu_pin_is_free(CP_RGB_STATUS_B)) {
+ pwmout_result_t blue_result = common_hal_pulseio_pwmout_construct(&rgb_status_b, CP_RGB_STATUS_B, 0, 50000, false);
+
+ if (PWMOUT_OK == blue_result) {
+ common_hal_pulseio_pwmout_never_reset(&rgb_status_b);
+ }
+ }
+ #endif
+
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
// Force a write of the current status color.
uint32_t rgb = current_status_color;
current_status_color = 0x1000000; // Not a valid color
@@ -109,10 +154,13 @@ void reset_status_led() {
reset_pin_number(MICROPY_HW_APA102_MOSI->number);
reset_pin_number(MICROPY_HW_APA102_SCK->number);
#endif
+ #if defined(CP_RGB_STATUS_LED)
+ // TODO: Support sharing status LED with user.
+ #endif
}
void new_status_color(uint32_t rgb) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
if (current_status_color == rgb) {
return;
}
@@ -143,10 +191,30 @@ void new_status_color(uint32_t rgb) {
common_hal_busio_spi_write(&status_apa102, status_apa102_color, 8);
#endif
#endif
+
+ #if defined(CP_RGB_STATUS_LED)
+ uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
+ uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
+ uint8_t blue_u8 = rgb_adjusted & 0xFF;
+
+ #if defined(CP_RGB_STATUS_INVERTED_PWM)
+ status_rgb_color[0] = (1 << 16) - 1 - ((uint16_t) (red_u8 << 8) + red_u8);
+ status_rgb_color[1] = (1 << 16) - 1 - ((uint16_t) (green_u8 << 8) + green_u8);
+ status_rgb_color[2] = (1 << 16) - 1 - ((uint16_t) (blue_u8 << 8) + blue_u8);
+ #else
+ status_rgb_color[0] = (uint16_t) (red_u8 << 8) + red_u8;
+ status_rgb_color[1] = (uint16_t) (green_u8 << 8) + green_u8;
+ status_rgb_color[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
+ #endif
+
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, status_rgb_color[0]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, status_rgb_color[1]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, status_rgb_color[2]);
+ #endif
}
void temp_status_color(uint32_t rgb) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t rgb_adjusted = rgb;
rgb_adjusted = color_brightness(rgb, rgb_status_brightness);
#endif
@@ -168,6 +236,27 @@ void temp_status_color(uint32_t rgb) {
common_hal_busio_spi_write(&status_apa102, colors, 12);
#endif
#endif
+ #if defined(CP_RGB_STATUS_LED)
+ uint8_t red_u8 = (rgb_adjusted >> 16) & 0xFF;
+ uint8_t green_u8 = (rgb_adjusted >> 8) & 0xFF;
+ uint8_t blue_u8 = rgb_adjusted & 0xFF;
+
+ uint16_t temp_status_color_rgb[3] = {0};
+
+ #if defined(CP_RGB_STATUS_INVERTED_PWM)
+ temp_status_color_rgb[0] = (1 << 16) - 1 - ((uint16_t) (red_u8 << 8) + red_u8);
+ temp_status_color_rgb[1] = (1 << 16) - 1 - ((uint16_t) (green_u8 << 8) + green_u8);
+ temp_status_color_rgb[2] = (1 << 16) - 1 - ((uint16_t) (blue_u8 << 8) + blue_u8);
+ #else
+ temp_status_color_rgb[0] = (uint16_t) (red_u8 << 8) + red_u8;
+ temp_status_color_rgb[1] = (uint16_t) (green_u8 << 8) + green_u8;
+ temp_status_color_rgb[2] = (uint16_t) (blue_u8 << 8) + blue_u8;
+ #endif
+
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, temp_status_color_rgb[0]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, temp_status_color_rgb[1]);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, temp_status_color_rgb[2]);
+ #endif
}
void clear_temp_status() {
@@ -181,10 +270,30 @@ void clear_temp_status() {
common_hal_busio_spi_write(&status_apa102, status_apa102_color, 8);
#endif
#endif
+ #if defined(CP_RGB_STATUS_LED)
+
+ uint16_t red = 0;
+ uint16_t green = 0;
+ uint16_t blue = 0;
+
+ #if defined(CP_RGB_STATUS_INVERTED_PWM)
+ red = (1 << 16) - 1 - status_rgb_color[0];
+ green = (1 << 16) - 1 - status_rgb_color[1];
+ blue = (1 << 16) - 1 - status_rgb_color[2];
+ #else
+ red = status_rgb_color[0];
+ green = status_rgb_color[1];
+ blue = status_rgb_color[2];
+ #endif
+
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_r, red);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_g, green);
+ common_hal_pulseio_pwmout_set_duty_cycle(&rgb_status_b, blue);
+ #endif
}
uint32_t color_brightness(uint32_t color, uint8_t brightness) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t result = ((color & 0xff0000) * brightness / 255) & 0xff0000;
result += ((color & 0xff00) * brightness / 255) & 0xff00;
result += ((color & 0xff) * brightness / 255) & 0xff;
@@ -195,7 +304,7 @@ uint32_t color_brightness(uint32_t color, uint8_t brightness) {
}
void set_rgb_status_brightness(uint8_t level){
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
rgb_status_brightness = level;
uint32_t current_color = current_status_color;
// Temporarily change the current color global to force the new_status_color call to update the
@@ -210,7 +319,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
bool found_main,
safe_mode_t safe_mode,
rgb_status_animation_t* status) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
new_status_color(ALL_DONE);
status->pattern_start = ticks_ms;
status->safe_mode = safe_mode;
@@ -256,7 +365,7 @@ void prep_rgb_status_animation(const pyexec_result_t* result,
}
void tick_rgb_status_animation(rgb_status_animation_t* status) {
- #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK))
+ #if defined(MICROPY_HW_NEOPIXEL) || (defined(MICROPY_HW_APA102_MOSI) && defined(MICROPY_HW_APA102_SCK)) || (defined(CP_RGB_STATUS_LED))
uint32_t tick_diff = ticks_ms - status->pattern_start;
if (status->ok) {
// All is good. Ramp ALL_DONE up and down.
diff --git a/supervisor/shared/usb/tusb_config.h b/supervisor/shared/usb/tusb_config.h
index d7c5199be..2e85bc820 100644
--- a/supervisor/shared/usb/tusb_config.h
+++ b/supervisor/shared/usb/tusb_config.h
@@ -107,7 +107,9 @@
// USB RAM PLACEMENT
//--------------------------------------------------------------------+
#define CFG_TUSB_ATTR_USBRAM
-#define CFG_TUSB_MEM_ALIGN TU_ATTR_ALIGNED(4)
+
+#define CFG_TUSB_MEM_ALIGN __attribute__ ((aligned(4)))
+
#ifdef __cplusplus
diff --git a/supervisor/shared/usb/usb_desc.c b/supervisor/shared/usb/usb_desc.c
index 5a0de1ffc..9b333d6fe 100644
--- a/supervisor/shared/usb/usb_desc.c
+++ b/supervisor/shared/usb/usb_desc.c
@@ -24,23 +24,35 @@
* THE SOFTWARE.
*/
-#include "supervisor/shared/usb/usb_desc.h"
+#include "lib/tinyusb/src/tusb.h"
#include "shared-module/usb_hid/Device.h"
#include "genhdr/autogen_usb_descriptor.h"
-// tud_desc_set is required by tinyusb stack
-tud_desc_set_t tud_desc_set =
-{
- .device = &usb_desc_dev,
- .config = &usb_desc_cfg,
- .string_arr = (uint8_t const **) string_desc_arr,
- .string_count = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]),
+// Invoked when received GET DEVICE DESCRIPTOR
+// Application return pointer to descriptor
+uint8_t const * tud_descriptor_device_cb(void) {
+ return usb_desc_dev;
+}
- .hid_report =
- {
- .generic = hid_report_descriptor,
- .boot_keyboard = NULL,
- .boot_mouse = NULL
- }
-};
+// Invoked when received GET CONFIGURATION DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_descriptor_configuration_cb(uint8_t index) {
+ (void) index; // for multiple configurations
+ return usb_desc_cfg;
+}
+
+// Invoked when received GET HID REPORT DESCRIPTOR
+// Application return pointer to descriptor
+// Descriptor contents must exist long enough for transfer to complete
+uint8_t const * tud_hid_descriptor_report_cb(void) {
+ return hid_report_descriptor;
+}
+
+// Invoked when received GET STRING DESCRIPTOR request
+// Application return pointer to descriptor, whose contents must exist long enough for transfer to complete
+uint16_t const* tud_descriptor_string_cb(uint8_t index) {
+ uint8_t const max_index = sizeof(string_desc_arr)/sizeof(string_desc_arr[0]);
+ return (index < max_index) ? string_desc_arr[index] : NULL;
+}
diff --git a/supervisor/shared/usb/usb_desc.h b/supervisor/shared/usb/usb_desc.h
deleted file mode 100644
index 250147aa6..000000000
--- a/supervisor/shared/usb/usb_desc.h
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * This file is part of the MicroPython project, http://micropython.org/
- *
- * The MIT License (MIT)
- *
- * Copyright (c) 2018 hathach 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.
- */
-
-#ifndef USB_DESC_H_
-#define USB_DESC_H_
-
-#include "lib/tinyusb/src/tusb.h"
-
-#ifdef __cplusplus
- extern "C" {
-#endif
-
-// Descriptors set used by tinyusb stack
-extern tud_desc_set_t tud_desc_set;
-
-#ifdef __cplusplus
- }
-#endif
-
-#endif /* USB_DESC_H_ */
diff --git a/supervisor/shared/usb/usb_msc_flash.c b/supervisor/shared/usb/usb_msc_flash.c
index f6f0fa9c3..205cb7b50 100644
--- a/supervisor/shared/usb/usb_msc_flash.c
+++ b/supervisor/shared/usb/usb_msc_flash.c
@@ -59,59 +59,18 @@ static fs_user_mount_t* get_vfs(int lun) {
}
// Callback invoked when received an SCSI command not in built-in list below
-// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
+// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, TEST_UNIT_READY, START_STOP_UNIT, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 have their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer, uint16_t bufsize) {
const void* response = NULL;
int32_t resplen = 0;
switch ( scsi_cmd[0] ) {
- case SCSI_CMD_TEST_UNIT_READY:
- // Command that host uses to check our readiness before sending other commands
- resplen = 0;
- if (lun > 1) {
- resplen = -1;
- } else {
- fs_user_mount_t* current_mount = get_vfs(lun);
- if (current_mount == NULL) {
- resplen = -1;
- }
- if (ejected[lun]) {
- resplen = -1;
- }
- }
- break;
-
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;
break;
- case SCSI_CMD_START_STOP_UNIT:
- {
- // Host try to eject/safe remove/poweroff us. We could safely disconnect with disk storage, or go into lower power
- const scsi_start_stop_unit_t* start_stop = (const scsi_start_stop_unit_t*) scsi_cmd;
- // Start bit = 0 : low power mode, if load_eject = 1 : unmount disk storage as well
- // Start bit = 1 : Ready mode, if load_eject = 1 : mount disk storage
- resplen = 0;
- if (start_stop->load_eject == 1) {
- if (lun > 1) {
- resplen = -1;
- } else {
- fs_user_mount_t* current_mount = get_vfs(lun);
- if (current_mount == NULL) {
- resplen = -1;
- }
- if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
- resplen = -1;
- } else {
- ejected[lun] = true;
- }
- }
- }
- }
- break;
-
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
@@ -127,7 +86,7 @@ int32_t tud_msc_scsi_cb (uint8_t lun, const uint8_t scsi_cmd[16], void* buffer,
}
// copy response to stack's buffer if any
- if ( response && resplen ) {
+ if ( response && (resplen > 0) ) {
memcpy(buffer, response, resplen);
}
@@ -206,3 +165,54 @@ void tud_msc_write10_complete_cb (uint8_t lun) {
// This write is complete, start the autoreload clock.
autoreload_start();
}
+
+// Invoked when received SCSI_CMD_INQUIRY
+// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
+void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4]) {
+ (void) lun;
+
+ memcpy(vendor_id , CFG_TUD_MSC_VENDOR , strlen(CFG_TUD_MSC_VENDOR));
+ memcpy(product_id , CFG_TUD_MSC_PRODUCT , strlen(CFG_TUD_MSC_PRODUCT));
+ memcpy(product_rev, CFG_TUD_MSC_PRODUCT_REV, strlen(CFG_TUD_MSC_PRODUCT_REV));
+}
+
+// Invoked when received Test Unit Ready command.
+// return true allowing host to read/write this LUN e.g SD card inserted
+bool tud_msc_test_unit_ready_cb(uint8_t lun) {
+ if (lun > 1) {
+ return false;
+ }
+
+ fs_user_mount_t* current_mount = get_vfs(lun);
+ if (current_mount == NULL) {
+ return false;
+ }
+ if (ejected[lun]) {
+ return false;
+ }
+
+ return true;
+}
+
+// Invoked when received Start Stop Unit command
+// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
+// - Start = 1 : active mode, if load_eject = 1 : load disk storage
+bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject) {
+ if (load_eject) {
+ if (lun > 1) {
+ return false;
+ } else {
+ fs_user_mount_t* current_mount = get_vfs(lun);
+ if (current_mount == NULL) {
+ return false;
+ }
+ if (disk_ioctl(current_mount, CTRL_SYNC, NULL) != RES_OK) {
+ return false;
+ } else {
+ ejected[lun] = true;
+ }
+ }
+ }
+
+ return true;
+}