How to stop triggering USB Autoplay when Nordic USB Mass Storage Plugged in, but no SD Card

Problem:

I am working on USB, CDC ACM, USB Mass Storage (using SD Card). I currently want to be able to still use the USB stack functions with only my USB Mass Storage being deactivated. The caveat being, the deactivation happens during runtime (not compile time, hence cannot solve this by using KConfig). I need it during runtime because I want to do a check if whether or not my SD Card is plugged in or not. The current problem is in which the automatic USB storage pop up will show in windows. I need to stop that pop up from showing through modifying the device kernel logic on.

Here is my current `prj.conf`

# Enable USB subsystem
CONFIG_USB_COMPOSITE_DEVICE=y
CONFIG_UART_ASYNC_API=y
CONFIG_SERIAL=y
CONFIG_USB_DEVICE_PRODUCT="My Nordic USB Device"
CONFIG_USB_DEVICE_PID=0x530E
# --- Disable USB logging/console output ---
CONFIG_UART_CONSOLE=n      
CONFIG_LOG_BACKEND_UART=n       
CONFIG_USB_DRIVER_LOG_LEVEL_OFF=y
CONFIG_USB_DEVICE_LOG_LEVEL_OFF=y

# Enable the UART driver
CONFIG_UART_LINE_CTRL=y
CONFIG_UART_INTERRUPT_DRIVEN=y
CONFIG_UART_ASYNC_ADAPTER=y
CONFIG_USB_DEVICE_STACK=y
CONFIG_USB_DEVICE_REMOTE_WAKEUP=n
CONFIG_USB_CDC_ACM=y
CONFIG_USB_CDC_ACM_LOG_LEVEL_OFF=y
# Enable Sdcard
CONFIG_DISK_DRIVER_SDMMC=y
CONFIG_FILE_SYSTEM=y
CONFIG_FAT_FILESYSTEM_ELM=y
CONFIG_FS_FATFS_LFN=y
CONFIG_FS_FATFS_MAX_LFN=255
# Enable Storae-Mass
CONFIG_USB_MASS_STORAGE=y
CONFIG_USB_DEVICE_LOG_LEVEL_ERR=y
CONFIG_USB_MASS_STORAGE_LOG_LEVEL_ERR=y
CONFIG_USB_DEVICE_INITIALIZE_AT_BOOT=n
CONFIG_MASS_STORAGE_DISK_NAME="SD"

This image shows the popup everytime I plugin or reset my device, I need this popup to stop showing, even when my SD Card is not plugged in.

I've tried modifying the `msc.c` to do a check. But adding this line comprimises the USB stack, it causes the `usb_dc_status_code == USB_DC_SUSPEND`

static void mass_interface_config(struct usb_desc_header *head,
				  uint8_t bInterfaceNumber)
{
	ARG_UNUSED(head);

    // ADDED THIS_START
	if (disk_access_init(disk_pdrv) != 0) {
		LOG_ERR("Storage init ERROR !!!! - Aborting USB Mass Storage Interface Config");
		return -ENODEV;
	}
	// ADDED THIS_END

	mass_cfg.if0.bInterfaceNumber = bInterfaceNumber;
}

/* Configuration of the Mass Storage Device send to the USB Driver */
USBD_DEFINE_CFG_DATA(mass_storage_config) = {
	.usb_device_description = NULL,
	.interface_config = mass_interface_config,
	.interface_descriptor = &mass_cfg.if0,
	.cb_usb_status = mass_storage_status_cb,
	.interface = {
		.class_handler = mass_storage_class_handle_req,
		.custom_handler = NULL,
	},
	.num_endpoints = ARRAY_SIZE(mass_ep_data),
	.endpoint = mass_ep_data
};

I've also tried modifying the `mass_storage_init()` like such. But the popup still shows. The macro is just for simple example. The real implementation would be using a zbus or something to suspend and then notify this very function after the sdcard status is confirmed (whether is inserted or not).

#define TEST_STOP_MSC true
static int mass_storage_init(void)
{
	uint32_t block_size = 0U;

	if (TEST_STOP_MSC) {
		return 0;
	}

	if (disk_access_init(disk_pdrv) != 0) {
		LOG_ERR("Storage init ERROR !!!! - Aborting USB init");
		return 0;
	}

Parents
  • Won't work - windows for example does not support changing USB descriptors on a device, these are cached in registry.

    What you can do is to try to implement a removable mass storage device, i.e. a device with removable media. This feature could be implemented in the SCSI layer - but I believe there is no support in zephyr mass storage yet.

    Can be hacked in though - its a magic bit in the SCSI inquiry and some command/error handling in "TEST_UNIT_READY" SCSI command. The existing msc.c could be a good starting point, but it needs some modifications for your own implementation.

  • So essentially my approach is to modify the USB Mass Storage component from zephyr `zephyr/subsys/usb/device/class/msc.c`

    Currently my workaround is adding conditionals to different function handlers inside the `CBWDecode`

    static void CBWDecode(uint8_t *buf, uint16_t size)
    {
    ...
    }
    For example using `disk_access_init(disk_pdrv) != 0` to check if SD Card is inserted or not
    static bool storage_media_check_failed = false;
    static bool is_storage_media_available(void)
    {
     	if (disk_access_init(disk_pdrv) != 0) {
    		LOG_WRN("No Media Detected !!!! - Aborting USB Mass Storage init");
    		return false;
    	}
    	return true;
    }
    
    static void testUnitReady(void)
    {
    	if (!is_storage_media_available() && !storage_media_check_failed) {
    		LOG_DBG("Test Unit fail check");
            // No media - return error
            csw.Status = CSW_FAILED;
            sendCSW();
    		storage_media_check_failed = true;
            return;
        }
    
    	if (cbw.DataLength != 0U) {
    		if ((cbw.Flags & 0x80) != 0U) {
    			LOG_WRN("Stall IN endpoint");
    			usb_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
    		} else {
    			LOG_WRN("Stall OUT endpoint");
    			usb_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
    		}
    	}
    
    	csw.Status = CSW_PASSED;
    	sendCSW();
    }
    Also adding the removable bit inside the inquiry response
    static const struct dabc_inquiry_data inq_rsp = {
    	.head = {0x00, 0x80, 0x80, 0x01, 36 - 4, 0x80, 0x00, 0x00}, // changing from {0x00, 0x80, 0x00, 0x01, 36 - 4, 0x80, 0x00, 0x00}
    	.t10_vid = CONFIG_MASS_STORAGE_INQ_VENDOR_ID,
    	.product_id = CONFIG_MASS_STORAGE_INQ_PRODUCT_ID,
    	.product_rev = CONFIG_MASS_STORAGE_INQ_REVISION,
    };
    And also changing the Request Sense, but am not entirely sure what the default implementation implies, and if this changed version is accurate already. Don't really know how to verify if it from the PC if I miss anything
    uint8_t request_sense[] = {
    		0x70,
    		0x00,
    		is_storage_media_available() ? 0x00 : 0x02, // 0x00 NO SENSE/Media Present; 0x02 NOT READY/Media NOT Present
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    		0x0A,
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    		is_storage_media_available() ? 0x00 : 0x3A, // MEDIA PRESENT/MEDIA NOT PRESENT
    		is_storage_media_available() ? 0x00 : 0x00, // MEDIA PRESENT/MEDIA NOT PRESENT
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    	};
    If I get any part wrong, don't hesitate to correct me on this   
Reply
  • So essentially my approach is to modify the USB Mass Storage component from zephyr `zephyr/subsys/usb/device/class/msc.c`

    Currently my workaround is adding conditionals to different function handlers inside the `CBWDecode`

    static void CBWDecode(uint8_t *buf, uint16_t size)
    {
    ...
    }
    For example using `disk_access_init(disk_pdrv) != 0` to check if SD Card is inserted or not
    static bool storage_media_check_failed = false;
    static bool is_storage_media_available(void)
    {
     	if (disk_access_init(disk_pdrv) != 0) {
    		LOG_WRN("No Media Detected !!!! - Aborting USB Mass Storage init");
    		return false;
    	}
    	return true;
    }
    
    static void testUnitReady(void)
    {
    	if (!is_storage_media_available() && !storage_media_check_failed) {
    		LOG_DBG("Test Unit fail check");
            // No media - return error
            csw.Status = CSW_FAILED;
            sendCSW();
    		storage_media_check_failed = true;
            return;
        }
    
    	if (cbw.DataLength != 0U) {
    		if ((cbw.Flags & 0x80) != 0U) {
    			LOG_WRN("Stall IN endpoint");
    			usb_ep_set_stall(mass_ep_data[MSD_IN_EP_IDX].ep_addr);
    		} else {
    			LOG_WRN("Stall OUT endpoint");
    			usb_ep_set_stall(mass_ep_data[MSD_OUT_EP_IDX].ep_addr);
    		}
    	}
    
    	csw.Status = CSW_PASSED;
    	sendCSW();
    }
    Also adding the removable bit inside the inquiry response
    static const struct dabc_inquiry_data inq_rsp = {
    	.head = {0x00, 0x80, 0x80, 0x01, 36 - 4, 0x80, 0x00, 0x00}, // changing from {0x00, 0x80, 0x00, 0x01, 36 - 4, 0x80, 0x00, 0x00}
    	.t10_vid = CONFIG_MASS_STORAGE_INQ_VENDOR_ID,
    	.product_id = CONFIG_MASS_STORAGE_INQ_PRODUCT_ID,
    	.product_rev = CONFIG_MASS_STORAGE_INQ_REVISION,
    };
    And also changing the Request Sense, but am not entirely sure what the default implementation implies, and if this changed version is accurate already. Don't really know how to verify if it from the PC if I miss anything
    uint8_t request_sense[] = {
    		0x70,
    		0x00,
    		is_storage_media_available() ? 0x00 : 0x02, // 0x00 NO SENSE/Media Present; 0x02 NOT READY/Media NOT Present
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    		0x0A,
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    		is_storage_media_available() ? 0x00 : 0x3A, // MEDIA PRESENT/MEDIA NOT PRESENT
    		is_storage_media_available() ? 0x00 : 0x00, // MEDIA PRESENT/MEDIA NOT PRESENT
    		0x00,
    		0x00,
    		0x00,
    		0x00,
    	};
    If I get any part wrong, don't hesitate to correct me on this   
Children
Related