| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483 |
- #include "mass_storage_usb.h"
- #include <furi_hal.h>
- #define TAG "MassStorageUsb"
- #define USB_MSC_RX_EP (0x01)
- #define USB_MSC_TX_EP (0x82)
- #define USB_MSC_RX_EP_SIZE (64UL)
- #define USB_MSC_TX_EP_SIZE (64UL)
- #define USB_MSC_BOT_GET_MAX_LUN (0xFE)
- #define USB_MSC_BOT_RESET (0xFF)
- #define CBW_SIG (0x43425355)
- #define CBW_FLAGS_DEVICE_TO_HOST (0x80)
- #define CSW_SIG (0x53425355)
- #define CSW_STATUS_OK (0)
- #define CSW_STATUS_NOK (1)
- #define CSW_STATUS_PHASE_ERROR (2)
- // must be SCSI_BLOCK_SIZE aligned
- // larger than 0x10000 exceeds size_t, storage_file_* ops fail
- #define USB_MSC_BUF_MAX (0x10000UL - SCSI_BLOCK_SIZE)
- static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg);
- static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback);
- typedef enum {
- EventExit = 1 << 0,
- EventReset = 1 << 1,
- EventRxTx = 1 << 2,
- EventAll = EventExit | EventReset | EventRxTx,
- } MassStorageEvent;
- typedef struct {
- uint32_t sig;
- uint32_t tag;
- uint32_t len;
- uint8_t flags;
- uint8_t lun;
- uint8_t cmd_len;
- uint8_t cmd[16];
- } __attribute__((packed)) CBW;
- typedef struct {
- uint32_t sig;
- uint32_t tag;
- uint32_t residue;
- uint8_t status;
- } __attribute__((packed)) CSW;
- struct MassStorageUsb {
- FuriHalUsbInterface usb;
- FuriHalUsbInterface* usb_prev;
- FuriThread* thread;
- usbd_device* dev;
- SCSIDeviceFunc fn;
- };
- static int32_t mass_thread_worker(void* context) {
- MassStorageUsb* mass = context;
- usbd_device* dev = mass->dev;
- SCSISession scsi = {
- .fn = mass->fn,
- };
- CBW cbw = {0};
- CSW csw = {0};
- uint8_t* buf = NULL;
- uint32_t buf_len = 0, buf_cap = 0, buf_sent = 0;
- enum {
- StateReadCBW,
- StateReadData,
- StateWriteData,
- StateBuildCSW,
- StateWriteCSW,
- } state = StateReadCBW;
- while(true) {
- uint32_t flags = furi_thread_flags_wait(EventAll, FuriFlagWaitAny, FuriWaitForever);
- if(flags & EventExit) {
- FURI_LOG_D(TAG, "exit");
- break;
- }
- if(flags & EventReset) {
- FURI_LOG_D(TAG, "reset");
- scsi.sk = 0;
- scsi.asc = 0;
- memset(&cbw, 0, sizeof(cbw));
- memset(&csw, 0, sizeof(csw));
- if(buf) {
- free(buf);
- buf = NULL;
- }
- buf_len = buf_cap = buf_sent = 0;
- state = StateReadCBW;
- }
- if(flags & EventRxTx) do {
- switch(state) {
- case StateReadCBW: {
- FURI_LOG_T(TAG, "StateReadCBW");
- int32_t len = usbd_ep_read(dev, USB_MSC_RX_EP, &cbw, sizeof(cbw));
- if(len <= 0) {
- FURI_LOG_T(TAG, "cbw not ready");
- break;
- }
- if(len != sizeof(cbw) || cbw.sig != CBW_SIG) {
- FURI_LOG_W(TAG, "bad cbw sig=%08lx", cbw.sig);
- usbd_ep_stall(dev, USB_MSC_TX_EP);
- usbd_ep_stall(dev, USB_MSC_RX_EP);
- continue;
- }
- if(!scsi_cmd_start(&scsi, cbw.cmd, cbw.cmd_len)) {
- FURI_LOG_W(TAG, "bad cmd");
- usbd_ep_stall(dev, USB_MSC_RX_EP);
- csw.sig = CSW_SIG;
- csw.tag = cbw.tag;
- csw.status = CSW_STATUS_NOK;
- state = StateWriteCSW;
- continue;
- }
- if(cbw.flags & CBW_FLAGS_DEVICE_TO_HOST) {
- buf_len = 0;
- buf_sent = 0;
- state = StateWriteData;
- } else {
- buf_len = 0;
- state = StateReadData;
- }
- continue;
- }; break;
- case StateReadData: {
- FURI_LOG_T(TAG, "StateReadData %lu/%lu", buf_len, cbw.len);
- if(!cbw.len) {
- state = StateBuildCSW;
- continue;
- }
- uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
- if(buf_clamp > buf_cap) {
- FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
- if(buf) {
- free(buf);
- }
- buf_cap = buf_clamp;
- buf = malloc(buf_cap);
- }
- if(buf_len < buf_clamp) {
- int32_t len =
- usbd_ep_read(dev, USB_MSC_RX_EP, buf + buf_len, buf_clamp - buf_len);
- if(len < 0) {
- FURI_LOG_T(TAG, "rx not ready %ld", len);
- break;
- }
- FURI_LOG_T(TAG, "clamp %lu len %ld", buf_clamp, len);
- buf_len += len;
- }
- if(buf_len == buf_clamp) {
- if(!scsi_cmd_rx_data(&scsi, buf, buf_len)) {
- FURI_LOG_W(TAG, "short rx");
- usbd_ep_stall(dev, USB_MSC_RX_EP);
- csw.sig = CSW_SIG;
- csw.tag = cbw.tag;
- csw.status = CSW_STATUS_NOK;
- csw.residue = cbw.len;
- state = StateWriteCSW;
- continue;
- }
- cbw.len -= buf_len;
- buf_len = 0;
- }
- continue;
- }; break;
- case StateWriteData: {
- FURI_LOG_T(TAG, "StateWriteData %lu", cbw.len);
- if(!cbw.len) {
- state = StateBuildCSW;
- continue;
- }
- uint32_t buf_clamp = MIN(cbw.len, USB_MSC_BUF_MAX);
- if(buf_clamp > buf_cap) {
- FURI_LOG_T(TAG, "growing buf %lu -> %lu", buf_cap, buf_clamp);
- if(buf) {
- free(buf);
- }
- buf_cap = buf_clamp;
- buf = malloc(buf_cap);
- }
- if(!buf_len && !scsi_cmd_tx_data(&scsi, buf, &buf_len, buf_clamp)) {
- FURI_LOG_W(TAG, "short tx");
- // usbd_ep_stall(dev, USB_MSC_TX_EP);
- state = StateBuildCSW;
- continue;
- }
- int32_t len = usbd_ep_write(
- dev,
- USB_MSC_TX_EP,
- buf + buf_sent,
- MIN(USB_MSC_TX_EP_SIZE, buf_len - buf_sent));
- if(len < 0) {
- FURI_LOG_T(TAG, "tx not ready %ld", len);
- break;
- }
- buf_sent += len;
- if(buf_sent == buf_len) {
- cbw.len -= buf_len;
- buf_len = 0;
- buf_sent = 0;
- }
- continue;
- }; break;
- case StateBuildCSW: {
- FURI_LOG_T(TAG, "StateBuildCSW");
- csw.sig = CSW_SIG;
- csw.tag = cbw.tag;
- if(scsi_cmd_end(&scsi)) {
- csw.status = CSW_STATUS_OK;
- } else {
- csw.status = CSW_STATUS_NOK;
- }
- csw.residue = cbw.len;
- state = StateWriteCSW;
- continue;
- }; break;
- case StateWriteCSW: {
- FURI_LOG_T(TAG, "StateWriteCSW");
- if(csw.status) {
- FURI_LOG_W(
- TAG,
- "csw sig=%08lx tag=%08lx residue=%08lx status=%02x",
- csw.sig,
- csw.tag,
- csw.residue,
- csw.status);
- }
- int32_t len = usbd_ep_write(dev, USB_MSC_TX_EP, &csw, sizeof(csw));
- if(len < 0) {
- FURI_LOG_T(TAG, "csw not ready");
- break;
- }
- if(len != sizeof(csw)) {
- FURI_LOG_W(TAG, "bad csw write %ld", len);
- usbd_ep_stall(dev, USB_MSC_TX_EP);
- break;
- }
- memset(&cbw, 0, sizeof(cbw));
- memset(&csw, 0, sizeof(csw));
- state = StateReadCBW;
- continue;
- }; break;
- }
- break;
- } while(true);
- }
- if(buf) {
- free(buf);
- }
- return 0;
- }
- // needed in usb_deinit, usb_suspend, usb_rxtx_ep_callback, usb_control,
- // where if_ctx isn't passed
- static MassStorageUsb* mass_cur = NULL;
- static void usb_init(usbd_device* dev, FuriHalUsbInterface* intf, void* ctx) {
- UNUSED(intf);
- MassStorageUsb* mass = ctx;
- mass_cur = mass;
- mass->dev = dev;
- usbd_reg_config(dev, usb_ep_config);
- usbd_reg_control(dev, usb_control);
- usbd_connect(dev, true);
- mass->thread = furi_thread_alloc();
- furi_thread_set_name(mass->thread, "MassStorageUsb");
- furi_thread_set_stack_size(mass->thread, 1024);
- furi_thread_set_context(mass->thread, ctx);
- furi_thread_set_callback(mass->thread, mass_thread_worker);
- furi_thread_start(mass->thread);
- }
- static void usb_deinit(usbd_device* dev) {
- usbd_reg_config(dev, NULL);
- usbd_reg_control(dev, NULL);
- MassStorageUsb* mass = mass_cur;
- if(!mass || mass->dev != dev) {
- FURI_LOG_E(TAG, "deinit mass_cur leak");
- return;
- }
- mass_cur = NULL;
- furi_assert(mass->thread);
- furi_thread_flags_set(furi_thread_get_id(mass->thread), EventExit);
- furi_thread_join(mass->thread);
- furi_thread_free(mass->thread);
- mass->thread = NULL;
- free(mass->usb.str_prod_descr);
- mass->usb.str_prod_descr = NULL;
- free(mass->usb.str_serial_descr);
- mass->usb.str_serial_descr = NULL;
- free(mass);
- }
- static void usb_wakeup(usbd_device* dev) {
- UNUSED(dev);
- }
- static void usb_suspend(usbd_device* dev) {
- MassStorageUsb* mass = mass_cur;
- if(!mass || mass->dev != dev) return;
- furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset);
- }
- static void usb_rxtx_ep_callback(usbd_device* dev, uint8_t event, uint8_t ep) {
- UNUSED(ep);
- UNUSED(event);
- MassStorageUsb* mass = mass_cur;
- if(!mass || mass->dev != dev) return;
- furi_thread_flags_set(furi_thread_get_id(mass->thread), EventRxTx);
- }
- static usbd_respond usb_ep_config(usbd_device* dev, uint8_t cfg) {
- switch(cfg) {
- case 0: // deconfig
- usbd_ep_deconfig(dev, USB_MSC_RX_EP);
- usbd_ep_deconfig(dev, USB_MSC_TX_EP);
- usbd_reg_endpoint(dev, USB_MSC_RX_EP, NULL);
- usbd_reg_endpoint(dev, USB_MSC_TX_EP, NULL);
- return usbd_ack;
- case 1: // config
- usbd_ep_config(
- dev, USB_MSC_RX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_RX_EP_SIZE);
- usbd_ep_config(
- dev, USB_MSC_TX_EP, USB_EPTYPE_BULK /* | USB_EPTYPE_DBLBUF*/, USB_MSC_TX_EP_SIZE);
- usbd_reg_endpoint(dev, USB_MSC_RX_EP, usb_rxtx_ep_callback);
- usbd_reg_endpoint(dev, USB_MSC_TX_EP, usb_rxtx_ep_callback);
- return usbd_ack;
- }
- return usbd_fail;
- }
- static usbd_respond usb_control(usbd_device* dev, usbd_ctlreq* req, usbd_rqc_callback* callback) {
- UNUSED(callback);
- if(((USB_REQ_RECIPIENT | USB_REQ_TYPE) & req->bmRequestType) !=
- (USB_REQ_INTERFACE | USB_REQ_CLASS)) {
- return usbd_fail;
- }
- switch(req->bRequest) {
- case USB_MSC_BOT_GET_MAX_LUN: {
- static uint8_t max_lun = 0;
- dev->status.data_ptr = &max_lun;
- dev->status.data_count = 1;
- return usbd_ack;
- }; break;
- case USB_MSC_BOT_RESET: {
- MassStorageUsb* mass = mass_cur;
- if(!mass || mass->dev != dev) return usbd_fail;
- furi_thread_flags_set(furi_thread_get_id(mass->thread), EventReset);
- return usbd_ack;
- }; break;
- }
- return usbd_fail;
- }
- static const struct usb_string_descriptor dev_manuf_desc = USB_STRING_DESC("Flipper Devices Inc.");
- struct MassStorageDescriptor {
- struct usb_config_descriptor config;
- struct usb_interface_descriptor intf;
- struct usb_endpoint_descriptor ep_rx;
- struct usb_endpoint_descriptor ep_tx;
- } __attribute__((packed));
- static const struct usb_device_descriptor usb_mass_dev_descr = {
- .bLength = sizeof(struct usb_device_descriptor),
- .bDescriptorType = USB_DTYPE_DEVICE,
- .bcdUSB = VERSION_BCD(2, 0, 0),
- .bDeviceClass = USB_CLASS_PER_INTERFACE,
- .bDeviceSubClass = USB_SUBCLASS_NONE,
- .bDeviceProtocol = USB_PROTO_NONE,
- .bMaxPacketSize0 = 8, // USB_EP0_SIZE
- .idVendor = 0x0483,
- .idProduct = 0x5720,
- .bcdDevice = VERSION_BCD(1, 0, 0),
- .iManufacturer = 1, // UsbDevManuf
- .iProduct = 2, // UsbDevProduct
- .iSerialNumber = 3, // UsbDevSerial
- .bNumConfigurations = 1,
- };
- static const struct MassStorageDescriptor usb_mass_cfg_descr = {
- .config =
- {
- .bLength = sizeof(struct usb_config_descriptor),
- .bDescriptorType = USB_DTYPE_CONFIGURATION,
- .wTotalLength = sizeof(struct MassStorageDescriptor),
- .bNumInterfaces = 1,
- .bConfigurationValue = 1,
- .iConfiguration = NO_DESCRIPTOR,
- .bmAttributes = USB_CFG_ATTR_RESERVED | USB_CFG_ATTR_SELFPOWERED,
- .bMaxPower = USB_CFG_POWER_MA(100),
- },
- .intf =
- {
- .bLength = sizeof(struct usb_interface_descriptor),
- .bDescriptorType = USB_DTYPE_INTERFACE,
- .bInterfaceNumber = 0,
- .bAlternateSetting = 0,
- .bNumEndpoints = 2,
- .bInterfaceClass = USB_CLASS_MASS_STORAGE,
- .bInterfaceSubClass = 0x06, // scsi transparent
- .bInterfaceProtocol = 0x50, // bulk only
- .iInterface = NO_DESCRIPTOR,
- },
- .ep_rx =
- {
- .bLength = sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DTYPE_ENDPOINT,
- .bEndpointAddress = USB_MSC_RX_EP,
- .bmAttributes = USB_EPTYPE_BULK,
- .wMaxPacketSize = USB_MSC_RX_EP_SIZE,
- .bInterval = 0,
- },
- .ep_tx =
- {
- .bLength = sizeof(struct usb_endpoint_descriptor),
- .bDescriptorType = USB_DTYPE_ENDPOINT,
- .bEndpointAddress = USB_MSC_TX_EP,
- .bmAttributes = USB_EPTYPE_BULK,
- .wMaxPacketSize = USB_MSC_TX_EP_SIZE,
- .bInterval = 0,
- },
- };
- MassStorageUsb* mass_storage_usb_start(const char* filename, SCSIDeviceFunc fn) {
- MassStorageUsb* mass = malloc(sizeof(MassStorageUsb));
- mass->usb_prev = furi_hal_usb_get_config();
- mass->usb.init = usb_init;
- mass->usb.deinit = usb_deinit;
- mass->usb.wakeup = usb_wakeup;
- mass->usb.suspend = usb_suspend;
- mass->usb.dev_descr = (struct usb_device_descriptor*)&usb_mass_dev_descr;
- mass->usb.str_manuf_descr = (void*)&dev_manuf_desc;
- mass->usb.str_prod_descr = NULL;
- mass->usb.str_serial_descr = NULL;
- mass->usb.cfg_descr = (void*)&usb_mass_cfg_descr;
- const char* name = furi_hal_version_get_device_name_ptr();
- if(!name) name = "Flipper Zero";
- size_t len = strlen(name);
- struct usb_string_descriptor* str_prod_descr = malloc(len * 2 + 2);
- str_prod_descr->bLength = len * 2 + 2;
- str_prod_descr->bDescriptorType = USB_DTYPE_STRING;
- for(uint8_t i = 0; i < len; i++)
- str_prod_descr->wString[i] = name[i];
- mass->usb.str_prod_descr = str_prod_descr;
- len = strlen(filename);
- struct usb_string_descriptor* str_serial_descr = malloc(len * 2 + 2);
- str_serial_descr->bLength = len * 2 + 2;
- str_serial_descr->bDescriptorType = USB_DTYPE_STRING;
- for(uint8_t i = 0; i < len; i++)
- str_serial_descr->wString[i] = filename[i];
- mass->usb.str_serial_descr = str_serial_descr;
- mass->fn = fn;
- if(!furi_hal_usb_set_config(&mass->usb, mass)) {
- FURI_LOG_E(TAG, "USB locked, cannot start Mass Storage");
- free(mass->usb.str_prod_descr);
- free(mass->usb.str_serial_descr);
- free(mass);
- return NULL;
- }
- return mass;
- }
- void mass_storage_usb_stop(MassStorageUsb* mass) {
- furi_hal_usb_set_config(mass->usb_prev, NULL);
- }
|