diff --git a/boards/arm/kinetis/freedom-k28f/Kconfig b/boards/arm/kinetis/freedom-k28f/Kconfig index 928307b697..8793605960 100644 --- a/boards/arm/kinetis/freedom-k28f/Kconfig +++ b/boards/arm/kinetis/freedom-k28f/Kconfig @@ -33,4 +33,34 @@ config FRDMK28F_SDHC_AUTOMOUNT_UDELAY default 2000 endif # FRDMK28F_SDHC_AUTOMOUNT + +config FRDMK28F_USB_AUTOMOUNT + bool "USB Mass Storage automounter" + default n + depends on USBHOST_MSC && USBHOST_MSC_NOTIFIER + +if FRDMK28F_USB_AUTOMOUNT + +config FRDMK28F_USB_AUTOMOUNT_FSTYPE + string "USB file system type" + default "vfat" + +config FRDMK28F_USB_AUTOMOUNT_BLKDEV + string "USB block device prefix" + default "/dev/sd" + +config FRDMK28F_USB_AUTOMOUNT_MOUNTPOINT + string "USB mount point prefix" + default "/mnt/usb" + +config FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV + int "Number of block devices to monitor." + range 1 26 + default 4 + +config FRDMK28F_USB_AUTOMOUNT_UDELAY + int "USB unmount retry delay (milliseconds)" + default 2000 + +endif # FRDMK28F_USB_AUTOMOUNT endif # ARCH_BOARD_FREEDOM_K28F diff --git a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h index 9116cfa522..0913999178 100644 --- a/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h +++ b/boards/arm/kinetis/freedom-k28f/src/freedom-k28f.h @@ -52,7 +52,13 @@ /* Assume we have everything */ #define HAVE_MMCSD 1 -#define HAVE_AUTOMOUNTER 1 +#define HAVE_USB_MSC 1 +#ifdef CONFIG_FRDMK28F_SDHC_AUTOMOUNT +# define HAVE_SDHC_AUTOMOUNTER 1 +#endif +#ifdef CONFIG_FRDMK28F_USB_AUTOMOUNT +# define HAVE_USB_AUTOMOUNTER 1 +#endif /* SD card support */ @@ -77,6 +83,12 @@ # define MMSCD_MINOR 0 # endif +/* Check for USB_HOST and USB_MSC */ +#if defined(CONFIG_DISABLE_MOUNTPOINT) || \ + !defined(CONFIG_KINETIS_USBHS) || !defined(CONFIG_USBHOST_MSC) +# undef HAVE_USB_MSC +#endif + /* We expect to receive GPIO interrupts for card insertion events */ # ifndef CONFIG_KINETIS_GPIOIRQ @@ -92,17 +104,16 @@ /* Automounter */ #if !defined(CONFIG_FS_AUTOMOUNTER) || !defined(HAVE_MMCSD) -# undef HAVE_AUTOMOUNTER -# undef CONFIG_FRDMK28F_SDHC_AUTOMOUNT +# undef HAVE_SDHC_AUTOMOUNTER #endif -#ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT -# undef HAVE_AUTOMOUNTER +#if !defined(HAVE_USB_MSC) || !defined(CONFIG_USBHOST_MSC_NOTIFIER) +# undef HAVE_USB_AUTOMOUNTER #endif /* Automounter defaults */ -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER # ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_FSTYPE # define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_FSTYPE "vfat" @@ -123,7 +134,30 @@ # ifndef CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY # define CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY 2000 # endif -#endif /* HAVE_AUTOMOUNTER */ +#endif /* HAVE_SDHC_AUTOMOUNTER */ + +#ifdef HAVE_USB_AUTOMOUNTER + +# ifndef CONFIG_FRDMK28F_USB_AUTOMOUNT_FSTYPE +# define CONFIG_FRDMK28F_USB_AUTOMOUNT_FSTYPE "vfat" +# endif + +# ifndef CONFIG_FRDMK28F_USB_AUTOMOUNT_BLKDEV +# define CONFIG_FRDMK28F_USB_AUTOMOUNT_BLKDEV "/dev/sd" +# endif + +# ifndef CONFIG_FRDMK28F_USB_AUTOMOUNT_MOUNTPOINT +# define CONFIG_FRDMK28F_USB_AUTOMOUNT_MOUNTPOINT "/mnt/usb" +# endif + +# ifndef CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV +# define CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV 4 +# endif + +# ifndef CONFIG_FRDMK28F_USB_AUTOMOUNT_UDELAY +# define CONFIG_FRDMK28F_USB_AUTOMOUNT_UDELAY 2000 +# endif +#endif /* HAVE_USB_AUTOMOUNTER */ /* Freedom-K28F GPIOs *******************************************************/ @@ -279,7 +313,7 @@ int k28_sdhc_initialize(void); * ****************************************************************************/ -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER bool k28_cardinserted(void); #else # define k28_cardinserted() (false) @@ -293,32 +327,14 @@ bool k28_cardinserted(void); * ****************************************************************************/ -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER bool k28_writeprotected(void); #else # define k28_writeprotected() (false) #endif /**************************************************************************** - * Name: k28_automount_initialize - * - * Description: - * Configure auto-mounter for the configured SDHC slot - * - * Input Parameters: - * None - * - * Returned Value: - * None - * - ****************************************************************************/ - -#ifdef HAVE_AUTOMOUNTER -void k28_automount_initialize(void); -#endif - -/**************************************************************************** - * Name: k28_automount_event + * Name: k28_sdhc_automount_event * * Description: * The SDHC card detection logic has detected an insertion or removal @@ -338,8 +354,26 @@ void k28_automount_initialize(void); * ****************************************************************************/ -#ifdef HAVE_AUTOMOUNTER -void k28_automount_event(bool inserted); +#ifdef HAVE_SDHC_AUTOMOUNTER +void k28_sdhc_automount_event(bool inserted); +#endif + +/**************************************************************************** + * Name: k28_automount_initialize + * + * Description: + * Configure auto-mounter for the configured SDHC slot + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if defined(HAVE_SDHC_AUTOMOUNTER) || defined(HAVE_USB_AUTOMOUNTER) +void k28_automount_initialize(void); #endif /**************************************************************************** diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_automount.c b/boards/arm/kinetis/freedom-k28f/src/k28_automount.c index cf996a516d..a5d2b48e99 100644 --- a/boards/arm/kinetis/freedom-k28f/src/k28_automount.c +++ b/boards/arm/kinetis/freedom-k28f/src/k28_automount.c @@ -36,7 +36,7 @@ #include "freedom-k28f.h" -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER /**************************************************************************** * Pre-processor Definitions @@ -80,11 +80,11 @@ struct k28_automount_config_s * Private Function Prototypes ****************************************************************************/ -static int k28_attach(FAR const struct automount_lower_s *lower, +static int k28_sdhc_attach(FAR const struct automount_lower_s *lower, automount_handler_t isr, FAR void *arg); -static void k28_enable(FAR const struct automount_lower_s *lower, +static void k28_sdhc_enable(FAR const struct automount_lower_s *lower, bool enable); -static bool k28_inserted(FAR const struct automount_lower_s *lower); +static bool k28_sdhc_inserted(FAR const struct automount_lower_s *lower); /**************************************************************************** * Private Data @@ -100,9 +100,9 @@ static const struct k28_automount_config_s g_sdhc_config = .mountpoint = CONFIG_FRDMK28F_SDHC_AUTOMOUNT_MOUNTPOINT, .ddelay = MSEC2TICK(CONFIG_FRDMK28F_SDHC_AUTOMOUNT_DDELAY), .udelay = MSEC2TICK(CONFIG_FRDMK28F_SDHC_AUTOMOUNT_UDELAY), - .attach = k28_attach, - .enable = k28_enable, - .inserted = k28_inserted + .attach = k28_sdhc_attach, + .enable = k28_sdhc_enable, + .inserted = k28_sdhc_inserted }, .state = &g_sdhc_state }; @@ -112,7 +112,7 @@ static const struct k28_automount_config_s g_sdhc_config = ****************************************************************************/ /**************************************************************************** - * Name: k28_attach + * Name: k28_sdhc_attach * * Description: * Attach a new SDHC event handler @@ -127,7 +127,7 @@ static const struct k28_automount_config_s g_sdhc_config = * ****************************************************************************/ -static int k28_attach(FAR const struct automount_lower_s *lower, +static int k28_sdhc_attach(FAR const struct automount_lower_s *lower, automount_handler_t isr, FAR void *arg) { FAR const struct k28_automount_config_s *config; @@ -152,7 +152,7 @@ static int k28_attach(FAR const struct automount_lower_s *lower, } /**************************************************************************** - * Name: k28_enable + * Name: k28_sdhc_enable * * Description: * Enable card insertion/removal event detection @@ -166,7 +166,7 @@ static int k28_attach(FAR const struct automount_lower_s *lower, * ****************************************************************************/ -static void k28_enable(FAR const struct automount_lower_s *lower, +static void k28_sdhc_enable(FAR const struct automount_lower_s *lower, bool enable) { FAR const struct k28_automount_config_s *config; @@ -204,7 +204,7 @@ static void k28_enable(FAR const struct automount_lower_s *lower, } /**************************************************************************** - * Name: k28_inserted + * Name: k28_sdhc_inserted * * Description: * Check if a card is inserted into the slot. @@ -217,7 +217,7 @@ static void k28_enable(FAR const struct automount_lower_s *lower, * ****************************************************************************/ -static bool k28_inserted(FAR const struct automount_lower_s *lower) +static bool k28_sdhc_inserted(FAR const struct automount_lower_s *lower) { return k28_cardinserted(); } @@ -226,6 +226,59 @@ static bool k28_inserted(FAR const struct automount_lower_s *lower) * Public Functions ****************************************************************************/ +/**************************************************************************** + * Name: k28_sdhc_automount_event + * + * Description: + * The SDHC card detection logic has detected an insertion or removal + * event. + * It has already scheduled the MMC/SD block driver operations. + * Now we need to schedule the auto-mount event which will occur with a + * substantial delay to make sure that everything has settle down. + * + * Input Parameters: + * slotno - Identifies the SDHC0 slot: SDHC0_SLOTNO or SDHC1_SLOTNO. + * There is a terminology problem here: Each SDHC supports two slots, + * slot A and slot B. Only slot A is used. + * So this is not a really a slot, but an HSCMI peripheral number. + * inserted - True if the card is inserted in the slot. False otherwise. + * + * Returned Value: + * None + * + * Assumptions: + * Interrupts are disabled. + * + ****************************************************************************/ + +void k28_sdhc_automount_event(bool inserted) +{ + FAR const struct k28_automount_config_s *config = &g_sdhc_config; + FAR struct k28_automount_state_s *state = &g_sdhc_state; + + /* Is the auto-mounter interrupt attached? */ + + if (state->handler) + { + /* Yes.. Have we been asked to hold off interrupts? */ + + if (!state->enable) + { + /* Yes.. just remember that there is a pending interrupt. We will + * deliver the interrupt when interrupts are "re-enabled." + */ + + state->pending = true; + } + else + { + /* No.. forward the event to the handler */ + + state->handler(&config->lower, state->arg, inserted); + } + } +} + /**************************************************************************** * Name: k28_automount_initialize * @@ -255,56 +308,4 @@ void k28_automount_initialize(void) } } -/**************************************************************************** - * Name: k28_automount_event - * - * Description: - * The SDHC card detection logic has detected an insertion or removal event. - * It has already scheduled the MMC/SD block driver operations. - * Now we need to schedule the auto-mount event which will occur with a - * substantial delay to make sure that everything has settle down. - * - * Input Parameters: - * slotno - Identifies the SDHC0 slot: SDHC0_SLOTNO or SDHC1_SLOTNO. - * There is a terminology problem here: Each SDHC supports two slots, - * slot A and slot B. Only slot A is used. - * So this is not a really a slot, but an HSCMI peripheral number. - * inserted - True if the card is inserted in the slot. False otherwise. - * - * Returned Value: - * None - * - * Assumptions: - * Interrupts are disabled. - * - ****************************************************************************/ - -void k28_automount_event(bool inserted) -{ - FAR const struct k28_automount_config_s *config = &g_sdhc_config; - FAR struct k28_automount_state_s *state = &g_sdhc_state; - - /* Is the auto-mounter interrupt attached? */ - - if (state->handler) - { - /* Yes.. Have we been asked to hold off interrupts? */ - - if (!state->enable) - { - /* Yes.. just remember that there is a pending interrupt. We will - * deliver the interrupt when interrupts are "re-enabled." - */ - - state->pending = true; - } - else - { - /* No.. forward the event to the handler */ - - state->handler(&config->lower, state->arg, inserted); - } - } -} - -#endif /* HAVE_AUTOMOUNTER */ +#endif /* HAVE_SDHC_AUTOMOUNTER */ diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c index 1170f2023d..f55d398ee7 100644 --- a/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c +++ b/boards/arm/kinetis/freedom-k28f/src/k28_bringup.c @@ -118,12 +118,6 @@ int k28_bringup(void) #endif /* CONFIG_FRDMK28F_SDHC_MOUNT */ #endif /* HAVE_MMCSD */ -#ifdef HAVE_AUTOMOUNTER - /* Initialize the auto-mounter */ - - k28_automount_initialize(); -#endif - #if defined(CONFIG_USBDEV) && defined(CONFIG_KINETIS_USBOTG) if (k28_usbdev_initialize) { @@ -154,9 +148,17 @@ int k28_bringup(void) #endif #if defined(CONFIG_USBHOST) && defined(CONFIG_KINETIS_USBHS) + /* Initialize the USB highspeed host */ + k28_usbhost_initialize(); #endif +#ifdef HAVE_SDHC_AUTOMOUNTER + /* Initialize the auto-mounter */ + + k28_automount_initialize(); +#endif + UNUSED(ret); return OK; } diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c b/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c index 356c91237e..86bb99e768 100644 --- a/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c +++ b/boards/arm/kinetis/freedom-k28f/src/k28_sdhc.c @@ -109,15 +109,17 @@ static void k28_mediachange(void) { mcinfo("Media change: %d->%d\n", g_sdhc.inserted, inserted); - /* Yes.. perform the appropriate action (this might need some debounce). */ + /* Yes.. perform the appropriate action + * (this might need some debounce). + */ g_sdhc.inserted = inserted; sdhc_mediachange(g_sdhc.sdhc, inserted); -#ifdef CONFIG_FRDMK28F_SDHC_AUTOMOUNT +#ifdef HAVE_SDHC_AUTOMOUNTER /* Let the automounter know about the insertion event */ - k28_automount_event(k28_cardinserted()); + k28_sdhc_automount_event(k28_cardinserted()); #endif } } @@ -180,7 +182,8 @@ int k28_sdhc_initialize(void) ret = mmcsd_slotinitialize(MMSCD_MINOR, g_sdhc.sdhc); if (ret != OK) { - syslog(LOG_ERR, "ERROR: Failed to bind SDHC to the MMC/SD driver: %d\n", + syslog(LOG_ERR, + "ERROR: Failed to bind SDHC to the MMC/SD driver: %d\n", ret); return ret; } @@ -205,7 +208,7 @@ int k28_sdhc_initialize(void) * ****************************************************************************/ -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER bool k28_cardinserted(void) { bool inserted; @@ -228,7 +231,7 @@ bool k28_cardinserted(void) * ****************************************************************************/ -#ifdef HAVE_AUTOMOUNTER +#ifdef HAVE_SDHC_AUTOMOUNTER bool k28_writeprotected(void) { /* There are no write protect pins */ diff --git a/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c b/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c index 51145d1eea..f6ee46e221 100644 --- a/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c +++ b/boards/arm/kinetis/freedom-k28f/src/k28_usbhshost.c @@ -42,6 +42,7 @@ #include #include #include +#include #include #include #include @@ -54,6 +55,8 @@ #include #include +#include + #include #include "arm_arch.h" @@ -88,6 +91,19 @@ # endif #endif +/***************************************************************************** + * Private Function Prototypes + *****************************************************************************/ + +static int ehci_waiter(int argc, char *argv[]); +static void ehci_hwinit(void); + +# ifdef HAVE_USB_AUTOMOUNTER +static void usb_msc_connect(FAR void *arg); +static void unmount_retry_timeout(int argc, uint32_t arg1, ...); +static void usb_msc_disconnect(FAR void *arg); +# endif + /***************************************************************************** * Private Data *****************************************************************************/ @@ -96,6 +112,12 @@ static struct usbhost_connection_s *g_ehciconn; +# ifdef HAVE_USB_AUTOMOUNTER +/* Unmount retry timer */ + +static WDOG_ID g_umount_tmr[CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV]; +# endif + /***************************************************************************** * Private Functions *****************************************************************************/ @@ -232,6 +254,152 @@ static void ehci_hwinit(void) putreg32(regval, KINETIS_USBHSPHY_CTRL); } +# ifdef HAVE_USB_AUTOMOUNTER +/***************************************************************************** + * Name: usb_msc_connect + * + * Description: + * Mount the USB mass storage device + * + *****************************************************************************/ + +static void usb_msc_connect(FAR void *arg) +{ + int index = (int)arg; + char sdchar = 'a' + index; + int ret; + + char blkdev[32]; + char mntpnt[32]; + + DEBUGASSERT(index >= 0 && index < CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV); + + wd_cancel(g_umount_tmr[index]); + + /* Resetup the event. */ + + usbhost_msc_notifier_setup(usb_msc_connect, WORK_USB_MSC_CONNECT, + sdchar, arg); + + snprintf(blkdev, sizeof(blkdev), "%s%c", + CONFIG_FRDMK28F_USB_AUTOMOUNT_BLKDEV, sdchar); + snprintf(mntpnt, sizeof(mntpnt), "%s%c", + CONFIG_FRDMK28F_USB_AUTOMOUNT_MOUNTPOINT, sdchar); + + /* Mount */ + + ret = mount((FAR const char *)blkdev, (FAR const char *)mntpnt, + CONFIG_FRDMK28F_USB_AUTOMOUNT_FSTYPE, 0, NULL); + if (ret < 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + ferr("ERROR: Mount failed: %d\n", errcode); + UNUSED(errcode); + } +} + +/***************************************************************************** + * Name: unmount_retry_timeout + * + * Description: + * A previous unmount failed because the volume was busy... busy meaning + * the volume could not be unmounted because there are open references + * the files or directories in the volume. When this failure occurred, + * the unmount logic setup a delay and this function is called as a result + * of that delay timeout. + * + * This function will attempt the unmount again. + * + * Input Parameters: + * Standard wdog timeout parameters + * + * Returned Value: + * None + * + *****************************************************************************/ + +static void unmount_retry_timeout(int argc, uint32_t arg1, ...) +{ + int index = (int)arg1; + char sdchar = 'a' + index; + + finfo("Timeout!\n"); + DEBUGASSERT(argc == 1 && \ + index >= 0 && index < CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV); + + /* Resend the notification. */ + + usbhost_msc_notifier_signal(WORK_USB_MSC_DISCONNECT, sdchar); +} + +/***************************************************************************** + * Name: usb_msc_disconnect + * + * Description: + * Unmount the USB mass storage device + * + *****************************************************************************/ + +static void usb_msc_disconnect(FAR void *arg) +{ + int index = (int)arg; + char sdchar = 'a' + index; + int ret; + + char mntpnt[32]; + + DEBUGASSERT(index >= 0 && index < CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV); + + wd_cancel(g_umount_tmr[index]); + + /* Resetup the event. */ + + usbhost_msc_notifier_setup(usb_msc_disconnect, WORK_USB_MSC_DISCONNECT, + sdchar, arg); + + snprintf(mntpnt, sizeof(mntpnt), "%s%c", + CONFIG_FRDMK28F_USB_AUTOMOUNT_MOUNTPOINT, sdchar); + + /* Unmount */ + + ret = umount2((FAR const char *)mntpnt, MNT_FORCE); + if (ret < 0) + { + int errcode = get_errno(); + DEBUGASSERT(errcode > 0); + + /* We expect the error to be EBUSY meaning that the volume could + * not be unmounted because there are currently reference via open + * files or directories. + */ + + if (errcode == EBUSY) + { + finfo("WARNING: Volume is busy, try again later\n"); + + /* Start a timer to retry the umount2 after a delay */ + + ret = wd_start(g_umount_tmr[index], + MSEC2TICK(CONFIG_FRDMK28F_USB_AUTOMOUNT_UDELAY), + unmount_retry_timeout, 1, (uint32_t)index); + if (ret < 0) + { + ferr("ERROR: wd_start failed: %d\n", ret); + } + } + + /* Other errors are fatal */ + + else + { + ferr("ERROR: Unmount failed: %d\n", errcode); + } + } +} +# endif /* HAVE_USB_AUTOMOUNTER */ + /***************************************************************************** * Public Functions *****************************************************************************/ @@ -251,6 +419,9 @@ int k28_usbhost_initialize(void) { pid_t pid; int ret; +# ifdef HAVE_USB_AUTOMOUNTER + int index; +# endif /* First, register all of the class drivers needed to support the drivers * that we care about @@ -269,6 +440,22 @@ int k28_usbhost_initialize(void) #ifdef CONFIG_USBHOST_MSC /* Register the USB host Mass Storage Class */ +# ifdef HAVE_USB_AUTOMOUNTER + /* Initialize the notifier listener for automount */ + + for (index = 0; index < CONFIG_FRDMK28F_USB_AUTOMOUNT_NUM_BLKDEV; index++) + { + char sdchar = 'a' + index; + + g_umount_tmr[index] = wd_create(); + + usbhost_msc_notifier_setup(usb_msc_connect, + WORK_USB_MSC_CONNECT, sdchar, (FAR void *)(intptr_t)index); + usbhost_msc_notifier_setup(usb_msc_disconnect, + WORK_USB_MSC_DISCONNECT, sdchar, (FAR void *)(intptr_t)index); + } +# endif + ret = usbhost_msc_initialize(); if (ret != OK) { diff --git a/drivers/usbhost/Kconfig b/drivers/usbhost/Kconfig index f57904bf4b..08f4dc8972 100644 --- a/drivers/usbhost/Kconfig +++ b/drivers/usbhost/Kconfig @@ -108,7 +108,17 @@ config USBHOST_MSC ---help--- Enable support for the mass storage class driver. This also depends on NFILE_DESCRIPTORS > 0 && SCHED_WORKQUEUE=y - + +config USBHOST_MSC_NOTIFIER + bool "Support USB Mass Storage notifications" + default n + depends on USBHOST_MSC + select WQUEUE_NOTIFIER + ---help--- + Enable building of USB MSC notifier logic that will execute a worker + function on the low priority work queue when a mass storage device is + connected or disconnected. + config USBHOST_CDCACM bool "CDC/ACM support" default n diff --git a/drivers/usbhost/usbhost_storage.c b/drivers/usbhost/usbhost_storage.c index 1d0308d3f4..fb39886373 100644 --- a/drivers/usbhost/usbhost_storage.c +++ b/drivers/usbhost/usbhost_storage.c @@ -1400,6 +1400,14 @@ static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv) ret = -ENODEV; } +# ifdef CONFIG_USBHOST_MSC_NOTIFIER + else + { + /* Signal the connect */ + + usbhost_msc_notifier_signal(WORK_USB_MSC_CONNECT, priv->sdchar); + } +# endif /* Release the semaphore... there is a race condition here. * Decrementing the reference count and releasing the semaphore @@ -1409,6 +1417,7 @@ static inline int usbhost_initvolume(FAR struct usbhost_state_s *priv) */ usbhost_givesem(&priv->exclsem); + return ret; } @@ -1848,6 +1857,12 @@ static int usbhost_disconnected(struct usbhost_class_s *usbclass) DEBUGASSERT(priv != NULL); +# ifdef CONFIG_USBHOST_MSC_NOTIFIER + /* Signal the disconnect */ + + usbhost_msc_notifier_signal(WORK_USB_MSC_DISCONNECT, priv->sdchar); +# endif + /* Set an indication to any users of the mass storage device that the * device is no longer available. */ @@ -2360,4 +2375,98 @@ int usbhost_msc_initialize(void) return usbhost_registerclass(&g_storage); } +# ifdef CONFIG_USBHOST_MSC_NOTIFIER + +/**************************************************************************** + * Name: usbhost_msc_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when a mass storage + * device is attached. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work queue + * when the event occurs. + * event - Currently only USBHOST_MSC_DISCONNECT and USBHOST_MSC_CONNECT + * sdchar - sdchar of the connected or disconnected block device + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The notification is in place. The returned value is a key that + * may be used later in a call to + * usbmsc_attach_notifier_teardown(). + * == 0 - Not used. + * < 0 - An unexpected error occurred and no notification will occur. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ****************************************************************************/ + +int usbhost_msc_notifier_setup(worker_t worker, uint8_t event, char sdchar, + FAR void *arg) +{ + struct work_notifier_s info; + + DEBUGASSERT(worker != NULL); + + info.evtype = event; + info.qid = LPWORK; + info.qualifier = (FAR void *)(uintptr_t)sdchar; + info.arg = arg; + info.worker = worker; + + return work_notifier_setup(&info); +} + +/**************************************************************************** + * Name: usbhost_msc_notifier_teardown + * + * Description: + * Eliminate an USB MSC notification previously setup by + * usbhost_msc_notifier_setup(). + * This function should only be called if the notification should be + * aborted prior to the notification. The notification will automatically + * be torn down after the notification. + * + * Input Parameters: + * key - The key value returned from a previous call to + * usbhost_msc_notifier_setup(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ****************************************************************************/ + +int usbhost_msc_notifier_teardown(int key) +{ + /* This is just a simple wrapper around work_notifier_teardown(). */ + + return work_notifier_teardown(key); +} + +/**************************************************************************** + * Name: usbhost_msc_notifier_signal + * + * Description: + * An USB mass storage device has been connected or disconnected. + * Signal all threads. + * + * Input Parameters: + * event - Currently only USBHOST_MSC_DISCONNECT and USBHOST_MSC_CONNECT + * sdchar - sdchar of the connected or disconnected block device + * + * Returned Value: + * None. + * + ****************************************************************************/ + +void usbhost_msc_notifier_signal(uint8_t event, char sdchar) +{ + work_notifier_signal(event, (FAR void *)(uintptr_t)sdchar); +} + +# endif /* CONFIG_USBHOST_MSC_NOTIFIER */ + #endif /* CONFIG_USBHOST && !CONFIG_USBHOST_BULK_DISABLE && !CONFIG_DISABLE_MOUNTPOINT */ diff --git a/include/nuttx/usb/usbhost.h b/include/nuttx/usb/usbhost.h index f217b5917b..4a4e060f40 100644 --- a/include/nuttx/usb/usbhost.h +++ b/include/nuttx/usb/usbhost.h @@ -39,6 +39,10 @@ #include #include +#ifdef CONFIG_USBHOST_MSC_NOTIFIER +# include +#endif + #include /************************************************************************************ @@ -1014,6 +1018,78 @@ int usbhost_hub_initialize(void); ************************************************************************************/ int usbhost_msc_initialize(void); + +# ifdef CONFIG_USBHOST_MSC_NOTIFIER +/************************************************************************************ + * Name: usbhost_msc_notifier_setup + * + * Description: + * Set up to perform a callback to the worker function when a mass storage + * device is attached. + * + * Input Parameters: + * worker - The worker function to execute on the low priority work queue + * when the event occurs. + * event - Only WORK_USB_MSC_CONNECT and WORK_USB_MSC_DISCONNECT + * sdchar - sdchar of the connected or disconnected block device + * arg - A user-defined argument that will be available to the worker + * function when it runs. + * + * Returned Value: + * > 0 - The notification is in place. The returned value is a key that + * may be used later in a call to + * usbmsc_attach_notifier_teardown(). + * == 0 - Not used. + * < 0 - An unexpected error occurred and no notification will occur. The + * returned value is a negated errno value that indicates the + * nature of the failure. + * + ************************************************************************************/ + +int usbhost_msc_notifier_setup(worker_t worker, uint8_t event, char sdchar, + FAR void *arg); + +/************************************************************************************ + * Name: usbhost_msc_notifier_teardown + * + * Description: + * Eliminate an USB MSC notification previously setup by + * usbhost_msc_notifier_setup(). + * This function should only be called if the notification should be + * aborted prior to the notification. The notification will automatically + * be torn down after the notification. + * + * Input Parameters: + * key - The key value returned from a previous call to + * usbhost_msc_notifier_setup(). + * + * Returned Value: + * Zero (OK) is returned on success; a negated errno value is returned on + * any failure. + * + ************************************************************************************/ + +int usbhost_msc_notifier_teardown(int key); + +/************************************************************************************ + * Name: usbhost_msc_notifier_signal + * + * Description: + * An USB mass storage device has been connected or disconnected. + * Signal all threads. + * + * Input Parameters: + * event - Currently only USBHOST_MSC_DISCONNECT and USBHOST_MSC_CONNECT + * sdchar - sdchar of the connected or disconnected block device + * + * Returned Value: + * None. + * + ************************************************************************************/ + +void usbhost_msc_notifier_signal(uint8_t event, char sdchar); + +# endif #endif #ifdef CONFIG_USBHOST_CDCACM diff --git a/include/nuttx/wqueue.h b/include/nuttx/wqueue.h index ea7563cf7c..ceda164307 100644 --- a/include/nuttx/wqueue.h +++ b/include/nuttx/wqueue.h @@ -273,15 +273,17 @@ struct work_s enum work_evtype_e { - WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */ - WORK_NET_DOWN, /* Notify that the network is down */ - WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */ - WORK_TCP_WRITEBUFFER, /* Notify that TCP write buffer is empty */ - WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ - WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */ - WORK_UDP_WRITEBUFFER, /* Notify that UDP write buffer is empty */ - WORK_NETLINK_RESPONSE, /* Notify that Netlink response is available */ - WORK_CAN_READAHEAD /* Notify that CAN read-ahead data is available */ + WORK_IOB_AVAIL = 1, /* Notify availability of an IOB */ + WORK_NET_DOWN, /* Notify that the network is down */ + WORK_TCP_READAHEAD, /* Notify that TCP read-ahead data is available */ + WORK_TCP_WRITEBUFFER, /* Notify that TCP write buffer is empty */ + WORK_TCP_DISCONNECT, /* Notify loss of TCP connection */ + WORK_UDP_READAHEAD, /* Notify that UDP read-ahead data is available */ + WORK_UDP_WRITEBUFFER, /* Notify that UDP write buffer is empty */ + WORK_NETLINK_RESPONSE, /* Notify that Netlink response is available */ + WORK_CAN_READAHEAD, /* Notify that CAN read-ahead data is available */ + WORK_USB_MSC_CONNECT, /* Notify that an USB MSC connect occured */ + WORK_USB_MSC_DISCONNECT /* Notify that an USB MSC connect occured */ }; /* This structure describes one notification and is provided as input to