32ESPecial Version 1.00 #9

Merged
Joe merged 8 commits from Cleanup_Nov_2025 into main 2025-11-30 21:46:46 +00:00
46 changed files with 647 additions and 470 deletions
Showing only changes of commit c979c38fd7 - Show all commits

View file

@ -40,8 +40,8 @@ This software in turn makes use of the following open-source software libraries
| ESP-IDF | 5.5.1 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://github.com/espressif/esp-idf/ | ESP-IDF | 5.5.1 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://github.com/espressif/esp-idf/
| espressif/button | 3.5.0 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/button | espressif/button | 3.5.0 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/button
| espressif/led_strip | 2.5.3 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/led_strip | espressif/led_strip | 2.5.3 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/led_strip
| espressif/usb_host_msc | 1.1.3 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/usb_host_msc | espressif/usb_host_msc | 1.1.4 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/usb_host_msc
| espressif/mdns | 1.8.2 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/mdns | espressif/mdns | 1.9.1 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/espressif/mdns
| chmorgan/esp-audio-player | 1.0.7 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/chmorgan/esp-audio-player | chmorgan/esp-audio-player | 1.0.7 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://components.espressif.com/components/chmorgan/esp-audio-player
| esp-libhelix-mp3 | 1.0.3 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://github.com/chmorgan/esp-libhelix-mp3 | esp-libhelix-mp3 | 1.0.3 | [Apache-2.0](https://spdx.org/licenses/Apache-2.0.html) | https://github.com/chmorgan/esp-libhelix-mp3
| libhelix-mp3 | f443079 | [RPSL](https://github.com/chmorgan/libhelix-mp3/blob/master/LICENSE.txt) | https://github.com/chmorgan/libhelix-mp3/ | libhelix-mp3 | f443079 | [RPSL](https://github.com/chmorgan/libhelix-mp3/blob/master/LICENSE.txt) | https://github.com/chmorgan/libhelix-mp3/

View file

@ -5,6 +5,7 @@ idf_component_register(
"." "."
REQUIRES REQUIRES
"SystemK" "SystemK"
"System_Events"
"driver" "driver"
"spiffs" "spiffs"
"esp-audio-player" "esp-audio-player"

View file

@ -1,4 +1,5 @@
#include <SystemK.h> #include <SystemK.h>
#include <System_Events.h>
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -245,6 +246,21 @@ SystemKResult_T Play_Sound_By_Prefix(const char *prefix)
{ {
SystemKResult_T result = SYSTEMK_RESULT_SUCCESS; SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
// Check for USB audio files.
EventBits_t bits = xEventGroupWaitBits(
Get_System_Events(),
SYS_USB_FS_PRESENT,
pdFALSE,
pdTRUE,
0);
if ((bits & SYS_USB_FS_PRESENT) == 0)
{
KLOG_ERROR(TAG, "USB file system not present!");
result = SYSTEMK_RESULT_FILESYSTEM_NOT_PRESENT;
}
else
{
const char *sounds_dir = "/usb/01"; const char *sounds_dir = "/usb/01";
char *filename = find_filename(sounds_dir, prefix); char *filename = find_filename(sounds_dir, prefix);
@ -261,6 +277,7 @@ SystemKResult_T Play_Sound_By_Prefix(const char *prefix)
{ {
result = SYSTEMK_RESULT_FILE_NOT_FOUND; result = SYSTEMK_RESULT_FILE_NOT_FOUND;
} }
}
return result; return result;
} }
@ -304,6 +321,7 @@ void I2SAudioTask(void *pvParameters)
if (xQueueReceive(xQueueAudio, &action, portMAX_DELAY) == pdPASS) if (xQueueReceive(xQueueAudio, &action, portMAX_DELAY) == pdPASS)
{ {
SystemKResult_T result = SYSTEMK_RESULT_SUCCESS;
switch (action.ID) switch (action.ID)
{ {
@ -318,23 +336,23 @@ void I2SAudioTask(void *pvParameters)
break; break;
case AUDIO_PLAY_STARTUP_SOUND: case AUDIO_PLAY_STARTUP_SOUND:
Play_Sound_By_Prefix("001"); result = Play_Sound_By_Prefix("001");
break; break;
case AUDIO_PLAY_SHOT_FIRED: case AUDIO_PLAY_SHOT_FIRED:
Play_Sound_By_Prefix("002"); result = Play_Sound_By_Prefix("002");
break; break;
case AUDIO_PLAY_TAG_RECEIVED: case AUDIO_PLAY_TAG_RECEIVED:
Play_Sound_By_Prefix("003"); result = Play_Sound_By_Prefix("003");
break; break;
case AUDIO_PLAY_TAGGED_OUT: case AUDIO_PLAY_TAGGED_OUT:
Play_Sound_By_Prefix("004"); result = Play_Sound_By_Prefix("004");
break; break;
case AUDIO_PLAY_MISFIRE: case AUDIO_PLAY_MISFIRE:
Play_Sound_By_Prefix("005"); result = Play_Sound_By_Prefix("005");
break; break;
case AUDIO_PRONOUNCE_NUMBER_0_TO_100: case AUDIO_PRONOUNCE_NUMBER_0_TO_100:
@ -347,103 +365,109 @@ void I2SAudioTask(void *pvParameters)
{ {
number = 101; number = 101;
} }
Play_Number_Sound(number); result = Play_Number_Sound(number);
break; break;
case AUDIO_PLAY_MENU_PROMPT: case AUDIO_PLAY_MENU_PROMPT:
Play_Sound_By_Prefix("006"); result = Play_Sound_By_Prefix("006");
break; break;
case AUDIO_PLAY_SELECTION_INDICATOR: case AUDIO_PLAY_SELECTION_INDICATOR:
Play_Sound_By_Prefix("007"); result = Play_Sound_By_Prefix("007");
break; break;
case AUDIO_PLAY_HEALTH_REMAINING: case AUDIO_PLAY_HEALTH_REMAINING:
Play_Sound_By_Prefix("008"); result = Play_Sound_By_Prefix("008");
break; break;
case AUDIO_PLAY_ELECTRONIC_DANCE_MUSIC: case AUDIO_PLAY_ELECTRONIC_DANCE_MUSIC:
Play_Sound_By_Prefix("009"); result = Play_Sound_By_Prefix("009");
break; break;
case AUDIO_PLAY_GENERIC_ERROR: case AUDIO_PLAY_GENERIC_ERROR:
Play_Sound_By_Prefix("010"); result = Play_Sound_By_Prefix("010");
break; break;
case AUDIO_PLAY_VOLUME_PROMPT: case AUDIO_PLAY_VOLUME_PROMPT:
Play_Sound_By_Prefix("011"); result = Play_Sound_By_Prefix("011");
break; break;
case AUDIO_PLAY_RIGHT_HANDED: case AUDIO_PLAY_RIGHT_HANDED:
Play_Sound_By_Prefix("012"); result = Play_Sound_By_Prefix("012");
break; break;
case AUDIO_PLAY_LEFT_HANDED: case AUDIO_PLAY_LEFT_HANDED:
Play_Sound_By_Prefix("013"); result = Play_Sound_By_Prefix("013");
break; break;
case AUDIO_PLAY_GAME_ON: case AUDIO_PLAY_GAME_ON:
Play_Sound_By_Prefix("014"); result = Play_Sound_By_Prefix("014");
break; break;
case AUDIO_PLAY_HARDWARE_SETTINGS_PROMPT: case AUDIO_PLAY_HARDWARE_SETTINGS_PROMPT:
Play_Sound_By_Prefix("015"); result = Play_Sound_By_Prefix("015");
break; break;
case AUDIO_PLAY_GAME_SETTINGS_PROMPT: case AUDIO_PLAY_GAME_SETTINGS_PROMPT:
Play_Sound_By_Prefix("016"); result = Play_Sound_By_Prefix("016");
break; break;
case AUDIO_PLAY_BONK: case AUDIO_PLAY_BONK:
Play_Sound_By_Prefix("017"); result = Play_Sound_By_Prefix("017");
break; break;
case AUDIO_PLAY_NEAR_MISS: case AUDIO_PLAY_NEAR_MISS:
Play_Sound_By_Prefix("018"); result = Play_Sound_By_Prefix("018");
break; break;
case AUDIO_PLAY_PLAYER_ID_PROMPT: case AUDIO_PLAY_PLAYER_ID_PROMPT:
Play_Sound_By_Prefix("019"); result = Play_Sound_By_Prefix("019");
break; break;
case AUDIO_PLAY_TEAM_ID_PROMPT: case AUDIO_PLAY_TEAM_ID_PROMPT:
Play_Sound_By_Prefix("020"); result = Play_Sound_By_Prefix("020");
break; break;
case AUDIO_PLAY_FRIENDLY_FIRE: case AUDIO_PLAY_FRIENDLY_FIRE:
KLOG_WARN(TAG, "\"Friendly Fire\" audio is disabled in this build."); KLOG_WARN(TAG, "\"Friendly Fire\" audio is disabled in this build.");
//Play_Sound_By_Prefix("021"); // result = Play_Sound_By_Prefix("021");
break; break;
case AUDIO_PLAY_STARTING_THEME: case AUDIO_PLAY_STARTING_THEME:
Play_Sound_By_Prefix("022"); result = Play_Sound_By_Prefix("022");
break; break;
case AUDIO_PLAY_BOOP: case AUDIO_PLAY_BOOP:
Play_Sound_By_Prefix("023"); result = Play_Sound_By_Prefix("023");
break; break;
case AUDIO_PLAY_BEEP: case AUDIO_PLAY_BEEP:
Play_Sound_By_Prefix("024"); result = Play_Sound_By_Prefix("024");
break; break;
case AUDIO_PLAY_REPROGRAMMING: case AUDIO_PLAY_REPROGRAMMING:
Play_Sound_By_Prefix("025"); result = Play_Sound_By_Prefix("025");
break; break;
case AUDIO_PLAY_BOMB: case AUDIO_PLAY_BOMB:
Play_Sound_By_Prefix("026"); result = Play_Sound_By_Prefix("026");
break; break;
case AUDIO_PLAY_GAME_OVER: case AUDIO_PLAY_GAME_OVER:
Play_Sound_By_Prefix("027"); result = Play_Sound_By_Prefix("027");
break; break;
default: default:
Play_Audio_File("/spiffs/bad.wav"); Play_Audio_File("/spiffs/KTag_broken.mp3");
break; break;
} }
if (result == SYSTEMK_RESULT_FILESYSTEM_NOT_PRESENT)
{
Play_Audio_File("/spiffs/KTag_broken.mp3");
}
else if (result == SYSTEMK_RESULT_SUCCESS)
{
if (action.Play_To_Completion == true) if (action.Play_To_Completion == true)
{ {
// Allow some time for the audio to start. // Allow some time for the audio to start.
@ -459,4 +483,5 @@ void I2SAudioTask(void *pvParameters)
} }
} }
} }
}
} }

View file

@ -9,6 +9,7 @@ idf_component_register(
"." "."
REQUIRES REQUIRES
"SystemK" "SystemK"
"System_Events"
"spiffs" "spiffs"
"driver" "driver"
"usb" "usb"

View file

@ -20,6 +20,7 @@
*/ */
#include <SystemK.h> #include <SystemK.h>
#include <System_Events.h>
#include <string.h> #include <string.h>
#include "esp_spiffs.h" #include "esp_spiffs.h"
@ -80,5 +81,9 @@ void Initialize_SPIFFS(SemaphoreHandle_t init_complete)
fclose(f); fclose(f);
KLOG_INFO(TAG, ">>> %s <<<", buf); KLOG_INFO(TAG, ">>> %s <<<", buf);
xEventGroupSetBits(Get_System_Events(), SYS_SPIFFS_READY);
KLOG_INFO(TAG, "SPIFFS initialized.");
xSemaphoreGive(init_complete); xSemaphoreGive(init_complete);
} }

View file

@ -22,6 +22,7 @@
// From https://github.com/espressif/esp-idf/blob/master/examples/peripherals/usb/host/msc/main/msc_example_main.c // From https://github.com/espressif/esp-idf/blob/master/examples/peripherals/usb/host/msc/main/msc_example_main.c
#include <SystemK.h> #include <SystemK.h>
#include <System_Events.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
#include <assert.h> #include <assert.h>
@ -262,13 +263,11 @@ static void usb_host_task(void *args)
if (usb_host_device_free_all() == ESP_OK) if (usb_host_device_free_all() == ESP_OK)
{ {
KLOG_INFO(TAG, "USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS"); KLOG_INFO(TAG, "USB_HOST_LIB_EVENT_FLAGS_NO_CLIENTS");
// break;
}; };
} }
if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE) if (event_flags & USB_HOST_LIB_EVENT_FLAGS_ALL_FREE)
{ {
KLOG_INFO(TAG, "USB_HOST_LIB_EVENT_FLAGS_ALL_FREE"); KLOG_INFO(TAG, "USB_HOST_LIB_EVENT_FLAGS_ALL_FREE");
// break;
} }
} }
} }
@ -356,6 +355,7 @@ static void app_usb_task(void *args)
else else
{ {
Current_State = USB_STATE_VFS_REGISTERED; Current_State = USB_STATE_VFS_REGISTERED;
xEventGroupSetBits(Get_System_Events(), SYS_USB_FS_PRESENT);
xSemaphoreGive(init_complete); xSemaphoreGive(init_complete);
} }
} }
@ -371,6 +371,7 @@ static void app_usb_task(void *args)
if (msg.id == USB_DEVICE_DISCONNECTED) if (msg.id == USB_DEVICE_DISCONNECTED)
{ {
Current_State = USB_STATE_PROCESSING_DISCONNECTION; Current_State = USB_STATE_PROCESSING_DISCONNECTION;
xEventGroupClearBits(Get_System_Events(), SYS_USB_FS_PRESENT);
} }
} }
vTaskDelay(pdMS_TO_TICKS(1000)); vTaskDelay(pdMS_TO_TICKS(1000));

@ -1 +1 @@
Subproject commit 6d8dab53e0c2efbb1aa17b3aaad556dc65005a4d Subproject commit edf80ba83b5924144bba611abdd3a54447ed92b0

View file

@ -0,0 +1,8 @@
idf_component_register(
SRCS
"System_Events.c"
INCLUDE_DIRS
"."
REQUIRES
"SystemK"
)

View file

@ -0,0 +1,41 @@
/*
* This program source code file is part of the KTag project, a DIY laser tag
* game with customizable features and wide interoperability.
*
* 🛡 <https://ktag.clubk.club> 🃞
*
* Copyright © 2025 Joseph P. Kearney and the KTag developers.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* There should be a copy of the GNU Affero General Public License in the LICENSE
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
*/
#include "System_Events.h"
static EventGroupHandle_t The_System_Events = NULL;
static const char *TAG = "System Events";
void Initialize_System_Events(void)
{
if (The_System_Events == NULL)
{
The_System_Events = xEventGroupCreate();
KLOG_INFO(TAG, "System Events initialized.");
}
}
EventGroupHandle_t Get_System_Events(void)
{
return The_System_Events;
}

View file

@ -0,0 +1,41 @@
/*
* This program source code file is part of the KTag project, a DIY laser tag
* game with customizable features and wide interoperability.
*
* 🛡 <https://ktag.clubk.club> 🃞
*
* Copyright © 2025 Joseph P. Kearney and the KTag developers.
*
* This program is free software: you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
* details.
*
* There should be a copy of the GNU Affero General Public License in the LICENSE
* file in the root of this repository. If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef SYSTEM_EVENTS_H
#define SYSTEM_EVENTS_H
#include <SystemK.h>
#include <freertos/FreeRTOS.h>
#include <freertos/event_groups.h>
// System-wide event bits
#define SYS_NVS_READY (1 << 0)
#define SYS_SPIFFS_READY (1 << 1)
#define SYS_USB_FS_PRESENT (1 << 2)
#define SYS_BLE_READY (1 << 3)
#define SYS_AUDIO_READY (1 << 4)
#define SYS_CONFIG_LOADED (1 << 5)
EventGroupHandle_t Get_System_Events(void);
void Initialize_System_Events(void);
#endif // SYSTEM_EVENTS_H

View file

@ -48,7 +48,7 @@ dependencies:
type: service type: service
version: 0.5.3 version: 0.5.3
espressif/mdns: espressif/mdns:
component_hash: 3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5 component_hash: 29e47564b1a7ee778135e17fbbf2a2773f71c97ebabfe626c8eda7c958a7ad16
dependencies: dependencies:
- name: idf - name: idf
require: private require: private
@ -56,13 +56,20 @@ dependencies:
source: source:
registry_url: https://components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
version: 1.8.2 version: 1.9.1
espressif/usb_host_msc: espressif/usb_host_msc:
component_hash: efbf44743b0f1f1f808697a671064531ae4661ccbce84632637261f8f670b375 component_hash: 865a651c08d0bf2ce255a369778375e493df588dfb0720c3d97e12bfdcc4c0f9
dependencies: dependencies:
- name: idf - name: idf
require: private require: private
version: '>=4.4.1' version: '>=4.4.1'
- name: espressif/usb
registry_url: https://components.espressif.com
require: public
rules:
- if: idf_version >=6.0
- if: target not in ["linux"]
version: ^1.0.0
source: source:
registry_url: https://components.espressif.com/ registry_url: https://components.espressif.com/
type: service type: service
@ -70,7 +77,8 @@ dependencies:
- esp32s2 - esp32s2
- esp32s3 - esp32s3
- esp32p4 - esp32p4
version: 1.1.3 - esp32h4
version: 1.1.4
idf: idf:
source: source:
type: idf type: idf
@ -82,6 +90,6 @@ direct_dependencies:
- espressif/mdns - espressif/mdns
- espressif/usb_host_msc - espressif/usb_host_msc
- idf - idf
manifest_hash: b398d97279b89c77c23123af7f755f3bd8058248ead23eadcb3a50ab152a2e6c manifest_hash: 9164944e752c9209cbb452b373b4f381078750cd7e2458c8ff9059700987d0b3
target: esp32s3 target: esp32s3
version: 2.0.0 version: 2.0.0

View file

@ -3,8 +3,8 @@ dependencies:
chmorgan/esp-libhelix-mp3: "^1.0.3" chmorgan/esp-libhelix-mp3: "^1.0.3"
chmorgan/esp-audio-player: "^1.0.7" chmorgan/esp-audio-player: "^1.0.7"
espressif/button: "^3.5.0" espressif/button: "^3.5.0"
espressif/mdns: "^1.8.2" espressif/mdns: "^1.9.1"
espressif/usb_host_msc: "^1.1.3" espressif/usb_host_msc: "^1.1.4"
## Required IDF version (>=5.1 is required for the SPI backend of the led-strip component.) ## Required IDF version (>=5.1 is required for the SPI backend of the led-strip component.)
## We tested with 5.5.1. ## We tested with 5.5.1.

View file

@ -41,6 +41,7 @@
#include <string.h> #include <string.h>
#include <SystemK.h> #include <SystemK.h>
#include <System_Events.h>
#include <SPIFFS.h> #include <SPIFFS.h>
#include <USB.h> #include <USB.h>
#include <I2S_Audio.h> #include <I2S_Audio.h>
@ -62,6 +63,9 @@ void app_main(void)
KLOG_INFO(TAG, VERSION_AS_STR()); KLOG_INFO(TAG, VERSION_AS_STR());
KLOG_INFO(TAG, "Initializing app..."); KLOG_INFO(TAG, "Initializing app...");
Initialize_System_Events();
init_complete_semaphore = xSemaphoreCreateBinary(); init_complete_semaphore = xSemaphoreCreateBinary();
// Initialize NVS — it is used by both the BLE and WiFi drivers. // Initialize NVS — it is used by both the BLE and WiFi drivers.
@ -71,7 +75,17 @@ void app_main(void)
ESP_ERROR_CHECK(nvs_flash_erase()); ESP_ERROR_CHECK(nvs_flash_erase());
ret = nvs_flash_init(); ret = nvs_flash_init();
} }
ESP_ERROR_CHECK(ret);
if (ret == ESP_OK)
{
xEventGroupSetBits(Get_System_Events(), SYS_NVS_READY);
KLOG_INFO(TAG, "NVS initialized.");
}
else
{
KLOG_ERROR(TAG, "Error initializing NVS: %s", esp_err_to_name(ret));
}
Initialize_SPIFFS(init_complete_semaphore); Initialize_SPIFFS(init_complete_semaphore);
if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE) if (xSemaphoreTake(init_complete_semaphore, pdMS_TO_TICKS(INITIALIZATION_TIMEOUT_IN_ms)) != pdTRUE)

View file

@ -1 +1 @@
3ec0af5f6bce310512e90f482388d21cc7c0e99668172d2f895356165fc6f7c5 29e47564b1a7ee778135e17fbbf2a2773f71c97ebabfe626c8eda7c958a7ad16

View file

@ -3,6 +3,6 @@ commitizen:
bump_message: 'bump(mdns): $current_version -> $new_version' bump_message: 'bump(mdns): $current_version -> $new_version'
pre_bump_hooks: python ../../ci/changelog.py mdns pre_bump_hooks: python ../../ci/changelog.py mdns
tag_format: mdns-v$version tag_format: mdns-v$version
version: 1.8.2 version: 1.9.1
version_files: version_files:
- idf_component.yml - idf_component.yml

View file

@ -1,5 +1,32 @@
# Changelog # Changelog
## [1.9.1](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.1)
### Bug Fixes
- Fix to use tagged AFL image + minor format fix ([2b2f009a](https://github.com/espressif/esp-protocols/commit/2b2f009a))
- Fix unused variable `dcst` warning for wifi-remote chips ([081eef88](https://github.com/espressif/esp-protocols/commit/081eef88))
## [1.9.0](https://github.com/espressif/esp-protocols/commits/mdns-v1.9.0)
### Features
- support null value for boolean txt records ([fa96de3b](https://github.com/espressif/esp-protocols/commit/fa96de3b))
### Bug Fixes
- Add test case for bool/NULL txt handling ([5068f221](https://github.com/espressif/esp-protocols/commit/5068f221))
- Temporary fix for build issues on IDF master ([0197c994](https://github.com/espressif/esp-protocols/commit/0197c994))
- Add tests for delegated answers ([487a746d](https://github.com/espressif/esp-protocols/commit/487a746d))
- Add fuzzing into mdns CI ([af6bb1b5](https://github.com/espressif/esp-protocols/commit/af6bb1b5))
- Host test to use hw_support include dir ([8bba3a97](https://github.com/espressif/esp-protocols/commit/8bba3a97))
- Fixes case where we create our own malloc/free allocators, therefore we need to call mdns_mem_free and not free ([63bf7091](https://github.com/espressif/esp-protocols/commit/63bf7091))
- put srv/txt records in additional section for ptr queries ([b7b8c5db](https://github.com/espressif/esp-protocols/commit/b7b8c5db))
### Updated
- ci(common): Update test component dir for IDFv6.0 ([18418c83](https://github.com/espressif/esp-protocols/commit/18418c83))
## [1.8.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.2) ## [1.8.2](https://github.com/espressif/esp-protocols/commits/mdns-v1.8.2)
### Bug Fixes ### Bug Fixes

View file

@ -1,8 +1,6 @@
dependencies: dependencies:
## Required IDF version
idf: ">=5.0"
espressif/mdns: espressif/mdns:
version: "^1.0.0" version: ^1.0.0
override_path: "../../../" idf: '>=5.0'
protocol_examples_common: protocol_examples_common:
path: ${IDF_PATH}/examples/common_components/protocol_examples_common path: ${IDF_PATH}/examples/common_components/protocol_examples_common

View file

@ -12,6 +12,7 @@ CONFIG_EXAMPLE_CONNECT_ETHERNET=y
CONFIG_EXAMPLE_CONNECT_WIFI=n CONFIG_EXAMPLE_CONNECT_WIFI=n
CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y CONFIG_EXAMPLE_USE_INTERNAL_ETHERNET=y
CONFIG_EXAMPLE_ETH_PHY_IP101=y CONFIG_EXAMPLE_ETH_PHY_IP101=y
CONFIG_EXAMPLE_ETH_PHY_GENERIC=y
CONFIG_EXAMPLE_ETH_MDC_GPIO=23 CONFIG_EXAMPLE_ETH_MDC_GPIO=23
CONFIG_EXAMPLE_ETH_MDIO_GPIO=18 CONFIG_EXAMPLE_ETH_MDIO_GPIO=18
CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5 CONFIG_EXAMPLE_ETH_PHY_RST_GPIO=5

View file

@ -7,7 +7,7 @@ documentation: https://docs.espressif.com/projects/esp-protocols/mdns/docs/lates
issues: https://github.com/espressif/esp-protocols/issues issues: https://github.com/espressif/esp-protocols/issues
repository: git://github.com/espressif/esp-protocols.git repository: git://github.com/espressif/esp-protocols.git
repository_info: repository_info:
commit_sha: e9d7350219dfb5e39eb56e5ef60c094190888c55 commit_sha: 3bfa00389de6f0d6d40efda8bea808380899a43d
path: components/mdns path: components/mdns
url: https://github.com/espressif/esp-protocols/tree/master/components/mdns url: https://github.com/espressif/esp-protocols/tree/master/components/mdns
version: 1.8.2 version: 1.9.1

View file

@ -33,7 +33,7 @@ static void _mdns_browse_send(mdns_browse_t *browse, mdns_if_t interface);
#if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 0) #if ESP_IDF_VERSION <= ESP_IDF_VERSION_VAL(5, 1, 0)
#define MDNS_ESP_WIFI_ENABLED CONFIG_SOC_WIFI_SUPPORTED #define MDNS_ESP_WIFI_ENABLED CONFIG_SOC_WIFI_SUPPORTED
#else #else
#define MDNS_ESP_WIFI_ENABLED CONFIG_ESP_WIFI_ENABLED #define MDNS_ESP_WIFI_ENABLED (CONFIG_ESP_WIFI_ENABLED || CONFIG_ESP_WIFI_REMOTE_ENABLED)
#endif #endif
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) #if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
@ -1795,8 +1795,8 @@ static bool _mdns_create_answer_from_service(mdns_tx_packet_t *packet, mdns_serv
// According to RFC6763-section12.1, for DNS-SD, SRV, TXT and all address records // According to RFC6763-section12.1, for DNS-SD, SRV, TXT and all address records
// should be included in additional records. // should be included in additional records.
if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) || if (!_mdns_alloc_answer(&packet->answers, MDNS_TYPE_PTR, service, NULL, false, false) ||
!_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_SRV, service, NULL, send_flush, false) || !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_SRV, service, NULL, send_flush, false) ||
!_mdns_alloc_answer(is_delegated ? &packet->additional : &packet->answers, MDNS_TYPE_TXT, service, NULL, send_flush, false) || !_mdns_alloc_answer(&packet->additional, MDNS_TYPE_TXT, service, NULL, send_flush, false) ||
!_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush, !_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_A, service, host, send_flush,
false) || false) ||
!_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host, !_mdns_alloc_answer((shared || is_delegated) ? &packet->additional : &packet->answers, MDNS_TYPE_AAAA, service, host,
@ -2656,6 +2656,7 @@ static mdns_txt_linked_item_t *_mdns_allocate_txt(size_t num_items, mdns_txt_ite
mdns_mem_free(new_item); mdns_mem_free(new_item);
break; break;
} }
if (txt[i].value) {
new_item->value = mdns_mem_strdup(txt[i].value); new_item->value = mdns_mem_strdup(txt[i].value);
if (!new_item->value) { if (!new_item->value) {
mdns_mem_free((char *)new_item->key); mdns_mem_free((char *)new_item->key);
@ -2663,6 +2664,10 @@ static mdns_txt_linked_item_t *_mdns_allocate_txt(size_t num_items, mdns_txt_ite
break; break;
} }
new_item->value_len = strlen(new_item->value); new_item->value_len = strlen(new_item->value);
} else {
new_item->value = NULL;
new_item->value_len = 0;
}
new_item->next = new_txt; new_item->next = new_txt;
new_txt = new_item; new_txt = new_item;
} }
@ -4488,9 +4493,9 @@ void mdns_preset_if_handle_system_event(void *arg, esp_event_base_t event_base,
return; return;
} }
esp_netif_dhcp_status_t dcst;
#if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP) #if MDNS_ESP_WIFI_ENABLED && (CONFIG_MDNS_PREDEF_NETIF_STA || CONFIG_MDNS_PREDEF_NETIF_AP)
if (event_base == WIFI_EVENT) { if (event_base == WIFI_EVENT) {
esp_netif_dhcp_status_t dcst;
switch (event_id) { switch (event_id) {
case WIFI_EVENT_STA_CONNECTED: case WIFI_EVENT_STA_CONNECTED:
if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_STA), &dcst)) { if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_STA), &dcst)) {
@ -4517,6 +4522,7 @@ void mdns_preset_if_handle_system_event(void *arg, esp_event_base_t event_base,
#endif #endif
#if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH #if CONFIG_ETH_ENABLED && CONFIG_MDNS_PREDEF_NETIF_ETH
if (event_base == ETH_EVENT) { if (event_base == ETH_EVENT) {
esp_netif_dhcp_status_t dcst;
switch (event_id) { switch (event_id) {
case ETHERNET_EVENT_CONNECTED: case ETHERNET_EVENT_CONNECTED:
if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_ETH), &dcst)) { if (!esp_netif_dhcpc_get_status(esp_netif_from_preset_if(MDNS_IF_ETH), &dcst)) {

View file

@ -154,7 +154,7 @@
} \ } \
} }
#define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; free(_q); } #define queueFree(type, queue) while (queue) { type * _q = queue; queue = queue->next; mdns_mem_free(_q); }
#define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1) #define PCB_STATE_IS_PROBING(s) (s->state > PCB_OFF && s->state < PCB_ANNOUNCE_1)
#define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING) #define PCB_STATE_IS_ANNOUNCING(s) (s->state > PCB_PROBE_3 && s->state < PCB_RUNNING)

View file

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0 # SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging import logging
import re import re
@ -92,10 +92,58 @@ class DnsPythonWrapper:
if expect is None: if expect is None:
expect = name expect = name
if expected: if expected:
assert any(expect in answer for answer in answers), f"Expected record '{expect}' not found in answer section" assert any(expect in answer for answer in answers), f"Expected record '{expect}' not in answer section"
else: else:
assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section" assert not any(expect in answer for answer in answers), f"Unexpected record '{expect}' found in answer section"
def parse_section(self, response, section: str, rdtype_text: str):
"""Parse a specific response section (answer, authority, additional) for given rdtype.
Returns list of textual records for that rdtype.
"""
out = []
if not response:
return out
rrsets = []
if section == 'answer':
rrsets = response.answer
elif section == 'authority':
rrsets = response.authority
elif section == 'additional':
rrsets = response.additional
else:
raise ValueError('invalid section')
for rr in rrsets:
if dns.rdatatype.to_text(rr.rdtype) != rdtype_text:
continue
for item in rr.items:
full = (
f'{rr.name} {rr.ttl} '
f'{dns.rdataclass.to_text(rr.rdclass)} '
f'{dns.rdatatype.to_text(rr.rdtype)} '
f'{item.to_text()}'
)
out.append(full)
return out
def check_additional(self, response, rdtype_text: str, owner_contains: str, expected: bool = True, expect_substr: str | None = None):
"""Check Additional section for an RR of type rdtype_text whose owner includes owner_contains.
If expect_substr is provided, also require it to appear in the textual RR.
"""
records = self.parse_section(response, 'additional', rdtype_text)
logger.info(f'additional({rdtype_text}): {records}')
def _matches(line: str) -> bool:
in_owner = owner_contains in line
has_val = (expect_substr in line) if expect_substr else True
return in_owner and has_val
found = any(_matches(r) for r in records)
if expected:
assert found, f"Expected {rdtype_text} for {owner_contains} in Additional not found"
else:
assert not found, f"Unexpected {rdtype_text} for {owner_contains} found in Additional"
if __name__ == '__main__': if __name__ == '__main__':
if len(sys.argv) < 3: if len(sys.argv) < 3:

View file

@ -1,4 +1,4 @@
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD # SPDX-FileCopyrightText: 2024-2025 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: Unlicense OR CC0-1.0 # SPDX-License-Identifier: Unlicense OR CC0-1.0
import logging import logging
@ -65,6 +65,17 @@ def test_add_service(mdns_console, dig_app):
dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True) dig_app.check_record('_http._tcp.local', query_type='PTR', expected=True)
def test_ptr_additional_records_for_service(dig_app):
# Query PTR for the service type and ensure SRV/TXT are in Additional (RFC 6763 §12.1)
resp = dig_app.run_query('_http._tcp.local', query_type='PTR')
# Answer section should have at least one PTR to the instance
answers = dig_app.parse_answer_section(resp, 'PTR')
assert any('test_service._http._tcp.local' in a for a in answers)
# Additional section should include SRV and TXT for the same instance
dig_app.check_additional(resp, 'SRV', 'test_service._http._tcp.local', expected=True)
dig_app.check_additional(resp, 'TXT', 'test_service._http._tcp.local', expected=True)
def test_remove_service(mdns_console, dig_app): def test_remove_service(mdns_console, dig_app):
mdns_console.send_input('mdns_service_remove _http _tcp') mdns_console.send_input('mdns_service_remove _http _tcp')
mdns_console.send_input('mdns_service_lookup _http _tcp') mdns_console.send_input('mdns_service_lookup _http _tcp')

View file

@ -1,7 +1,9 @@
#INSTR=off
TEST_NAME=test TEST_NAME=test
FUZZ=afl-fuzz FUZZ=afl-fuzz
COMPONENTS_DIR=$(IDF_PATH)/components COMPONENTS_DIR=$(IDF_PATH)/components
COMPILER_ICLUDE_DIR=$(shell echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf) # Use ESP32 toolchain include path if available, otherwise fall back to system includes for host-based compilation
COMPILER_INCLUDE_DIR=$(shell if command -v xtensa-esp32-elf-gcc >/dev/null 2>&1; then echo `which xtensa-esp32-elf-gcc | xargs dirname | xargs dirname`/xtensa-esp32-elf; else echo /usr; fi)
CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversion -Wno-macro-redefined -Wno-int-to-void-pointer-cast -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ \ CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversion -Wno-macro-redefined -Wno-int-to-void-pointer-cast -DHOOK_MALLOC_FAILED -DESP_EVENT_H_ -D__ESP_LOG_H__ \
-I. -I../.. -I../../include -I../../private_include -I ./build/config \ -I. -I../.. -I../../include -I../../private_include -I ./build/config \
@ -34,7 +36,8 @@ CFLAGS=-g -Wno-unused-value -Wno-missing-declarations -Wno-pointer-bool-conversi
-I$(COMPONENTS_DIR)/soc/src/esp32/include \ -I$(COMPONENTS_DIR)/soc/src/esp32/include \
-I$(COMPONENTS_DIR)/xtensa/include \ -I$(COMPONENTS_DIR)/xtensa/include \
-I$(COMPONENTS_DIR)/xtensa/esp32/include \ -I$(COMPONENTS_DIR)/xtensa/esp32/include \
-I$(COMPILER_ICLUDE_DIR)/include -I$(COMPONENTS_DIR)/esp_hw_support/etm/include \
-I$(COMPILER_INCLUDE_DIR)/include
MDNS_C_DEPENDENCY_INJECTION=-include mdns_di.h MDNS_C_DEPENDENCY_INJECTION=-include mdns_di.h
@ -76,7 +79,18 @@ $(TEST_NAME): $(OBJECTS)
@$(LD) $(OBJECTS) -o $@ $(LDLIBS) @$(LD) $(OBJECTS) -o $@ $(LDLIBS)
fuzz: $(TEST_NAME) fuzz: $(TEST_NAME)
@$(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME) # timeout returns 124 if time limit is reached, original return code otherwise
# pass only if: fuzzing was running smoothly until timeout AND no crash found
@timeout 10m $(FUZZ) -i "in" -o "out" -- ./$(TEST_NAME) || \
if [ $$? -eq 124 ]; then \
if [ -n "$$(find out/default/crashes -type f 2>/dev/null)" ]; then \
echo "Crashes found!"; \
tar -czf out/default/crashes.tar.gz -C out/default crashes; \
exit 1; \
fi \
else \
exit 1; \
fi
clean: clean:
@rm -rf *.o *.SYM $(TEST_NAME) out @rm -rf *.o *.SYM $(TEST_NAME) out

View file

@ -55,8 +55,7 @@
#define pdMS_TO_TICKS(a) a #define pdMS_TO_TICKS(a) a
#define xSemaphoreTake(s,d) true #define xSemaphoreTake(s,d) true
#define xTaskDelete(a) #define vTaskDelete(a) free(NULL)
#define vTaskDelete(a) free(a)
#define xSemaphoreGive(s) #define xSemaphoreGive(s)
#define xQueueCreateMutex(s) #define xQueueCreateMutex(s)
#define _mdns_pcb_init(a,b) true #define _mdns_pcb_init(a,b) true
@ -66,7 +65,7 @@
#define vSemaphoreDelete(s) free(s) #define vSemaphoreDelete(s) free(s)
#define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U #define queueQUEUE_TYPE_MUTEX ( ( uint8_t ) 1U
#define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1) #define xTaskCreatePinnedToCore(a,b,c,d,e,f,g) *(f) = malloc(1)
#define xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) true #define xTaskCreateStaticPinnedToCore(a,b,c,d,e,f,g,h) ((void*)1)
#define vTaskDelay(m) usleep((m)*0) #define vTaskDelay(m) usleep((m)*0)
#define esp_random() (rand()%UINT32_MAX) #define esp_random() (rand()%UINT32_MAX)
@ -139,4 +138,8 @@ TaskHandle_t xTaskGetCurrentTaskHandle(void);
void xTaskNotifyGive(TaskHandle_t task); void xTaskNotifyGive(TaskHandle_t task);
BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time); BaseType_t xTaskNotifyWait(uint32_t bits_entry_clear, uint32_t bits_exit_clear, uint32_t *value, TickType_t wait_time);
static inline void xTaskGetStaticBuffers(void *pvTaskBuffer, void *pvStackBuffer, void *pvTaskTCB)
{
}
#endif //_ESP32_COMPAT_H_ #endif //_ESP32_COMPAT_H_

View file

@ -78,30 +78,20 @@ static int mdns_test_service_txt_set(const char *service, const char *proto, ui
static int mdns_test_sub_service_add(const char *sub_name, const char *service_name, const char *proto, uint32_t port) static int mdns_test_sub_service_add(const char *sub_name, const char *service_name, const char *proto, uint32_t port)
{ {
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) { if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
// This is expected failure as the service thread is not running return ESP_FAIL;
} }
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) { if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
return ESP_FAIL; return ESP_FAIL;
} }
int ret = mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name); return mdns_service_subtype_add_for_host(NULL, service_name, proto, NULL, sub_name);
a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
return ret;
} }
static int mdns_test_service_add(const char *service_name, const char *proto, uint32_t port) static int mdns_test_service_add(const char *service_name, const char *proto, uint32_t port)
{ {
if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) { if (mdns_service_add(NULL, service_name, proto, port, NULL, 0)) {
// This is expected failure as the service thread is not running return ESP_FAIL;
} }
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) { if (mdns_test_mdns_get_service_item(service_name, proto) == NULL) {
return ESP_FAIL; return ESP_FAIL;
@ -266,9 +256,6 @@ int main(int argc, char **argv)
} }
#ifndef MDNS_NO_SERVICES #ifndef MDNS_NO_SERVICES
mdns_service_remove_all(); mdns_service_remove_all();
mdns_action_t *a = NULL;
GetLastItem(&a);
mdns_test_execute_action(a);
#endif #endif
ForceTaskDelete(); ForceTaskDelete();
mdns_free(); mdns_free();

View file

@ -1,7 +1,15 @@
# This is the project CMakeLists.txt file for the test subproject # This is the project CMakeLists.txt file for the test subproject
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
set(EXTRA_COMPONENT_DIRS ../.. "$ENV{IDF_PATH}/tools/unit-test-app/components")
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
if("${IDF_VERSION_MAJOR}.${IDF_VERSION_MINOR}" VERSION_GREATER_EQUAL "6.0")
set(test_component_dir $ENV{IDF_PATH}/tools/test_apps/components)
else()
set(test_component_dir $ENV{IDF_PATH}/tools/unit-test-app/components)
endif()
set(EXTRA_COMPONENT_DIRS ../..
${test_component_dir})
project(mdns_test) project(mdns_test)

View file

@ -61,6 +61,45 @@ TEST(mdns, init_deinit)
esp_event_loop_delete_default(); esp_event_loop_delete_default();
} }
TEST(mdns, boolean_txt_null_value)
{
mdns_result_t *results = NULL;
test_case_uses_tcpip();
TEST_ASSERT_EQUAL(ESP_OK, esp_event_loop_create_default());
TEST_ASSERT_EQUAL(ESP_OK, mdns_init());
TEST_ASSERT_EQUAL(ESP_OK, mdns_hostname_set(MDNS_HOSTNAME));
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_add(MDNS_INSTANCE, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, MDNS_SERVICE_PORT, NULL, 0));
mdns_txt_item_t txt_data[] = {
{"bool", NULL},
{"key", "value"},
};
const size_t txt_data_count = sizeof(txt_data) / sizeof(txt_data[0]);
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_txt_set(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, txt_data, txt_data_count));
yield_to_all_priorities();
TEST_ASSERT_EQUAL(ESP_OK, mdns_lookup_selfhosted_service(NULL, MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO, 1, &results));
TEST_ASSERT_NOT_EQUAL(NULL, results);
TEST_ASSERT_NOT_EQUAL(NULL, results->txt);
TEST_ASSERT_EQUAL(txt_data_count, results->txt_count);
bool found_bool = false;
for (size_t i = 0; i < results->txt_count; ++i) {
if (strcmp(results->txt[i].key, "bool") == 0) {
TEST_ASSERT_NOT_EQUAL(NULL, results->txt_value_len);
TEST_ASSERT_EQUAL_UINT8(0, results->txt_value_len[i]);
found_bool = true;
}
}
TEST_ASSERT_TRUE(found_bool);
mdns_query_results_free(results);
TEST_ASSERT_EQUAL(ESP_OK, mdns_service_remove(MDNS_SERVICE_NAME, MDNS_SERVICE_PROTO));
mdns_free();
esp_event_loop_delete_default();
}
TEST(mdns, api_fails_with_expected_err) TEST(mdns, api_fails_with_expected_err)
{ {
mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL}, mdns_txt_item_t serviceTxtData[CONFIG_MDNS_MAX_SERVICES] = { {NULL, NULL},
@ -290,6 +329,7 @@ TEST_GROUP_RUNNER(mdns)
RUN_TEST_CASE(mdns, init_deinit) RUN_TEST_CASE(mdns, init_deinit)
RUN_TEST_CASE(mdns, add_remove_service) RUN_TEST_CASE(mdns, add_remove_service)
RUN_TEST_CASE(mdns, add_remove_deleg_service) RUN_TEST_CASE(mdns, add_remove_deleg_service)
RUN_TEST_CASE(mdns, boolean_txt_null_value)
} }

View file

@ -1 +1 @@
efbf44743b0f1f1f808697a671064531ae4661ccbce84632637261f8f670b375 865a651c08d0bf2ce255a369778375e493df588dfb0720c3d97e12bfdcc4c0f9

View file

@ -1,3 +1,8 @@
## 1.1.4
- Added public API support for formatting
- Added support for ESP32-H4
## 1.1.3 ## 1.1.3
- Implemented request sense, to get sense data from USB device in case of an error - Implemented request sense, to get sense data from USB device in case of an error

View file

@ -1,10 +1,20 @@
# 1. IDF version >= 6.0 does not have usb component: usb from IDF component manager will be used
# 2. For linux target, we can't use IDF component manager to get usb component, we need to add it 'the old way'
# with EXTRA_COMPONENT_DIRS because mocking of managed components is not supported yet.
# This is acceptable workaround for testing.
set(requires "fatfs")
if((${IDF_VERSION_MAJOR} LESS 6) OR ("${IDF_TARGET}" STREQUAL "linux"))
list(APPEND requires usb)
endif()
set(sources src/msc_scsi_bot.c set(sources src/msc_scsi_bot.c
src/diskio_usb.c src/diskio_usb.c
src/msc_host.c src/msc_host.c
src/msc_host_vfs.c) src/msc_host_vfs.c)
idf_component_register( SRCS ${sources} idf_component_register(SRCS ${sources}
INCLUDE_DIRS include include/usb # 'include/usb' is here for backwards compatibility INCLUDE_DIRS include include/usb # 'include/usb' is here for backwards compatibility
PRIV_INCLUDE_DIRS private_include include/esp_private PRIV_INCLUDE_DIRS private_include include/esp_private
REQUIRES usb fatfs REQUIRES ${requires}
PRIV_REQUIRES heap ) PRIV_REQUIRES heap
)

View file

@ -1,13 +1,23 @@
dependencies: dependencies:
idf: '>=4.4.1' idf: '>=4.4.1'
usb:
public: true
rules:
- if: idf_version >=6.0
- if: target not in ["linux"]
version: ^1.0.0
description: USB Host MSC driver description: USB Host MSC driver
files:
exclude:
- test_app
repository: git://github.com/espressif/esp-usb.git repository: git://github.com/espressif/esp-usb.git
repository_info: repository_info:
commit_sha: 0d5b6e959b2ba6993f27c703f5b26f93557c9066 commit_sha: 0c2750cea32ebcff2c5bffd04fadf9579fa97009
path: host/class/msc/usb_host_msc path: host/class/msc/usb_host_msc
targets: targets:
- esp32s2 - esp32s2
- esp32s3 - esp32s3
- esp32p4 - esp32p4
- esp32h4
url: https://github.com/espressif/esp-usb/tree/master/host/class/msc/usb_host_msc url: https://github.com/espressif/esp-usb/tree/master/host/class/msc/usb_host_msc
version: 1.1.3 version: 1.1.4

View file

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -16,6 +16,20 @@ extern "C" {
typedef struct msc_host_vfs *msc_host_vfs_handle_t; /**< VFS handle to attached Mass Storage device */ typedef struct msc_host_vfs *msc_host_vfs_handle_t; /**< VFS handle to attached Mass Storage device */
/**
* @brief Format MSC device.
*
* @param[in] device Device handle obtained from MSC callback provided upon initialization
* @param[in] mount_config Mount configuration
* @param[in] vfs_handle Handle to MSC device associated with registered VFS
* @return esp_err_t
* @return
* - ESP_OK: Format completed
* - ESP_ERR_INVALID_ARG: All arguments must be present and couldn't be NULL
* - ESP_ERR_MSC_FORMAT_FAILED: Formatting failed
*/
esp_err_t msc_host_vfs_format(msc_host_device_handle_t device, const esp_vfs_fat_mount_config_t *mount_config, const msc_host_vfs_handle_t vfs_handle);
/** /**
* @brief Register MSC device to Virtual filesystem. * @brief Register MSC device to Virtual filesystem.
* *

View file

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -52,6 +52,18 @@ static esp_err_t msc_format_storage(size_t block_size, size_t allocation_size, c
return ESP_OK; return ESP_OK;
} }
esp_err_t msc_host_vfs_format(msc_host_device_handle_t device, const esp_vfs_fat_mount_config_t *mount_config, const msc_host_vfs_handle_t vfs_handle)
{
MSC_RETURN_ON_INVALID_ARG(device);
MSC_RETURN_ON_INVALID_ARG(mount_config);
MSC_RETURN_ON_INVALID_ARG(vfs_handle);
size_t block_size = ((msc_device_t *)device)->disk.block_size;
size_t alloc_size = mount_config->allocation_unit_size;
return msc_format_storage(block_size, alloc_size, vfs_handle->drive);
}
static void dealloc_msc_vfs(msc_host_vfs_t *vfs) static void dealloc_msc_vfs(msc_host_vfs_t *vfs)
{ {
free(vfs->base_path); free(vfs->base_path);
@ -89,7 +101,16 @@ esp_err_t msc_host_vfs_register(msc_host_device_handle_t device,
MSC_GOTO_ON_FALSE( vfs->base_path = strdup(base_path), ESP_ERR_NO_MEM ); MSC_GOTO_ON_FALSE( vfs->base_path = strdup(base_path), ESP_ERR_NO_MEM );
vfs->pdrv = pdrv; vfs->pdrv = pdrv;
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 3, 0)
esp_vfs_fat_conf_t conf = {
.base_path = base_path,
.fat_drive = drive,
.max_files = mount_config->max_files,
};
MSC_GOTO_ON_ERROR( esp_vfs_fat_register_cfg(&conf, &fs) );
#else
MSC_GOTO_ON_ERROR( esp_vfs_fat_register(base_path, drive, mount_config->max_files, &fs) ); MSC_GOTO_ON_ERROR( esp_vfs_fat_register(base_path, drive, mount_config->max_files, &fs) );
#endif
FRESULT fresult = f_mount(fs, drive, 1); FRESULT fresult = f_mount(fs, drive, 1);
@ -110,10 +131,10 @@ fail:
if (diskio_registered) { if (diskio_registered) {
ff_diskio_unregister(pdrv); ff_diskio_unregister(pdrv);
} }
esp_vfs_fat_unregister_path(base_path);
if (fs) { if (fs) {
f_mount(NULL, drive, 0); f_mount(NULL, drive, 0);
} }
esp_vfs_fat_unregister_path(base_path);
dealloc_msc_vfs(vfs); dealloc_msc_vfs(vfs);
return ret; return ret;
} }

View file

@ -3,11 +3,6 @@
cmake_minimum_required(VERSION 3.16) cmake_minimum_required(VERSION 3.16)
include($ENV{IDF_PATH}/tools/cmake/project.cmake) include($ENV{IDF_PATH}/tools/cmake/project.cmake)
set(EXTRA_COMPONENT_DIRS
../../usb_host_msc
../../../../../device/esp_tinyusb
)
# "Trim" the build. Include the minimal set of components, main, and anything it depends on. # "Trim" the build. Include the minimal set of components, main, and anything it depends on.
set(COMPONENTS main) set(COMPONENTS main)

View file

@ -1,7 +1,7 @@
| Supported Targets | ESP32-S2 | ESP32-S3 | | Supported Targets | ESP32-S2 | ESP32-S3 | ESP32-P4 |
| ----------------- | -------- | -------- | | ----------------- | -------- | -------- | -------- |
# USB: CDC Class test application # USB: MSC Class test application
## MSC driver ## MSC driver
@ -10,5 +10,9 @@ raw access to MSC device and sudden disconnect is tested.
### Hardware Required ### Hardware Required
This test requires two ESP32-S2/S3 boards with a interconnected USB peripherals, This test requires two ESP32 development board with USB-OTG support. The development boards shall have interconnected USB peripherals,
one acting as host running MSC host driver and another MSC device driver (tinyusb). one acting as host running MSC host driver and another MSC device driver (tinyusb).
## Selecting the USB Component
To manually select which USB Component shall be used to build this test application, please refer to the following documentation page: [Manual USB component selection](../../../../../docs/host/usb_host_lib/usb_component_manual_selection.md).

View file

@ -0,0 +1,18 @@
## IDF Component Manager Manifest File
dependencies:
# Needed as DUT
espressif/usb_host_msc:
version: "*"
override_path: "../../../usb_host_msc"
# Needed for MSC mock device
espressif/esp_tinyusb:
version: "*"
override_path: "../../../../../../device/esp_tinyusb"
espressif/usb:
version: "*"
override_path: "../../../../../usb"
rules: # Both if clauses must be fulfilled to override the component
- if: "$ENV_VAR_USB_COMP_MANAGED == yes" # Environmental variable to select between managed (esp-usb) and native (esp-idf) USB Component
- if: "idf_version >=5.4" # Use managed component only for 5.4 and above

View file

@ -1,5 +1,5 @@
/* /*
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
@ -7,23 +7,21 @@
#include "esp_log.h" #include "esp_log.h"
#include "tinyusb.h" #include "tinyusb.h"
#include "esp_idf_version.h" #include "tinyusb_default_config.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
#include "test_common.h" #include "test_common.h"
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#include "esp_check.h" #include "esp_check.h"
#include "driver/gpio.h" #include "driver/gpio.h"
#include "tusb_msc_storage.h" #include "tinyusb_msc.h"
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) */ #if SOC_SDMMC_HOST_SUPPORTED
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED
#include "diskio_impl.h" #include "diskio_impl.h"
#include "diskio_sdmmc.h" #include "diskio_sdmmc.h"
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED */ #endif /* SOC_SDMMC_HOST_SUPPORTED */
#if SOC_USB_OTG_SUPPORTED #if SOC_USB_OTG_SUPPORTED
/* sd-card configuration to be done by user */ /* sd-card configuration to be done by user */
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED #if SOC_SDMMC_HOST_SUPPORTED
#define SDMMC_BUS_WIDTH 4 /* Select the bus width of SD or MMC interface (4 or 1). #define SDMMC_BUS_WIDTH 4 /* Select the bus width of SD or MMC interface (4 or 1).
Note that even if 1 line mode is used, D3 pin of the SD card must Note that even if 1 line mode is used, D3 pin of the SD card must
have a pull-up resistor connected. Otherwise the card may enter have a pull-up resistor connected. Otherwise the card may enter
@ -34,13 +32,12 @@
#define PIN_D1 38 /* D1 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */ #define PIN_D1 38 /* D1 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */
#define PIN_D2 33 /* D2 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */ #define PIN_D2 33 /* D2 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */
#define PIN_D3 34 /* D3 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */ #define PIN_D3 34 /* D3 GPIO number (applicable when width SDMMC_BUS_WIDTH is 4) */
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED */ #endif /* SOC_SDMMC_HOST_SUPPORTED */
static const char *TAG = "msc_example"; static const char *TAG = "msc_example";
/* TinyUSB descriptors /* TinyUSB descriptors
********************************************************************* */ ********************************************************************* */
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN) #define TUSB_DESC_TOTAL_LEN (TUD_CONFIG_DESC_LEN + TUD_MSC_DESC_LEN)
enum { enum {
@ -53,14 +50,34 @@ enum {
EDPT_MSC_IN = 0x81, EDPT_MSC_IN = 0x81,
}; };
static uint8_t const desc_configuration[] = { static uint8_t const msc_fs_desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA // Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100), TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size // Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, TUD_OPT_HIGH_SPEED ? 512 : 64), TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 64),
}; };
#if (TUD_OPT_HIGH_SPEED)
static const uint8_t msc_hs_desc_configuration[] = {
// Config number, interface count, string index, total length, attribute, power in mA
TUD_CONFIG_DESCRIPTOR(1, ITF_NUM_TOTAL, 0, TUSB_DESC_TOTAL_LEN, TUSB_DESC_CONFIG_ATT_REMOTE_WAKEUP, 100),
// Interface number, string index, EP Out & EP In address, EP size
TUD_MSC_DESCRIPTOR(ITF_NUM_MSC, 0, EDPT_MSC_OUT, EDPT_MSC_IN, 512),
};
static const tusb_desc_device_qualifier_t device_qualifier = {
.bLength = sizeof(tusb_desc_device_qualifier_t),
.bDescriptorType = TUSB_DESC_DEVICE_QUALIFIER,
.bcdUSB = 0x0200,
.bDeviceClass = TUSB_CLASS_MISC,
.bDeviceSubClass = MISC_SUBCLASS_COMMON,
.bDeviceProtocol = MISC_PROTOCOL_IAD,
.bMaxPacketSize0 = CFG_TUD_ENDPOINT0_SIZE,
.bNumConfigurations = 0x01,
.bReserved = 0
};
#endif // TUD_OPT_HIGH_SPEED
static tusb_desc_device_t descriptor_config = { static tusb_desc_device_t descriptor_config = {
.bLength = sizeof(descriptor_config), .bLength = sizeof(descriptor_config),
.bDescriptorType = TUSB_DESC_DEVICE, .bDescriptorType = TUSB_DESC_DEVICE,
@ -86,10 +103,8 @@ static char const *string_desc_arr[] = {
//"123456", // 3: Serials //"123456", // 3: Serials
//"Test MSC", // 4. MSC //"Test MSC", // 4. MSC
}; };
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */
/*********************************************************************** TinyUSB descriptors*/ /*********************************************************************** TinyUSB descriptors*/
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
#define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4 #define VBUS_MONITORING_GPIO_NUM GPIO_NUM_4
static void configure_vbus_monitoring(void) static void configure_vbus_monitoring(void)
{ {
@ -103,27 +118,27 @@ static void configure_vbus_monitoring(void)
}; };
ESP_ERROR_CHECK(gpio_config(&vbus_gpio_config)); ESP_ERROR_CHECK(gpio_config(&vbus_gpio_config));
} }
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */
static void storage_init(void) static void storage_init(void)
{ {
ESP_LOGI(TAG, "USB MSC initialization"); ESP_LOGI(TAG, "USB MSC initialization");
const tinyusb_config_t tusb_cfg = {
.external_phy = false, tinyusb_config_t tusb_cfg = TINYUSB_DEFAULT_CONFIG();
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) tusb_cfg.descriptor.device = &descriptor_config;
.device_descriptor = &descriptor_config, tusb_cfg.descriptor.full_speed_config = msc_fs_desc_configuration;
.configuration_descriptor = desc_configuration, #if (TUD_OPT_HIGH_SPEED)
.string_descriptor = string_desc_arr, tusb_cfg.descriptor.high_speed_config = msc_hs_desc_configuration;
.string_descriptor_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]), tusb_cfg.descriptor.qualifier = &device_qualifier;
.self_powered = true, #endif // TUD_OPT_HIGH_SPEED
.vbus_monitor_io = VBUS_MONITORING_GPIO_NUM tusb_cfg.descriptor.string = string_desc_arr;
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */ tusb_cfg.descriptor.string_count = sizeof(string_desc_arr) / sizeof(string_desc_arr[0]);
}; tusb_cfg.phy.self_powered = true;
tusb_cfg.phy.vbus_monitor_io = VBUS_MONITORING_GPIO_NUM;
ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg)); ESP_ERROR_CHECK(tinyusb_driver_install(&tusb_cfg));
ESP_LOGI(TAG, "USB initialization DONE"); ESP_LOGI(TAG, "USB initialization DONE");
} }
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle) static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
{ {
ESP_LOGI(TAG, "Initializing wear levelling"); ESP_LOGI(TAG, "Initializing wear levelling");
@ -136,28 +151,27 @@ static esp_err_t storage_init_spiflash(wl_handle_t *wl_handle)
return wl_mount(data_partition, wl_handle); return wl_mount(data_partition, wl_handle);
} }
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */
void device_app(void) void device_app(void)
{ {
ESP_LOGI(TAG, "Initializing storage..."); ESP_LOGI(TAG, "Initializing storage...");
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
configure_vbus_monitoring(); configure_vbus_monitoring();
static wl_handle_t wl_handle = WL_INVALID_HANDLE; static wl_handle_t wl_handle = WL_INVALID_HANDLE;
ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle)); ESP_ERROR_CHECK(storage_init_spiflash(&wl_handle));
tinyusb_msc_spiflash_config_t config_spi; const tinyusb_msc_storage_config_t config = {
config_spi.wl_handle = wl_handle; .medium.wl_handle = wl_handle, // Set the medium of the storage to the wear leveling
ESP_ERROR_CHECK(tinyusb_msc_storage_init_spiflash(&config_spi)); };
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) */ ESP_ERROR_CHECK(tinyusb_msc_new_storage_spiflash(&config, NULL));
storage_init(); storage_init();
while (1) { while (1) {
vTaskDelay(100); vTaskDelay(100);
} }
} }
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED #if SOC_SDMMC_HOST_SUPPORTED
static esp_err_t storage_init_sdmmc(sdmmc_card_t **card) static esp_err_t storage_init_sdmmc(sdmmc_card_t **card)
{ {
esp_err_t ret = ESP_OK; esp_err_t ret = ESP_OK;
@ -237,225 +251,16 @@ void device_app_sdmmc(void)
static sdmmc_card_t *card = NULL; static sdmmc_card_t *card = NULL;
ESP_ERROR_CHECK(storage_init_sdmmc(&card)); ESP_ERROR_CHECK(storage_init_sdmmc(&card));
tinyusb_msc_sdmmc_config_t config_sdmmc; const tinyusb_msc_storage_config_t config = {
config_sdmmc.card = card; .medium.card = card, // Set the medium of the storage to the SDMMC card
ESP_ERROR_CHECK(tinyusb_msc_storage_init_sdmmc(&config_sdmmc)); };
ESP_ERROR_CHECK(tinyusb_msc_new_storage_sdmmc(&config, NULL));
storage_init(); storage_init();
while (1) { while (1) {
vTaskDelay(100); vTaskDelay(100);
} }
} }
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED */ #endif /* SOC_SDMMC_HOST_SUPPORTED */
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
// whether host does safe-eject
static bool ejected = false;
// Some MCU doesn't have enough 8KB SRAM to store the whole disk
// We will use Flash as read-only disk with board that has
// CFG_EXAMPLE_MSC_READONLY defined
uint8_t msc_disk[DISK_BLOCK_NUM][DISK_BLOCK_SIZE] = {
//------------- Block0: Boot Sector -------------//
// byte_per_sector = DISK_BLOCK_SIZE; fat12_sector_num_16 = DISK_BLOCK_NUM;
// sector_per_cluster = 1; reserved_sectors = 1;
// fat_num = 1; fat12_root_entry_num = 16;
// sector_per_fat = 1; sector_per_track = 1; head_num = 1; hidden_sectors = 0;
// drive_number = 0x80; media_type = 0xf8; extended_boot_signature = 0x29;
// filesystem_type = "FAT12 "; volume_serial_number = 0x1234; volume_label = "TinyUSB MSC";
// FAT magic code at offset 510-511
{
0xEB, 0x3C, 0x90, 0x4D, 0x53, 0x44, 0x4F, 0x53, 0x35, 0x2E, 0x30, 0x00, 0x02, 0x01, 0x01, 0x00,
0x01, 0x10, 0x00, 0x10, 0x00, 0xF8, 0x01, 0x00, 0x01, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x29, 0x34, 0x12, 0x00, 0x00, 'T', 'i', 'n', 'y', 'U',
'S', 'B', ' ', 'M', 'S', 'C', 0x46, 0x41, 0x54, 0x31, 0x32, 0x20, 0x20, 0x20, 0x00, 0x00,
// Zero up to 2 last bytes of FAT magic code
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 'F', 'A', 'T', '3', '2', ' ', ' ', ' ', 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x55, 0xAA
},
//------------- Block1: FAT12 Table -------------//
{
0xF8, 0xFF, 0xFF, 0xFF, 0x0F // // first 2 entries must be F8FF, third entry is cluster end of readme file
},
//------------- Block2: Root Directory -------------//
{
// first entry is volume label
'T', 'i', 'n', 'y', 'U', 'S', 'B', ' ', 'M', 'S', 'C', 0x08, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4F, 0x6D, 0x65, 0x43, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
// second entry is readme file
'R', 'E', 'A', 'D', 'M', 'E', ' ', ' ', 'T', 'X', 'T', 0x20, 0x00, 0xC6, 0x52, 0x6D,
0x65, 0x43, 0x65, 0x43, 0x00, 0x00, 0x88, 0x6D, 0x65, 0x43, 0x02, 0x00,
sizeof(README_CONTENTS) - 1, 0x00, 0x00, 0x00 // readme's files size (4 Bytes)
},
//------------- Block3: Readme Content -------------//
README_CONTENTS
};
// Invoked when received SCSI_CMD_INQUIRY
// Application fill vendor id, product id and revision with string up to 8, 16, 4 characters respectively
void tud_msc_inquiry_cb(uint8_t lun, uint8_t vendor_id[8], uint8_t product_id[16], uint8_t product_rev[4])
{
(void) lun;
const char vid[] = "TinyUSB";
const char pid[] = "Mass Storage";
const char rev[] = "1.0";
memcpy(vendor_id, vid, strlen(vid));
memcpy(product_id, pid, strlen(pid));
memcpy(product_rev, rev, strlen(rev));
}
// Invoked when received Test Unit Ready command.
// return true allowing host to read/write this LUN e.g SD card inserted
bool tud_msc_test_unit_ready_cb(uint8_t lun)
{
(void) lun;
// RAM disk is ready until ejected
if (ejected) {
tud_msc_set_sense(lun, SCSI_SENSE_NOT_READY, 0x3a, 0x00);
return false;
}
return true;
}
// Invoked when received SCSI_CMD_READ_CAPACITY_10 and SCSI_CMD_READ_FORMAT_CAPACITY to determine the disk size
// Application update block count and block size
void tud_msc_capacity_cb(uint8_t lun, uint32_t *block_count, uint16_t *block_size)
{
(void) lun;
*block_count = DISK_BLOCK_NUM;
*block_size = DISK_BLOCK_SIZE;
}
// Invoked when received Start Stop Unit command
// - Start = 0 : stopped power mode, if load_eject = 1 : unload disk storage
// - Start = 1 : active mode, if load_eject = 1 : load disk storage
bool tud_msc_start_stop_cb(uint8_t lun, uint8_t power_condition, bool start, bool load_eject)
{
(void) lun;
(void) power_condition;
if ( load_eject ) {
if (start) {
// load disk storage
} else {
// unload disk storage
ejected = true;
}
}
return true;
}
// Callback invoked when received READ10 command.
// Copy disk's data to buffer (up to bufsize) and return number of copied bytes.
int32_t tud_msc_read10_cb(uint8_t lun, uint32_t lba, uint32_t offset, void *buffer, uint32_t bufsize)
{
(void) lun;
uint8_t const *addr = msc_disk[lba] + offset;
memcpy(buffer, addr, bufsize);
return bufsize;
}
// Callback invoked when received WRITE10 command.
// Process data in buffer to disk's storage and return number of written bytes
int32_t tud_msc_write10_cb(uint8_t lun, uint32_t lba, uint32_t offset, uint8_t *buffer, uint32_t bufsize)
{
(void) lun;
#ifndef CFG_EXAMPLE_MSC_READONLY
uint8_t *addr = msc_disk[lba] + offset;
memcpy(addr, buffer, bufsize);
#else
(void) lba; (void) offset; (void) buffer;
#endif
return bufsize;
}
// Callback invoked when received an SCSI command not in built-in list below
// - READ_CAPACITY10, READ_FORMAT_CAPACITY, INQUIRY, MODE_SENSE6, REQUEST_SENSE
// - READ10 and WRITE10 has their own callbacks
int32_t tud_msc_scsi_cb (uint8_t lun, uint8_t const scsi_cmd[16], void *buffer, uint16_t bufsize)
{
// read10 & write10 has their own callback and MUST not be handled here
void const *response = NULL;
uint16_t resplen = 0;
// most scsi handled is input
bool in_xfer = true;
switch (scsi_cmd[0]) {
case SCSI_CMD_PREVENT_ALLOW_MEDIUM_REMOVAL:
// Host is about to read/write etc ... better not to disconnect disk
resplen = 0;
break;
default:
// Set Sense = Invalid Command Operation
tud_msc_set_sense(lun, SCSI_SENSE_ILLEGAL_REQUEST, 0x20, 0x00);
// negative means error -> tinyusb could stall and/or response with failed status
resplen = -1;
break;
}
// return resplen must not larger than bufsize
if ( resplen > bufsize ) {
resplen = bufsize;
}
if ( response && (resplen > 0) ) {
if (in_xfer) {
memcpy(buffer, response, resplen);
} else {
// SCSI output
}
}
return resplen;
}
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) */
#endif /* SOC_USB_OTG_SUPPORTED */ #endif /* SOC_USB_OTG_SUPPORTED */

View file

@ -7,17 +7,17 @@
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include "unity.h" #include "unity.h"
#include "esp_heap_caps.h" #include "unity_test_runner.h"
#include "unity_test_utils_memory.h"
static size_t before_free_8bit; void setUp(void)
static size_t before_free_32bit;
#define TEST_MEMORY_LEAK_THRESHOLD (-530)
static void check_leak(size_t before_free, size_t after_free, const char *type)
{ {
ssize_t delta = after_free - before_free; unity_utils_record_free_mem();
printf("MALLOC_CAP_%s: Before %u bytes free, After %u bytes free (delta %d)\n", type, before_free, after_free, delta); }
TEST_ASSERT_MESSAGE(delta >= TEST_MEMORY_LEAK_THRESHOLD, "memory leak");
void tearDown(void)
{
unity_utils_evaluate_leaks();
} }
void app_main(void) void app_main(void)
@ -35,23 +35,7 @@ void app_main(void)
printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n"); printf("|______/ /_______ / |______ / |__| \\___ >____ > |__| \r\n");
printf(" \\/ \\/ \\/ \\/ \r\n"); printf(" \\/ \\/ \\/ \\/ \r\n");
UNITY_BEGIN(); unity_utils_setup_heap_record(80);
unity_utils_set_leak_level(530);
unity_run_menu(); unity_run_menu();
UNITY_END();
}
/* setUp runs before every test */
void setUp(void)
{
before_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
before_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
}
/* tearDown runs after every test */
void tearDown(void)
{
size_t after_free_8bit = heap_caps_get_free_size(MALLOC_CAP_8BIT);
size_t after_free_32bit = heap_caps_get_free_size(MALLOC_CAP_32BIT);
check_leak(before_free_8bit, after_free_8bit, "8BIT");
check_leak(before_free_32bit, after_free_32bit, "32BIT");
} }

View file

@ -1,12 +1,10 @@
/* /*
* SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD * SPDX-FileCopyrightText: 2021-2024 Espressif Systems (Shanghai) CO LTD
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#pragma once #pragma once
#include "esp_idf_version.h"
enum { enum {
// FatFS only allows to format disks with number of blocks greater than 128 // FatFS only allows to format disks with number of blocks greater than 128
DISK_BLOCK_NUM = 128 + 1, DISK_BLOCK_NUM = 128 + 1,
@ -14,9 +12,9 @@ enum {
}; };
void device_app(void); void device_app(void);
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED #if SOC_SDMMC_HOST_SUPPORTED
void device_app_sdmmc(void); void device_app_sdmmc(void);
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED */ #endif /* SOC_SDMMC_HOST_SUPPORTED */
#define README_CONTENTS \ #define README_CONTENTS \
"This is tinyusb's MassStorage Class demo.\r\n\r\n\ "This is tinyusb's MassStorage Class demo.\r\n\r\n\

View file

@ -5,20 +5,21 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#include "soc/soc_caps.h"
#if SOC_USB_OTG_SUPPORTED
#include "unity.h" #include "unity.h"
#include <string.h> #include <string.h>
#include <unistd.h> #include <unistd.h>
#include <inttypes.h> #include <inttypes.h>
#include "esp_private/usb_phy.h" #include "esp_idf_version.h"
#include "esp_private/msc_scsi_bot.h" #include "esp_private/msc_scsi_bot.h"
#include "esp_private/usb_phy.h"
#include "usb/usb_host.h" #include "usb/usb_host.h"
#include "usb/msc_host_vfs.h" #include "usb/msc_host_vfs.h"
#include "test_common.h" #include "test_common.h"
#include "esp_idf_version.h"
#include "../private_include/msc_common.h" #include "../private_include/msc_common.h"
#if SOC_USB_OTG_SUPPORTED
static const char *TAG = "APP"; static const char *TAG = "APP";
#define ESP_OK_ASSERT(exp) TEST_ASSERT_EQUAL(ESP_OK, exp) #define ESP_OK_ASSERT(exp) TEST_ASSERT_EQUAL(ESP_OK, exp)
@ -34,18 +35,63 @@ static SemaphoreHandle_t ready_to_deinit_usb;
static msc_host_device_handle_t device; static msc_host_device_handle_t device;
static msc_host_vfs_handle_t vfs_handle; static msc_host_vfs_handle_t vfs_handle;
static volatile bool waiting_for_sudden_disconnect; static volatile bool waiting_for_sudden_disconnect;
// usb_host_lib_set_root_port_power is used to force toggle connection, primary developed for esp32p4
// esp32p4 is supported from IDF 5.3
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
static usb_phy_handle_t phy_hdl = NULL; static usb_phy_handle_t phy_hdl = NULL;
// Force connection/disconnection using PHY
static void force_conn_state(bool connected, TickType_t delay_ticks) static void force_conn_state(bool connected, TickType_t delay_ticks)
{ {
TEST_ASSERT(phy_hdl); TEST_ASSERT_NOT_EQUAL(NULL, phy_hdl);
if (delay_ticks > 0) { if (delay_ticks > 0) {
//Delay of 0 ticks causes a yield. So skip if delay_ticks is 0. // Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
vTaskDelay(delay_ticks); vTaskDelay(delay_ticks);
} }
ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN)); ESP_ERROR_CHECK(usb_phy_action(phy_hdl, (connected) ? USB_PHY_ACTION_HOST_ALLOW_CONN : USB_PHY_ACTION_HOST_FORCE_DISCONN));
} }
// Initialize the internal USB PHY to connect to the USB OTG peripheral. We manually install the USB PHY for testing
static bool install_phy(void)
{
usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_INT,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_UNDEFINED, // In Host mode, the speed is determined by the connected device
};
TEST_ASSERT_EQUAL(ESP_OK, usb_new_phy(&phy_config, &phy_hdl));
// Return true, to skip_phy_setup during the usb_host_install()
return true;
}
static void delete_phy(void)
{
TEST_ASSERT_EQUAL(ESP_OK, usb_del_phy(phy_hdl)); // Tear down USB PHY
phy_hdl = NULL;
}
#else // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
// Force connection/disconnection using root port power
static void force_conn_state(bool connected, TickType_t delay_ticks)
{
if (delay_ticks > 0) {
// Delay of 0 ticks causes a yield. So skip if delay_ticks is 0.
vTaskDelay(delay_ticks);
}
ESP_ERROR_CHECK(usb_host_lib_set_root_port_power(connected));
}
static bool install_phy(void)
{
// Return false, NOT to skip_phy_setup during the usb_host_install()
return false;
}
static void delete_phy(void) {}
#endif // ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 3, 0)
static void msc_event_cb(const msc_host_event_t *event, void *arg) static void msc_event_cb(const msc_host_event_t *event, void *arg)
{ {
if (waiting_for_sudden_disconnect) { if (waiting_for_sudden_disconnect) {
@ -138,21 +184,6 @@ static void msc_task(void *args)
vTaskDelete(NULL); vTaskDelete(NULL);
} }
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
static void check_file_content(const char *file_path, const char *expected)
{
ESP_LOGI(TAG, "Reading %s:", file_path);
FILE *file = fopen(file_path, "r");
TEST_ASSERT_NOT_NULL_MESSAGE(file, "Could not open file");
char content[200];
size_t read_cnt = fread(content, 1, sizeof(content), file);
TEST_ASSERT_EQUAL_MESSAGE(strlen(expected), read_cnt, "Error in reading file");
TEST_ASSERT_EQUAL_STRING(content, expected);
fclose(file);
}
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) */
static void check_sudden_disconnect(void) static void check_sudden_disconnect(void)
{ {
uint8_t data[512]; uint8_t data[512];
@ -168,7 +199,7 @@ static void check_sudden_disconnect(void)
TEST_ASSERT_EQUAL(0, fflush(file)); TEST_ASSERT_EQUAL(0, fflush(file));
ESP_LOGI(TAG, "Trigger a disconnect"); ESP_LOGI(TAG, "Trigger a disconnect");
//Trigger a disconnect // Trigger a disconnect
waiting_for_sudden_disconnect = true; waiting_for_sudden_disconnect = true;
force_conn_state(false, 0); force_conn_state(false, 0);
@ -189,21 +220,12 @@ static void msc_test_init(void)
ready_to_deinit_usb = xSemaphoreCreateBinary(); ready_to_deinit_usb = xSemaphoreCreateBinary();
TEST_ASSERT( app_queue = xQueueCreate(5, sizeof(msc_host_event_t)) ); TEST_ASSERT( app_queue = xQueueCreate(5, sizeof(msc_host_event_t)) );
const bool skip_phy_setup = install_phy();
//Initialize the internal USB PHY to connect to the USB OTG peripheral. We manually install the USB PHY for testing
usb_phy_config_t phy_config = {
.controller = USB_PHY_CTRL_OTG,
.target = USB_PHY_TARGET_INT,
.otg_mode = USB_OTG_MODE_HOST,
.otg_speed = USB_PHY_SPEED_UNDEFINED, //In Host mode, the speed is determined by the connected device
};
ESP_OK_ASSERT(usb_new_phy(&phy_config, &phy_hdl));
const usb_host_config_t host_config = { const usb_host_config_t host_config = {
.skip_phy_setup = true, .skip_phy_setup = skip_phy_setup,
.intr_flags = ESP_INTR_FLAG_LEVEL1, .intr_flags = ESP_INTR_FLAG_LEVEL1,
}; };
ESP_OK_ASSERT( usb_host_install(&host_config) ); ESP_OK_ASSERT( usb_host_install(&host_config) );
task_created = xTaskCreatePinnedToCore(handle_usb_events, "usb_events", 2 * 2048, NULL, 2, NULL, 0); task_created = xTaskCreatePinnedToCore(handle_usb_events, "usb_events", 2 * 2048, NULL, 2, NULL, 0);
TEST_ASSERT(task_created); TEST_ASSERT(task_created);
} }
@ -247,9 +269,7 @@ static void msc_test_deinit(void)
vSemaphoreDelete(ready_to_deinit_usb); vSemaphoreDelete(ready_to_deinit_usb);
vTaskDelay(10); // Wait to finish any ongoing USB operations vTaskDelay(10); // Wait to finish any ongoing USB operations
ESP_OK_ASSERT( usb_host_uninstall() ); ESP_OK_ASSERT( usb_host_uninstall() );
//Tear down USB PHY delete_phy();
ESP_OK_ASSERT(usb_del_phy(phy_hdl));
phy_hdl = NULL;
vQueueDelete(app_queue); vQueueDelete(app_queue);
vTaskDelay(10); // Wait for FreeRTOS to clean up deleted tasks vTaskDelay(10); // Wait for FreeRTOS to clean up deleted tasks
@ -269,8 +289,8 @@ static void write_read_sectors(void)
memset(write_data, 0x55, DISK_BLOCK_SIZE); memset(write_data, 0x55, DISK_BLOCK_SIZE);
memset(read_data, 0, DISK_BLOCK_SIZE); memset(read_data, 0, DISK_BLOCK_SIZE);
scsi_cmd_write10(device, write_data, 10, 1, DISK_BLOCK_SIZE); ESP_OK_ASSERT( scsi_cmd_write10(device, write_data, 10, 1, DISK_BLOCK_SIZE));
scsi_cmd_read10(device, read_data, 10, 1, DISK_BLOCK_SIZE); ESP_OK_ASSERT( scsi_cmd_read10(device, read_data, 10, 1, DISK_BLOCK_SIZE));
TEST_ASSERT_EQUAL_MEMORY(write_data, read_data, DISK_BLOCK_SIZE); TEST_ASSERT_EQUAL_MEMORY(write_data, read_data, DISK_BLOCK_SIZE);
} }
@ -306,21 +326,6 @@ TEST_CASE("sectors_can_be_written_and_read", "[usb_msc]")
msc_teardown(); msc_teardown();
} }
/**
* @brief Check README content
*
* This test strictly requires our implementation of USB MSC Mock device.
* This test will fail for usualW flash drives, as they don't have README.TXT file on them.
*/
#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0)
TEST_CASE("check_README_content", "[usb_msc]")
{
msc_setup();
check_file_content("/usb/README.TXT", README_CONTENTS);
msc_teardown();
}
#endif /* ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(5, 0, 0) */
esp_err_t bot_execute_command(msc_device_t *device, uint8_t *cbw, void *data, size_t size); esp_err_t bot_execute_command(msc_device_t *device, uint8_t *cbw, void *data, size_t size);
/** /**
* @brief Error recovery testcase * @brief Error recovery testcase
@ -454,6 +459,26 @@ TEST_CASE("can_be_formated", "[usb_msc]")
mount_config.format_if_mount_failed = false; mount_config.format_if_mount_failed = false;
} }
/**
* @brief USB MSC API format testcase
* @attention This testcase deletes all content on the USB MSC device.
* The device must be reset in order to contain the FILE_NAME again.
*/
TEST_CASE("can_be_formated_by_api", "[usb_msc]")
{
printf("Create file on MSC device\n");
msc_setup();
write_read_file(FILE_NAME);
printf("Format storage device using msc_host_vfs_format\n");
esp_err_t ret = msc_host_vfs_format(device, &mount_config, vfs_handle);
TEST_ASSERT_EQUAL(ESP_OK, ret);
printf("Verify file does not exist after formatting\n");
TEST_ASSERT_FALSE(file_exists(FILE_NAME));
msc_teardown();
}
static void print_device_info(msc_host_device_info_t *info) static void print_device_info(msc_host_device_info_t *info)
{ {
const size_t megabyte = 1024 * 1024; const size_t megabyte = 1024 * 1024;
@ -539,11 +564,11 @@ TEST_CASE("mock_device_app", "[usb_msc_device][ignore]")
device_app(); device_app();
} }
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED #if SOC_SDMMC_HOST_SUPPORTED
TEST_CASE("mock_device_app", "[usb_msc_device_sdmmc][ignore]") TEST_CASE("mock_device_app", "[usb_msc_device_sdmmc][ignore]")
{ {
device_app_sdmmc(); device_app_sdmmc();
} }
#endif /* ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0) && SOC_SDMMC_HOST_SUPPORTED */ #endif /* SOC_SDMMC_HOST_SUPPORTED */
#endif /* SOC_USB_OTG_SUPPORTED */ #endif /* SOC_USB_OTG_SUPPORTED */

View file

@ -9,6 +9,7 @@ from pytest_embedded_idf.dut import IdfDut
@pytest.mark.esp32s2 @pytest.mark.esp32s2
@pytest.mark.esp32s3 @pytest.mark.esp32s3
@pytest.mark.esp32p4
@pytest.mark.usb_host @pytest.mark.usb_host
@pytest.mark.parametrize('count', [ @pytest.mark.parametrize('count', [
2, 2,
@ -17,10 +18,10 @@ def test_usb_host_msc(dut: Tuple[IdfDut, IdfDut]) -> None:
device = dut[0] device = dut[0]
host = dut[1] host = dut[1]
# 2.1 Prepare USB device for MSC test # 1 Prepare USB device for MSC test
device.expect_exact('Press ENTER to see the list of tests.') device.expect_exact('Press ENTER to see the list of tests.')
device.write('[usb_msc_device]') device.write('[usb_msc_device]')
device.expect_exact('USB initialization DONE') device.expect_exact('USB initialization DONE')
# 2.2 Run MSC test # 2 Run MSC test
host.run_all_single_board_cases(group='usb_msc') host.run_all_single_board_cases(group='usb_msc')

View file

@ -5,8 +5,7 @@ CONFIG_TINYUSB_CDC_COUNT=0
CONFIG_TINYUSB_HID_COUNT=0 CONFIG_TINYUSB_HID_COUNT=0
# Disable watchdogs, they'd get triggered during unity interactive menu # Disable watchdogs, they'd get triggered during unity interactive menu
CONFIG_ESP_INT_WDT=n # CONFIG_ESP_TASK_WDT_INIT is not set
CONFIG_ESP_TASK_WDT=n
# Run-time checks of Heap and Stack # Run-time checks of Heap and Stack
CONFIG_HEAP_POISONING_COMPREHENSIVE=y CONFIG_HEAP_POISONING_COMPREHENSIVE=y

Binary file not shown.

BIN
spiffs_image/KTag_fixed.mp3 Normal file

Binary file not shown.