msc_example_main.c 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. /*
  2. * SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
  3. *
  4. * SPDX-License-Identifier: Unlicense OR CC0-1.0
  5. */
  6. #include <stdlib.h>
  7. #include <string.h>
  8. #include <assert.h>
  9. #include <sys/stat.h>
  10. #include <dirent.h>
  11. #include <inttypes.h>
  12. #include "freertos/FreeRTOS.h"
  13. #include "freertos/task.h"
  14. #include "freertos/queue.h"
  15. #include "esp_timer.h"
  16. #include "esp_err.h"
  17. #include "esp_log.h"
  18. #include "usb/usb_host.h"
  19. #include "usb/msc_host.h"
  20. #include "usb/msc_host_vfs.h"
  21. #include "ffconf.h"
  22. #include "errno.h"
  23. #include "driver/gpio.h"
  24. static const char *TAG = "example";
  25. #define MNT_PATH "/usb" // Path in the Virtual File System, where the USB flash drive is going to be mounted
  26. #define APP_QUIT_PIN GPIO_NUM_0 // BOOT button on most boards
  27. #define BUFFER_SIZE 4096 // The read/write performance can be improved with larger buffer for the cost of RAM, 4kB is enough for most usecases
  28. /**
  29. * @brief Application Queue and its messages ID
  30. */
  31. static QueueHandle_t app_queue;
  32. typedef struct {
  33. enum {
  34. APP_QUIT, // Signals request to exit the application
  35. APP_DEVICE_CONNECTED, // USB device connect event
  36. APP_DEVICE_DISCONNECTED, // USB device disconnect event
  37. } id;
  38. union {
  39. uint8_t new_dev_address; // Address of new USB device for APP_DEVICE_CONNECTED event if
  40. } data;
  41. } app_message_t;
  42. /**
  43. * @brief BOOT button pressed callback
  44. *
  45. * Signal application to exit the main task
  46. *
  47. * @param[in] arg Unused
  48. */
  49. static void gpio_cb(void *arg)
  50. {
  51. BaseType_t xTaskWoken = pdFALSE;
  52. app_message_t message = {
  53. .id = APP_QUIT,
  54. };
  55. if (app_queue) {
  56. xQueueSendFromISR(app_queue, &message, &xTaskWoken);
  57. }
  58. if (xTaskWoken == pdTRUE) {
  59. portYIELD_FROM_ISR();
  60. }
  61. }
  62. /**
  63. * @brief MSC driver callback
  64. *
  65. * Signal device connection/disconnection to the main task
  66. *
  67. * @param[in] event MSC event
  68. * @param[in] arg MSC event data
  69. */
  70. static void msc_event_cb(const msc_host_event_t *event, void *arg)
  71. {
  72. if (event->event == MSC_DEVICE_CONNECTED) {
  73. ESP_LOGI(TAG, "MSC device connected");
  74. app_message_t message = {
  75. .id = APP_DEVICE_CONNECTED,
  76. .data.new_dev_address = event->device.address,
  77. };
  78. xQueueSend(app_queue, &message, portMAX_DELAY);
  79. } else if (event->event == MSC_DEVICE_DISCONNECTED) {
  80. ESP_LOGI(TAG, "MSC device disconnected");
  81. app_message_t message = {
  82. .id = APP_DEVICE_DISCONNECTED,
  83. };
  84. xQueueSend(app_queue, &message, portMAX_DELAY);
  85. }
  86. }
  87. static void print_device_info(msc_host_device_info_t *info)
  88. {
  89. const size_t megabyte = 1024 * 1024;
  90. uint64_t capacity = ((uint64_t)info->sector_size * info->sector_count) / megabyte;
  91. printf("Device info:\n");
  92. printf("\t Capacity: %llu MB\n", capacity);
  93. printf("\t Sector size: %"PRIu32"\n", info->sector_size);
  94. printf("\t Sector count: %"PRIu32"\n", info->sector_count);
  95. printf("\t PID: 0x%04X \n", info->idProduct);
  96. printf("\t VID: 0x%04X \n", info->idVendor);
  97. wprintf(L"\t iProduct: %S \n", info->iProduct);
  98. wprintf(L"\t iManufacturer: %S \n", info->iManufacturer);
  99. wprintf(L"\t iSerialNumber: %S \n", info->iSerialNumber);
  100. }
  101. static void file_operations(void)
  102. {
  103. const char *directory = "/usb/esp";
  104. const char *file_path = "/usb/esp/test.txt";
  105. // Create /usb/esp directory
  106. struct stat s = {0};
  107. bool directory_exists = stat(directory, &s) == 0;
  108. if (!directory_exists) {
  109. if (mkdir(directory, 0775) != 0) {
  110. ESP_LOGE(TAG, "mkdir failed with errno: %s", strerror(errno));
  111. }
  112. }
  113. // Create /usb/esp/test.txt file, if it doesn't exist
  114. if (stat(file_path, &s) != 0) {
  115. ESP_LOGI(TAG, "Creating file");
  116. FILE *f = fopen(file_path, "w");
  117. if (f == NULL) {
  118. ESP_LOGE(TAG, "Failed to open file for writing");
  119. return;
  120. }
  121. fprintf(f, "Hello World!\n");
  122. fclose(f);
  123. }
  124. // Read back the file
  125. FILE *f;
  126. ESP_LOGI(TAG, "Reading file");
  127. f = fopen(file_path, "r");
  128. if (f == NULL) {
  129. ESP_LOGE(TAG, "Failed to open file for reading");
  130. return;
  131. }
  132. char line[64];
  133. fgets(line, sizeof(line), f);
  134. fclose(f);
  135. // strip newline
  136. char *pos = strchr(line, '\n');
  137. if (pos) {
  138. *pos = '\0';
  139. }
  140. ESP_LOGI(TAG, "Read from file '%s': '%s'", file_path, line);
  141. }
  142. void speed_test(void)
  143. {
  144. #define TEST_FILE "/usb/esp/dummy"
  145. #define ITERATIONS 256 // 256 * 4kb = 1MB
  146. int64_t test_start, test_end;
  147. FILE *f = fopen(TEST_FILE, "wb+");
  148. if (f == NULL) {
  149. ESP_LOGE(TAG, "Failed to open file for writing");
  150. return;
  151. }
  152. // Set larger buffer for this file. It results in larger and more effective USB transfers
  153. setvbuf(f, NULL, _IOFBF, BUFFER_SIZE);
  154. // Allocate application buffer used for read/write
  155. uint8_t *data = malloc(BUFFER_SIZE);
  156. assert(data);
  157. ESP_LOGI(TAG, "Writing to file %s", TEST_FILE);
  158. test_start = esp_timer_get_time();
  159. for (int i = 0; i < ITERATIONS; i++) {
  160. if (fwrite(data, BUFFER_SIZE, 1, f) == 0) {
  161. return;
  162. }
  163. }
  164. test_end = esp_timer_get_time();
  165. ESP_LOGI(TAG, "Write speed %1.2f MiB/s", (BUFFER_SIZE * ITERATIONS) / (float)(test_end - test_start));
  166. rewind(f);
  167. ESP_LOGI(TAG, "Reading from file %s", TEST_FILE);
  168. test_start = esp_timer_get_time();
  169. for (int i = 0; i < ITERATIONS; i++) {
  170. if (0 == fread(data, BUFFER_SIZE, 1, f)) {
  171. return;
  172. }
  173. }
  174. test_end = esp_timer_get_time();
  175. ESP_LOGI(TAG, "Read speed %1.2f MiB/s", (BUFFER_SIZE * ITERATIONS) / (float)(test_end - test_start));
  176. fclose(f);
  177. free(data);
  178. }
  179. /**
  180. * @brief USB task
  181. *
  182. * Install USB Host Library and MSC driver.
  183. * Handle USB Host Library events
  184. *
  185. * @param[in] args Unused
  186. */
  187. static void usb_task(void *args)
  188. {
  189. const usb_host_config_t host_config = { .intr_flags = ESP_INTR_FLAG_LEVEL1 };
  190. ESP_ERROR_CHECK(usb_host_install(&host_config));
  191. const msc_host_driver_config_t msc_config = {
  192. .create_backround_task = true,
  193. .task_priority = 5,
  194. .stack_size = 4096,
  195. .callback = msc_event_cb,
  196. };
  197. ESP_ERROR_CHECK(msc_host_install(&msc_config));
  198. while (true) {
  199. uint32_t event_flags;
  200. usb_host_lib_handle_events(portMAX_DELAY, &event_flags);
  201. // Release devices once all clients has deregistered
  202. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS) {
  203. if (usb_host_device_free_all() == ESP_OK) {
  204. break;
  205. };
  206. }
  207. if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) {
  208. break;
  209. }
  210. }
  211. vTaskDelay(10); // Give clients some time to uninstall
  212. ESP_LOGI(TAG, "Deinitializing USB");
  213. ESP_ERROR_CHECK(usb_host_uninstall());
  214. vTaskDelete(NULL);
  215. }
  216. void app_main(void)
  217. {
  218. // Create FreeRTOS primitives
  219. app_queue = xQueueCreate(5, sizeof(app_message_t));
  220. assert(app_queue);
  221. BaseType_t task_created = xTaskCreate(usb_task, "usb_task", 4096, NULL, 2, NULL);
  222. assert(task_created);
  223. // Init BOOT button: Pressing the button simulates app request to exit
  224. // It will disconnect the USB device and uninstall the MSC driver and USB Host Lib
  225. const gpio_config_t input_pin = {
  226. .pin_bit_mask = BIT64(APP_QUIT_PIN),
  227. .mode = GPIO_MODE_INPUT,
  228. .pull_up_en = GPIO_PULLUP_ENABLE,
  229. .intr_type = GPIO_INTR_NEGEDGE,
  230. };
  231. ESP_ERROR_CHECK(gpio_config(&input_pin));
  232. ESP_ERROR_CHECK(gpio_install_isr_service(ESP_INTR_FLAG_LEVEL1));
  233. ESP_ERROR_CHECK(gpio_isr_handler_add(APP_QUIT_PIN, gpio_cb, NULL));
  234. ESP_LOGI(TAG, "Waiting for USB flash drive to be connected");
  235. msc_host_device_handle_t msc_device = NULL;
  236. msc_host_vfs_handle_t vfs_handle = NULL;
  237. // Perform all example operations in a loop to allow USB reconnections
  238. while (1) {
  239. app_message_t msg;
  240. xQueueReceive(app_queue, &msg, portMAX_DELAY);
  241. if (msg.id == APP_DEVICE_CONNECTED) {
  242. // 1. MSC flash drive connected. Open it and map it to Virtual File System
  243. ESP_ERROR_CHECK(msc_host_install_device(msg.data.new_dev_address, &msc_device));
  244. const esp_vfs_fat_mount_config_t mount_config = {
  245. .format_if_mount_failed = false,
  246. .max_files = 3,
  247. .allocation_unit_size = 8192,
  248. };
  249. ESP_ERROR_CHECK(msc_host_vfs_register(msc_device, MNT_PATH, &mount_config, &vfs_handle));
  250. // 2. Print information about the connected disk
  251. msc_host_device_info_t info;
  252. ESP_ERROR_CHECK(msc_host_get_device_info(msc_device, &info));
  253. msc_host_print_descriptors(msc_device);
  254. print_device_info(&info);
  255. // 3. List all the files in root directory
  256. ESP_LOGI(TAG, "ls command output:");
  257. struct dirent *d;
  258. DIR *dh = opendir(MNT_PATH);
  259. assert(dh);
  260. while ((d = readdir(dh)) != NULL) {
  261. printf("%s\n", d->d_name);
  262. }
  263. closedir(dh);
  264. // 4. The disk is mounted to Virtual File System, perform some basic demo file operation
  265. file_operations();
  266. // 5. Perform speed test
  267. speed_test();
  268. ESP_LOGI(TAG, "Example finished, you can disconnect the USB flash drive");
  269. }
  270. if ((msg.id == APP_DEVICE_DISCONNECTED) || (msg.id == APP_QUIT)) {
  271. if (vfs_handle) {
  272. ESP_ERROR_CHECK(msc_host_vfs_unregister(vfs_handle));
  273. vfs_handle = NULL;
  274. }
  275. if (msc_device) {
  276. ESP_ERROR_CHECK(msc_host_uninstall_device(msc_device));
  277. msc_device = NULL;
  278. }
  279. if (msg.id == APP_QUIT) {
  280. // This will cause the usb_task to exit
  281. ESP_ERROR_CHECK(msc_host_uninstall());
  282. break;
  283. }
  284. }
  285. }
  286. ESP_LOGI(TAG, "Done");
  287. gpio_isr_handler_remove(APP_QUIT_PIN);
  288. vQueueDelete(app_queue);
  289. }