USB MSC host
Posted: 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:
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:
I would like to hear from you what do you think about idea and this example code complexity.
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)
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();
}