USB MSC host

chegewara
Posts: 2378
Joined: Wed Jun 14, 2017 9:00 pm

USB MSC host

Postby chegewara » Thu Sep 02, 2021 10:59 pm

I would like to thank espressif for pretty good USB host component. There probably is still some work to do, but so far i am having a lot of fun playing with it.

I am working on C++ library which should be compatible with esp-idf and arduino. I am just beginning, but i have some small success.
Here is example output from USB MSC device (another esp32 S2), but it can be easily any pendrive:

Code: Select all

I (320) : install status: 0
I (320) : client register status: 0
I (750) : client event: 0, address: 1
device speed: USB_SPEED_FULL, device address: 1, max ep_ctrl size: 64, config: 1

EP num: 1/2, len: 32, address: 0x04, EP max size: 64, dir: OUT
EP num: 2/2, len: 32, address: 0x84, EP max size: 64, dir: IN
I (760) : interface 0 claim status: 0
max luns: 0x00
MAX luns: 1
max_luns_cb
capacity_cb: block_size: 4096, block_count: 249
I (790) : VFS mount status: 0

I (850) TAG: Read from file: 
I (850) TAG: "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."

I (880) TAG: "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
I (970) TAG: "But I must explain to you how all this mistaken idea of denouncing pleasure and praising pain was born and I will give you a complete account of the system, and expound the actual teachings of the great explorer of the truth, the master-builder of human happiness. No one rejects, dislikes, or avoids pleasure itself, because it is pleasure, but because those who do not know how to pursue pleasure rationally encounter consequences that are extremely painful. Nor again is there anyone who loves or pursues or desires to obtain pain of itself, because it is pain, but because occasionally circumstances occur in which toil and pain can procure him some great pleasure. To take a trivial example, which of us ever undertakes laborious physical exercise, except to obtain some advantage from it? But who has any right to find fault with a man who chooses to enjoy a pleasure that has no annoying consequences, or one who avoids a pain that produces no resultant pleasure?"
I (1050) : Found file : README.TXT (1422 bytes)
I dont want to share code yet, mostly because its still messy and MSC can only read files from device.
Here is example demo code to discover device and use posix to open and read files:

Code: Select all

#include <stdio.h>
#include "string.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "esp_log.h"
#include "usb/usb_host.h"

#include "usb_host.hpp"
#include "usb_msc.hpp"

#include <fcntl.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/types.h>
#include <esp_vfs_fat.h>

#define TAG ""


USBhost host;
USBmscDevice *device;

void cbw_cb(usb_transfer_t *transfer)
{
    printf("USER CBW CB\n");
}

void data_cb(usb_transfer_t *transfer)
{
    printf("USER data CB\n");
}

static void csw_cb(usb_transfer_t *transfer)
{
    printf("USER CSW CB\n");
}

#define MOUNT_POINT "/msc"
char line[5000];
bool is_mount = false;
static void capacity_cb(usb_transfer_t *transfer)
{
    printf("capacity_cb: block_size: %d, block_count: %d\n", device->block_size, device->block_count);
    device->mount();
    is_mount = true;
}

static void inquiry_cb(usb_transfer_t *transfer)
{
    printf("inquiry_cb\n");
}

static void unit_ready_cb(usb_transfer_t *transfer)
{
    printf("unit_ready_cb\n");
}

static void max_luns_cb(usb_transfer_t *transfer)
{
    printf("max_luns_cb\n");
    device->getCapacity();
}

static void sense_cb(usb_transfer_t *transfer)
{
    printf("sense_cb\n");
}

void client_event_callback(const usb_host_client_event_msg_t *event_msg, void *arg)
{
    if (event_msg->event == USB_HOST_CLIENT_EVENT_NEW_DEV)
    {
        host.open(event_msg);
        usb_device_info_t info = host.getDeviceInfo();
        printf("device speed: %s, device address: %d, max ep_ctrl size: %d, config: %d\n\n", info.speed ? "USB_SPEED_FULL" : "USB_SPEED_LOW", info.dev_addr, info.bMaxPacketSize0, info.bConfigurationValue);
        const usb_config_desc_t *config_desc = host.getConfigurationDescriptor();
        int offset = 0;

        for (size_t n = 0; n < config_desc->bNumInterfaces; n++)
        {
            const usb_intf_desc_t *intf = usb_host_parse_interface(config_desc, n, 0, &offset);

            if (intf->bInterfaceClass == 0x08) // MSC
            {
                msc_transfer_cb_t cb = {
                    // .cbw_cb = cbw_cb,
                    // .data_cb = data_cb,
                    // .csw_cb = csw_cb,
                    .capacity_cb = capacity_cb,
                    .inquiry_cb = inquiry_cb,
                    .unit_ready_cb = unit_ready_cb,
                    .max_luns_cb = max_luns_cb,
                    .sense_cb = sense_cb,
                };
                device = new USBmscDevice(config_desc, &host);
                ((USBmscDevice *)device)->registerCallbacks(cb);
                n = config_desc->bNumInterfaces;
            }
            
            if (device)  device->init();
        }
    }
}

void test()
{
    FILE *f = fopen(MOUNT_POINT "/README.txt", "r+");
    if (f == NULL)
    {
        ESP_LOGE("TAG", "Failed to open file");
    }
    else
    {
        fgets(line, sizeof(line), f);
        fclose(f);
        // strip newline
        char *pos = strchr(line, '\n');
        if (pos)
        {
            *pos = '\0';
        }
        ESP_LOGI("TAG", "Read from file: ");
        ESP_LOGI("TAG", "%s", line);
    }

    char _dirpath[] = "/msc/";
    char dirpath[] = "/msc/";
    DIR *dir = opendir(_dirpath);

    // /* Retrieve the base path of file storage to construct the full path */
    // strlcpy(entrypath, dirpath, sizeof(entrypath));
    char entrypath[256];
    char entrysize[32];
    const char *entrytype;

    struct dirent *entry;
    struct stat entry_stat;
    const size_t dirpath_len = strlen(dirpath);
    strlcpy(entrypath, dirpath, sizeof(entrypath));

    if (!dir)
    {
        ESP_LOGE("TAG", "Failed to stat dir : %s", _dirpath);
    }
    else
    {
        entry = readdir(dir);
        while (entry != NULL)
        {
            entrytype = (entry->d_type == DT_DIR ? "directory" : "file");

            strlcpy(entrypath + dirpath_len, entry->d_name, sizeof(entrypath) - dirpath_len);

            int st = 0;
            st = stat(entrypath, &entry_stat);
            if (st == -1)
            {
                ESP_LOGE(TAG, "Failed to stat %s : %s", entrytype, entry->d_name);
                continue;
            }
            sprintf(entrysize, "%ld", entry_stat.st_size);
            ESP_LOGI(TAG, "Found %s : %s (%s bytes)", entrytype, entry->d_name, entrysize);
            entry = readdir(dir);
        }
        closedir(dir);
    }
}


extern "C" void app_main(void)
{
    host.registerClientCb(client_event_callback); // optional
    host.init();

    while (!is_mount)
    {
        vTaskDelay(1);
    }
   
    test();
}
I would like to hear from you what do you think about idea and this example code complexity.

chegewara
Posts: 2378
Joined: Wed Jun 14, 2017 9:00 pm

Re: USB MSC host

Postby chegewara » Sat Sep 04, 2021 12:23 am

Day 3:
- after long battle read10 and write10 implemented (bare minimum code).

Here is otput from pendrive connected:
- as we can see second mount failed (its expected), its because there is only one partition exists on pendrive and code is trying to mount second partition
- for the same reason "stat dir : /msc1" failed

Code: Select all

I (810) : VFS mount status: 0
I (810) : VFS mount status: 259
E (810) TAG: Failed to open file
E (820) TAG: Failed to stat dir : /msc1
I (890) READ file: /msc/README2.txt
I (910) : this is readme2.txt file.
I (910) : Found file : /msc/FULLCH~1.CER (5589 bytes)
I (910) : Found file : /msc/TEST.CPP (12682 bytes)
I (910) : Found file : /msc/USB~1.HTM (3312 bytes)
I (920) : Found file : /msc/BLE_MESH.TXT (31435 bytes)
I (920) : Found file : /msc/README2.TXT (26 bytes)
I (930) : Found file : /msc/README1.TXT (13 bytes)
I (1080) : File written
I (1200) : Renaming file
I (1240) : Reading file
I (1260) : Read from file /msc/README1.txt: 'Hello World!'
I (1260) READ file: /msc/README2.txt
I (1280) : this is readme2.txt file.
I (1280) : Found file : /msc/FULLCH~1.CER (5589 bytes)
I (1280) : Found file : /msc/TEST.CPP (12682 bytes)
I (1280) : Found file : /msc/USB~1.HTM (3312 bytes)
I (1290) : Found file : /msc/BLE_MESH.TXT (31435 bytes)
I (1290) : Found file : /msc/README2.TXT (26 bytes)
I (1300) : Found file : /msc/README1.TXT (13 bytes)
There is still few functions to implement and a lot cleanup and refactoring before code can be public.

chegewara
Posts: 2378
Joined: Wed Jun 14, 2017 9:00 pm

Re: USB MSC host

Postby chegewara » Sun Sep 05, 2021 6:25 pm

After very briefly cleanup i can share this "library". It is still very early stage, but i have 1 cool example i wish to show.
I know it is already few such devices on market and all are faster than this:
https://www.amazon.com/SanDisk-SDIX30C- ... B01CIEBXZG

but we can do it with esp32 S2 and use any wireless protocol (wifi in this example). That way we can connect any pendrive or maybe even SSD in the future.
https://github.com/chegewara/esp32-usb-host

Maybe speed is not impressive, but we can for example add remote logging to our esp32 projects.

chegewara
Posts: 2378
Joined: Wed Jun 14, 2017 9:00 pm

Re: USB MSC host

Postby chegewara » Sun Sep 26, 2021 9:41 pm

I would like to bring good news (i hope they are) to forum.
Today i updated my USB library for arduino community by adding USB host with examples.
There is only 3 USB host examples for now:
- CDC-ACM,
- simple MSC,
- MSC example with website access, mkdir and delete working, just missing upload and maybe firmware update.

I hope this allows our community to find easy way to get started with USB host.
In readme I forgot to mention that USB host is working with arduino-esp32 on branch idf-master only.

If there will be interest then maybe i will add more classes and examples, or maybe community will add some examples too?

https://github.com/chegewara/EspTinyUSB

Who is online

Users browsing this forum: No registered users and 11 guests