2026-08-12 21:29:35 +02:00

91 lines
3.8 KiB
C

// SPDX-License-Identifier: Apache-2.0
#pragma once
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <tactility/error.h>
#ifdef __cplusplus
extern "C" {
#endif
struct Device;
struct DeviceType;
// ---- USB mass-storage device mode ----
/** Which backing storage is exposed to the USB host as a mass-storage volume. */
enum UsbMscDeviceSource {
USB_MSC_DEVICE_SOURCE_SDMMC,
USB_MSC_DEVICE_SOURCE_FLASH,
};
/** Fired when the exposed volume's mount state changes (host mounted/unmounted it). */
typedef void (*UsbMscDeviceMountChangedCallback)(bool mounted, void* context);
/**
* USB mass-storage device profile API (present the board's SD card or internal flash as a USB
* mass-storage device to a host).
*/
struct UsbMscDeviceApi {
/**
* Claim the USB device-mode slot and expose the given storage source as a USB mass-storage
* volume.
* @warning the caller must ensure the backing storage is unmounted/quiesced from local use
* before calling this - a source exposed to a USB host while still locally mounted
* risks filesystem corruption from two concurrent writers. Tactility's own flash-MSC
* path (see UsbTusb.cpp / UsbSettingsApp) enforces this via a dedicated reboot-into-
* MSC boot flow rather than unmounting live, so local access never overlaps with USB
* exposure; local availability is only restored by rebooting back to normal OS.
* @param[in] device the MSC device child device
* @param[in] source which backing storage to expose
* @param[in] source_handle the backing storage handle: a `sdmmc_card_t*` when source is
* USB_MSC_DEVICE_SOURCE_SDMMC, or a pointer to a `wl_handle_t` (i.e. `wl_handle_t*`)
* when source is USB_MSC_DEVICE_SOURCE_FLASH - passed by address, not cast through
* `void*` by value, since `wl_handle_t` is a plain integer type where 0 is a valid
* handle and would collide with the nullptr/"no handle" check otherwise. The caller
* resolves this - platform-esp32 has no business knowing which wear-levelling
* partition Tactility mounted as /data.
* @param[in] mount_changed_cb optional callback fired on host mount/unmount, nullable
* @param[in] context passed back to mount_changed_cb, nullable
* @retval ERROR_RESOURCE_BUSY if another USB device class already holds the slot
* @retval ERROR_INVALID_ARGUMENT if source_handle is invalid for the given source
* @retval ERROR_NONE on success
*/
error_t (*start)(struct Device* device, enum UsbMscDeviceSource source, void* source_handle,
UsbMscDeviceMountChangedCallback mount_changed_cb, void* context);
/**
* Stop presenting as a USB mass-storage device and release the USB device-mode slot.
* @param[in] device the MSC device child device
* @return ERROR_NONE on success
*/
error_t (*stop)(struct Device* device);
/**
* @param[in] device the MSC device child device
* @return true when a USB host currently has the volume mounted
*/
bool (*is_connected)(struct Device* device);
};
extern const struct DeviceType USB_MSC_DEVICE_TYPE;
/**
* Find the first started USB MSC device child device and take a reference on it.
* @return the device with an outstanding reference, or NULL if none is available - caller must
* call device_put() exactly once when done, same as device_get_first_active_by_type().
*/
struct Device* usb_msc_device_get(void);
error_t usb_msc_device_start(struct Device* device, enum UsbMscDeviceSource source, void* source_handle,
UsbMscDeviceMountChangedCallback mount_changed_cb, void* context);
error_t usb_msc_device_stop(struct Device* device);
bool usb_msc_device_is_connected(struct Device* device);
#ifdef __cplusplus
}
#endif